2011-07-26 15 views
12

Próbuję wygenerować selektory CSS dla losowych elementów na stronie za pomocą C#. Niektóre tła:HtmlElement.Parent zwraca nieprawidłowy rodzic

Używam formularza z formantem WebBrowser. Podczas nawigacji można poprosić o selektor CSS elementu pod kursorem. Pierwsze HTML-element jest trywialne, oczywiście, za pomocą:

WebBrowser.Document.GetElementFromPoint(<Point>); 

Dąży się do tworzenia selektora 'surowe' css prowadzący do elementu pod kursora, a la:

html > body > span:eq(2) > li:eq(5) > div > div:eq(3) > span > a 

Ten selektor oparty jest na: operatorach eq, ponieważ jest przeznaczony do obsługi przez jQuery i/lub SizzleJS (te dwie funkcje obsługują: eq - oryginalne selektory CSS tego nie robią, Thumbs up @BoltClock za pomoc w wyjaśnieniu tego). Więc dostajesz obraz. Aby osiągnąć ten cel, możemy dostarczyć pobrane HTMLElement do poniższej metody i rozpocząć wynurzanie się drzewa DOM prosząc o dominującej każdego elementu natkniemy:

private static List<String> GetStrictCssForHtmlElement(HtmlElement element) 
    { 
     List<String> familyTree; 
     for (familyTree = new List<String>(); element != null; element = element.Parent) 
     { 
      string ordinalString = CalculateOrdinalPositionAmongSameTagSimblings(element); 
      if (ordinalString == null) return null; 

      familyTree.Add(element.TagName.ToLower() + ordinalString); 
     } 
     familyTree.Reverse(); 

     return familyTree; 
    } 

    private static string CalculateOrdinalPositionAmongSameTagSimblings(HtmlElement element, bool simplifyEq0 = true) 
    { 
     int count = 0; 
     int positionAmongSameTagSimblings = -1; 
     if (element.Parent != null) 
     { 
      foreach (HtmlElement child in element.Parent.Children) 
      { 
       if (element.TagName.ToLower() == child.TagName.ToLower()) 
       { 
        count++; 
        if (element == child) 
        { 
         positionAmongSameTagSimblings = count - 1; 
        } 
       } 
      } 

      if (positionAmongSameTagSimblings == -1) return null; // Couldn't find child in parent's offsprings!? 
     } 

     return ((count > 1) ? (":eq(" + positionAmongSameTagSimblings + ")") : ((simplifyEq0) ? ("") : (":eq(0)"))); 
    } 

Metoda ta pracuje niezawodnie do różnych stron. Jednak jest jedna konkretna strona, która sprawia, że ​​głowę w:

http://www.delicious.com/recent

Próbując odzyskać selektora CSS jakiegokolwiek elementu na liście (na środku strony) nie powiedzie się z jednego bardzo prostego powodu:

Po przebiciu uderza pierwszy element SPAN w górę (można go zobaczyć, sprawdzając stronę za pomocą narzędzi internetowych IE9 do weryfikacji) próbuje ją przetworzyć, obliczając jej pozycję porządkową wśród tego samego rodzeństwa tagów. Aby to zrobić, musimy poprosić go o węzeł rodzica dla rodzeństwa. To jest, gdzie sprawy stają się dziwne. Element SPAN zgłasza, że ​​Parent to element DIV o id = "recent-index". Jednak jest to obiekt nadrzędny SPAN (bezpośrednim rodzicem jest LI class = "wrap isAdv"). Powoduje to niepowodzenie metody, ponieważ - nie dziwi - nie wykrywa ona SPAN wśród dzieci.

Ale robi się jeszcze dziwniej. Pobrałem i wyizolowałem HtmlElement samego SPAN. Potem dostałem to nadrzędna i użył go, aby ponownie zejść z powrotem w dół do elementu SPAN używając:

HtmlElement regetSpanElement = spanElement.Parent.Children[0].Children[1].Children[1].Children[0].Children[2].Children[0]; 

to doprowadzić nas z powrotem do węzła SPAN zaczęliśmy ... z jednym skręcie Jednakże:

regetSpanElement.Parent.TagName; 

To teraz informuje LI jako rodzica XX. Jak to może być? Jakiś wgląd?

Jeszcze raz dziękuję.

Uwagi:

  1. Uratowałem kod HTML (jak to przedstawiono wewnątrz WebBrowser.Document.Html) i kontrolowane mu się być w 100% pewien, że nic śmiesznego odbywa się (aka inny kod służył Kontrola WebBrowser niż ta, którą widzę w IE9 - ale tak się nie dzieje, struktura pasuje do 100% dla danej ścieżki).

  2. Używam formantu WebBrowser w IE9 trybie wykonując instrukcje opisane tutaj:

    http://www.west-wind.com/weblog/posts/2011/May/21/Web-Browser-Control-Specifying-the-IE-Version

    Starając się uzyskać kontrolę WebBrowser i IE9 do uruchomienia jako podobnie jak to możliwe.

  3. Podejrzewam, że zaobserwowane efekty mogą być spowodowane działaniem skryptu za moimi plecami. Jednak moja wiedza nie jest tak daleko idąca pod względem programowania internetowego, aby go przypiąć.

EDIT: Literówki

+0

': eq()' nie jest prawidłowym selektorem CSS. Przypuszczam, że chodziło o 'html> body> span: nth-child (3)> li: nth-child (6)> div> div: nth-child (4)> span> a'? – BoltClock

+0

Dzięki za umożliwienie mi wyjaśnienia - chciałem powiedzieć, że selektory css mają być przekazywane do jQuery i/lub SizzleJS. Zaktualizuję treść w oryginalnym poście. Aby to odzwierciedlić. Jeszcze raz dziękuję;) – xDisruptor

+0

Witam, nie mam jeszcze odpowiedzi, ale chciałem powiedzieć kilka rzeczy; Po pierwsze, dziękuję za szczegóły w twoim pytaniu i dzięki uprzejmości członkom tej strony, +1 za to! Po drugie; Fascynuje mnie kontekst samego pytania; Rozumiem, co chcesz zrobić, czy możesz pomóc nam w wypełnieniu części Dlaczego? budujesz hierarchię drzew lub jakiś ślad szlaku? –

Odpowiedz

2

polegające na: eq() jest trudne! Trudno jest wiarygodnie ponownie wybrać DOM, który jest dynamiczny. Pewnie, że może działać na bardzo statycznych stronach, ale rzeczy stają się coraz bardziej dynamiczne każdego dnia. Możesz rozważyć zmianę strategii. Spróbuj użyć bardziej inteligentnego selektora. Być może pojawi się jakiś javascript tak:

predictCss = function(s, noid, noclass, noarrow) { 
    var path, node = s; 
    var psep = noarrow ? ' ' : ' > '; 
    if (s.length != 1) return path; //throw 'Requires one element.'; 
    while (node.length) { 
     var realNode = node[0]; 
     var name = (realNode.localName || realNode.tagName || realNode.nodeName); 
     if (!name || name == '#document') break; 
     name = name.toLowerCase(); 
     if(node.parent().children(name).length > 1){ 
      if (realNode.id && !noid) { 
       try { 
        var idtest = $(name + '#' + realNode.id); 
        if (idtest.length == 1) return name + '#' + realNode.id + (path ? '>' + path : ''); 
       } catch (ex) {} // just ignore the exception, it was a bad ID 
      } else if (realNode.className && !noclass) { 
       name += '.' + realNode.className.split(/\s+/).join('.'); 
      } 
     } 
     var parent = node.parent(); 
     if (name[name.length - 1] == '.') { 
      name = name.substring(0, name.length - 1); 
     } 
     siblings = parent.children(name); 
     //// If you really want to use eq: 
     //if (siblings.length > 1) name += ':eq(' + siblings.index(node) + ')'; 
     path = name + (path ? psep + path : ''); 
     node = parent; 
    } 
    return path 
} 

i używać go do generowania różnych selektorów:

var elem = $('#someelement'); 
var epath = self.model.util.predictCss(elem, true, true, false); 
var epathclass = self.model.util.predictCss(elem, true, false, false); 
var epathclassid = self.model.util.predictCss(elem, false, false, false); 

następnie wykorzystać każda:

var relem= $(epathclassid); 
if(relem.length === 0){ 
    relem = $(epathclass); 
    if(relem.length === 0){ 
     relem = $(epath); 
    } 
} 

A jeśli twój najlepszy selektor nadal przychodzi z więcej niż jednym elementem, będziesz musiał się wykazać kreatywnością w dopasowywaniu elementu dom - być może levenshtein lub może jest jakiś konkretny tekst, lub możesz zastąpić eq. Mam nadzieję, że pomaga!

Btw, założyłem, że masz jQuery - ze względu na skwierczenie odniesienia. Możesz wstrzyknąć powyższe w anonimowo wykonaną funkcję w tagu skryptu dołączonym na przykład do ostatniego dziecka ciała.

Powiązane problemy