2011-07-26 14 views
12

Estoy intentando generar selectores de CSS para elementos aleatorios en una página web mediante C#. Algunos antecedentes:HtmlElement.Parent devuelve el elemento primario incorrecto

Utilizo un formulario con un control WebBrowser. Mientras navega, puede solicitar el selector de CSS del elemento debajo del cursor. Conseguir el html-elemento es trivial, por supuesto, a través de:

WebBrowser.Document.GetElementFromPoint(<Point>); 

La ambición es crear un selector 'estricta' css que conduce hasta el elemento bajo el cursor, a-la:

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

Este selector se basa en: operadores eq, ya que está destinado a ser manejado por jQuery y/o SizzleJS (estos dos soportan: eq - los selectores de CSS originales no. Pulgares arriba @BoltClock para ayudarme a aclarar esto). Así se obtiene la imagen. Con el fin de lograr este objetivo, suministramos el HTMLElement recuperado en el método de abajo y empezar a ascender por el árbol DOM preguntando por los padres de cada elemento que nos encontramos:

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)"))); 
    } 

Este método ha funcionado de forma fiable para una variedad de páginas. Sin embargo, hay una página en particular que hace que mi cabeza en:

http://www.delicious.com/recent

Tratando de recuperar el selector CSS de cualquier elemento de la lista (en el centro de la página) falla por una razón muy simple:

Después de que la ascensión alcanza el primer elemento SPAN en su camino ascendente (puede detectarlo inspeccionando la página con las herramientas de desarrollo web de IE9 para la verificación) intenta procesarlo calculando su posición ordinal entre sus mismos hermanos de etiqueta. Para hacer eso, debemos preguntarle a los hermanos cuál es el nodo Padre. Aquí es donde las cosas se ponen raras. El elemento SPAN informa que su elemento primario es un elemento DIV con id = "índice reciente". Sin embargo, eso es no el inmediato padre del SPAN (el padre inmediato es LI class = "wrap isAdv"). Esto hace que el método falle porque, sorprendentemente, no detecta SPAN entre los niños.

Pero se vuelve aún más extraño. Recuperé y aislé el HtmlElement del SPAN. Entonces tuve que es Padre y lo utilizó para volver a descender de nuevo hasta el elemento SPAN usando:

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

Esto nos lleva de nuevo al nodo SPAN hemos empezado ... con un giro sin embargo:

regetSpanElement.Parent.TagName; 

Esto ahora informa LI como el padre XX. ¿Cómo puede ser esto? ¿Alguna idea?

Gracias de nuevo con antelación.

Notas:

  1. que guardan el código HTML (como se presenta en el interior WebBrowser.Document.Html) e inspeccionaron yo mismo para ser 100% seguro de que nada de divertido está teniendo lugar (también conocido como código diferente sirvió para Control WebBrowser que el que veo en IE9 - pero eso no está sucediendo, la estructura coincide con el 100% de la ruta en cuestión).

  2. Me postulo control WebBrowser en IE9 modo utilizando las instrucciones que se indican aquí:

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

    Tratando de conseguir el control WebBrowser e IE9 para funcionar de manera similar como sea posible.

  3. Sospecho que los efectos observados pueden deberse a un script que se ejecuta a mis espaldas. Sin embargo, mi conocimiento no es tan extenso en términos de programación web para precisarlo.

Editar: Typos

+0

': eq()' no es un selector válido de CSS. Supongo que usted quiso decir 'html> body> span: nth-child (3)> li: nth-child (6)> div> div: nth-child (4)> span> a'? – BoltClock

+0

Gracias por darme la oportunidad de aclarar: quise decir selectores de CSS destinados a ser entregados a jQuery y/o SizzleJS. Actualizaré la redacción en la publicación original para reflejar esto. Gracias de nuevo;) – xDisruptor

+0

Hola, todavía no tengo una respuesta, pero quería decir un par de cosas; En primer lugar, gracias por los detalles en su pregunta y su cortesía hacia los miembros de este sitio, ¡+1 por eso! En segundo lugar; Estoy fascinado por el contexto de la pregunta en sí; Entiendo lo que quieres hacer, ¿puedes ayudarnos a completar la sección ¿Por qué? ¿Estás construyendo una jerarquía de árbol o algún tipo de rastro de pan rallado? –

Respuesta

2

Basándose en: eq() es difícil! Es difícil volver a seleccionar confiablemente de un DOM que sea dinámico. Claro que puede funcionar en páginas muy estáticas, pero las cosas solo se vuelven más dinámicas todos los días. Puede considerar cambiar la estrategia un poco. Intenta usar un selector más inteligente y flexible. Tal vez el pop en algunos javascript de esta manera:

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 
} 

y utilizarlo para generar una variedad de selectores:

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); 

A continuación, utilice cada uno:

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

Y si su selector del mejor todavía viene con más de un elemento, tendrá que ser creativo en la forma en que combina un elemento dom - quizás levenshtein o tal vez hay algún texto específico, o puede recurrir a eq. ¡Espero que ayude!

Por cierto, asumí que tiene jQuery, debido a la referencia de sizzle. Podría inyectar lo anterior en una función anónima autoejecutable en una etiqueta de secuencia de comandos añadida al último hijo del cuerpo, por ejemplo.

Cuestiones relacionadas