Подтвердить что ты не робот

HtmlElement.Parent возвращает неверный родитель

Я пытаюсь создать селектор CSS для случайных элементов на веб-странице с помощью С#. Некоторые предпосылки:

Я использую форму с элементом управления WebBrowser. Во время навигации можно задать селектор CSS элемента под курсором. Получение html-элемента тривиально, конечно, с помощью:

WebBrowser.Document.GetElementFromPoint(<Point>);

Цель состоит в том, чтобы создать "строгий" css-селектор, ведущий к элементу под курсором, a-la:

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

Этот селектор основан на операторах eq, поскольку он предназначен для обработки jQuery и/или SizzleJS (эти две поддержки: eq - исходные селектор CSS не делают. Thumbs up @BoltClock, помогая мне прояснить это). Итак, вы получите картину. Чтобы достичь этой цели, мы предоставим восстановленный HtmlElement нижеуказанному методу и начнем подниматься вверх по дереву DOM, запросив родительский элемент каждого элемента, с которым мы сталкиваемся:

    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 offsprings!?   
        }

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

Этот метод работал надежно для разных страниц. Тем не менее, есть одна конкретная страница, которая делает мою голову в:

http://www.delicious.com/recent

Попытка извлечь CSS-селектор любого элемента в списке (в центре страницы) не удается по одной простой причине:

После того, как вознесение ударит по первому элементу SPAN в нем вверх (вы можете обнаружить его, проверив страницу с помощью инструментов веб-dev IE9 для проверки), он пытается обработать его, вычислив его порядковое положение среди тех же самых братьев-синглов. Для этого нам нужно спросить родителя node для братьев и сестер. Здесь все странно. Элемент SPAN сообщает, что Parent является элементом DIV с id = "recent-index". Однако не родительский немедленный для SPAN (непосредственным родителем является LI class= "wrap isAdv" ). Это приводит к сбою метода, потому что неудивительно, что он не обнаруживает SPAN среди детей.

Но он становится еще более странным. Я извлек и изолировал HtmlElement самого SPAN. Затем я получил его "Родитель" и использовал его, чтобы вернуться к элементу SPAN, используя:

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

Это привело нас к SPAN node, который мы начали... с одним поворотом:

regetSpanElement.Parent.TagName;

Теперь он сообщает LI как родительский X-X. Как это может быть? Любое понимание?

Еще раз заблаговременно.

Примечания:

  • Я сохранил код Html (как он был представлен в WebBrowser.Document.Html) и сам проверял его на 100% уверен, что ничего смешного не происходит (он же отличается от кода, который используется для управления WebBrowser, чем тот, который я вижу в IE9 - но это не происходит, если структура соответствует 100% для соответствующего пути).

  • Я запускаю элемент управления WebBrowser в IE9-режиме, используя инструкции, описанные здесь:

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

    Попытка заставить управление WebBrowser и IE9 работать как можно ближе.

  • Я подозреваю, что наблюдаемые эффекты могут быть связаны с тем, что какой-то script работает за моей спиной. Однако мои знания пока не достигают с точки зрения веб-программирования, чтобы прикрепить его.

Изменить: Typos

4b9b3361

Ответ 1

Опираясь на: eq() жестко! Трудно надежно переустановить из динамического DOM. Конечно, он может работать на очень статических страницах, но каждый день становится все более динамичным. Вы можете немного изменить стратегию. Попробуйте использовать более гибкий более гибкий селектор. Возможно, поп в каком-то javascript, например:

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
}

И используйте его для создания множества селекторов:

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

Затем используйте каждый:

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

И если ваш лучший селектор по-прежнему выходит с более чем одним элементом, вам нужно будет проявить творческий подход к тому, как вы сопоставляете элемент dom - возможно, levenshtein или, возможно, есть определенный текст, или вы можете отказаться от эквалайзера. Надеюсь, что это поможет!

Btw, я предположил, что у вас есть jQuery - из-за ссылки sizzle. Вы можете вставить вышеприведенную функцию в самозаверяющую анонимную функцию в теге script, добавленном к последнему ребенку тела, например.