3

我有一个 iframe,通过点击它的任何内容,css 类被应用在它上面(比如“selected”)。之后

单击下一个按钮时,它应该将此类应用于包含文本的下一个元素

并返回文本。遍历应该是基于文本节点的。虽然我确实试图做一些非常丑陋的事情,但我想必须有一些简单的解决方案可用。

这是我的代码:

$(function(){
$('#next').click(function(){
    var current_segment =$('#my_iframe').contents().find(".highlight");

    // if current segment has children
    if($(current_segment).children().not('.traversed_already').length > 0){
        $(current_segment).children().not('.traversed_already').first().addClass('highlight');
        $(current_segment).removeClass('highlight');

        // add class to stop repitative traversing
        $(current_segment).addClass('traversed_already');
    //                                                return false;

    // if has siblings and no children
    }else if($(current_segment).siblings().not('.traversed_already').length > 0 
        && $(current_segment).children().not('.traversed_already').length <= 0){
        $(current_segment).siblings().not('.traversed_already').first().addClass('highlight');
        $(current_segment).removeClass('highlight');

        // add class to stop repitative traversing
        $(current_segment).addClass('traversed_already'); 
    //                                                return false;

    // if no siblings and no children
    }else if($(current_segment).siblings().not('.traversed_already').length == 0 && 
        $(current_segment).children().not('.traversed_already').length == 0){

        // check the very first parent if traversed check its siblings
        var parent_segment = $(current_segment).parent().first();

        // if parent is already traversed already
        if($(parent_segment).hasClass('traversed_already')){

            // if parent is traversed but parent has sibling that is untraversed
            if($(parent_segment).siblings().not('.traversed_already').length > 0){
                $(parent_segment).siblings().not('.traversed_already').first().addClass('highlight');
                $(parent_segment).removeClass('highlight');

                // add class to stop repitative traversing
                $(parent_segment).addClass('traversed_already');
            //                                                        return false;
            // if no untraversed sibling then search for parent(s)
            }else{
                // Look for the parent in Dom tree which is not traversed
                $(parent_segment).parents().not('.traversed_already').first().addClass('highlight');
                $(parent_segment).removeClass('highlight');

                // add class to stop repitative traversing
                $(parent_segment).addClass('traversed_already');
            //                                                        return false;
            } // end of if traversed parent has siblings(untraversed).

        // if parent is not traversed
        } else {
            $(parent_segment).addClass('highlight');
            $(current_segment).removeClass('highlight');

            // add class to stop repitative traversing
            $(current_segment).addClass('traversed_already');
        } // end of if parent is already traversed or not

    //                                                return false;
    } // end of else if no siblings and no parents

此代码的问题是:

它只是通过首先看到孩子然后是兄弟姐妹然后是父母兄弟姐妹来遍历下一个元素,但是当出现父母>父母>父母兄弟姐妹时它会失败。

注意:我相信这可以通过 DOM 遍历很容易地完成,但我无法找到正确的解决方案。

4

5 回答 5

2

这是一个最小的 DOM 遍历脚本。我不知道它是最好的还是最小的,但我很确定它正在工作(仅浏览器测试,但看起来很可靠)。

getNextElement = function(element, goingUp) {
  if (element.firstChild && !goingUp){
    return element.firstChild;
  }
  else if (element.nextSibling){
    return element.nextSibling;
  }
  else if (element.parentNode) {
    return arguments.callee(element.parentNode, true);
  }
};

这实际上会命中文本节点以及其他节点。我认为您实际上可以利用它。但如果你只想要元素,只需更改firstChildfirstElementChild和。尽管 IE8 或更低版本不支持此功能。通过运行然后运行来尝试一下nextSiblingnextElementSiblingelement = getNextElement(document.getElementsByTagName('body')[0])

if (element.style){element.style.backgroundColor = 'yellow'};
element = getNextElement(element);

反复遍历(并突出显示)DOM。

于 2013-01-29T23:59:05.060 回答
2

最简单的解决方案是DOM TreeWalker,我很幸运,我花了很长时间才弄清楚这一点。

是它的链接。希望它能帮助面临同样问题的人,而不是加速数小时或数天,它会立即帮助他们。

于 2013-01-30T04:09:50.703 回答
1

我不确定你的 iframe 的标记有多复杂,所以我使用了一些我自己快速创建的标记。

首先,单击一个元素以记下您的起始位置 - 该元素将突出显示 - 然后单击“下一步”按钮,代码将运行突出显示它找到的下一个元素。一旦所有元素都被突出显示,就不会再发生任何事情了。取决于元素的嵌套程度取决于代码是否会看到一个元素并突出显示它。一直以来,找到的内容都将在控制台窗口中可见,并给出“已选择”类。

我知道您要求提供基于文本节点的解决方案,但我“认为”这可行。试试看吧。

iframe.html:

<html>
<head>
<title>iFrame</title>
</head>
<body>
<ul>
<li><abbr title="Cascading Style Sheets">CSS</abbr> (an abbreviation; <code>abbr</code> markup used)</li>
<li><acronym title="radio detecting and ranging">radar</acronym> (an acronym; <code>acronym</code> markup used)</li>
<li><b>bolded</b> (<code>b</code> markup used - just bolding with unspecified semantics)</li>
<li><big>big thing</big> (<code>big</code> markup used)</li>
<li><cite>Origin of Species</cite> (a book title; <code>cite</code> markup used)</li>
<li><code>a[i] = b[i] + c[i);</code> (computer code; <code>code</code> markup used)</li>
<li>here we have some <del>deleted</del> text (<code>del</code> markup used)</li>
<li>an <dfn>octet</dfn> is an entity consisting of eight bits (<code>dfn</code> markup used for the term being defined)</li>
<li>this is <em>very</em> simple (<code>em</code> markup used for emphasizing a word)</li>
<li><i lang="la">Homo sapiens</i> (should appear in italics; <code>i</code> markup used)</li>
<li>here we have some <ins>inserted</ins> text (<code>ins</code> markup used)</li>
<li><q>Hello!</q> (<code>q</code> markup used for quotation)</li>
<li>He said: <q>She said <q>Hello!</q></q> (a quotation inside a quotation)</li>
<li><small>this is not that important</small> (<code>small</code> markup used)</li>
<li><strike>overstruck</strike> (<code>strike</code> markup used; note: <code>s</code> is a nonstandard synonym for <code>strike</code>)</li>
<li><strong>this is highlighted text</strong> (<code>strong</code> markup used)</li>
<li>In order to test how subscripts and superscripts (<code>sub</code> and <code>sup</code> markup) work inside running text, we need some dummy text around constructs like x<sub>1</sub> and H<sub>2</sub>O (where subscripts occur). So here is some fill so that you will (hopefully) see whether and how badly the subscripts and superscripts mess up vertical spacing between lines. Now superscripts: M<sup>lle</sup>, 1<sup>st</sup>, and then some mathematical notations: e<sup>x</sup>, sin<sup>2</sup><i>x</i>, and some nested superscripts (exponents) too: e<sup>x<sup>2</sup></sup> and f(x)<sup>g(x)<sup>a+b+c</sup></sup> (where 2 and a+b+c should appear as exponents of exponents).</li>
<li><u>underlined</u> text (<code>u</code> markup used)</li>
<li>the command <code>cat</code><var>filename</var> displays the file specified by the <var>filename</var> (<code>var</code> markup used to indicate a word as a variable).</li>
</ul>
</body>
</html>

演示.html:

<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script><script type="text/javascript">
$(document).ready(function(){
    $('#iframe').load(function(){
        $all = $('#iframe').contents().find('body *').filter(function(){
            var $this = $(this);
            return $this.children().length == 0 && $.trim($this.text()).length > 0;
        });         

        $('#next').on('click', highlightTextNode);
        $('#iframe').contents().find('body').on('click', highlightTextNode);
    });

    var highlightTextNode = function(event){
        event.stopPropagation();
        $el = $(event.target).children().length == 0 ? $(event.target) : $(event.target).find(':first-child').first();

        if(!$el.hasClass('selected') && $.trim($el.text()).length > 0 && $('#iframe').contents().find('body').find('.selected').length == 0){
            $el.addClass('selected').css({backgroundColor: 'yellow'});
            console.log($.trim($el.text()));
        } else {
            var found = false;
            var finished = false;

            $all.each(function(){
                finished = true;

                if(found && !$(this).hasClass('selected')){
                    found = false;
                    $(this).addClass('selected').css({backgroundColor: 'yellow'});
                    console.log($.trim($(this).text()));
                    finished = false;
                    return false;
                }

                if($(this).hasClass('selected'))
                    found = true;
            });

            if(finished){ //start at the beginning again
                $all.each(function(){                   
                    if(found && !$(this).hasClass('selected')){
                        found = false;
                        $(this).addClass('selected').css({backgroundColor: 'yellow'});
                        console.log($.trim($(this).text()));
                        return false;
                    }

                    if($(this).hasClass('selected'))
                        found = true;                   
                });
            }
        }
    }       
});
</script>
</head>
<body>
<input type="button" value="NEXT" id="next" />
<iframe id="iframe" src="iframe.html" width="100%" height="100%"></iframe>
</body>
</html>
于 2013-01-29T01:43:58.223 回答
0

你为什么不使用一些while功能,比如

$(function(){
$('#next').click(function(){
    var current_segment =$('#my_iframe').contents().find(".highlight");
    var doWhile = true; // variable that will determine that You have to go again trough the loop
    while (doWhile){
       ... // Your code
       // Every time where You put
          // add class to stop repitative traversing
          $(element).addClass('traversed_already');
      // give also
          doWhile = false; // to stop while function
      // of course return will take You out as well
    }

它应该工作。

于 2013-01-23T20:33:51.177 回答
0

下面的解决方案应该可以满足您的目标。

笔记:

  • 它使用了一些较新的浏览器/JS 功能,因此您可能必须将其移植到 jQuery 或类似的东西以实现跨浏览器兼容性。
  • 它会预先计算完整的文本节点列表,因此只有当您的 iframe DOM 是静态的时它才会起作用。
  • 它遍历所有文本节点,而不是所有包含文本的元素。也就是说,对于 HTML <div id="a">1<div id="b">2</div>3</div>,它将选择1then而不是2then 。我相信这是您所要求的,但不是 100% 清楚。3ab
  • 我没有测试过。

编码:

// First grab the iframe's document object.
var iframeDocument = document.querySelector('iframe').contentDocument;

// Then generate the list of all text nodes in the iframe.
var textNodes = [];
function findTextNodes(curElem){
    Array.prototype.slice.apply(curElem.childNodes).forEach(function(childNode) {
        switch (childNode.nodeType) {
            case Node.TEXT_NODE:
                textNodes.push(childNode);
            case Node.ELEMENT_NODE:
                findTextNodes(childNode);
        }
    }, this);
}
findTextNodes(iframeDocument.body);

// Helper function to move the text node selection.
function selectTextNodeAtIndex(index){
    if (index > 0) {
        // Replace the old .selected wrapper with the original text node.
        var oldWrapperElem = iframeDocument.querySelector('.selected');
        oldWrapperElem.parentNode.replaceChild(textNodes[index - 1], oldWrapperElem);
    }

    // Replace the current text node with a .selected wrapper.
    var wrapperElem = iframeDocument.createElement('span');
    wrapperElem.classList.add('selected');
    wrapperElem.textContent = textNodes[index].textContent;
    textNodes[index].parentNode.replaceChild(wrapperElem, textNodes[index]);
}

// Finally, select the first text node and set up the click handler.
var curIndex = 0;
selectTextNodeAtIndex(curIndex);
document.querySelector('#next').addEventListener('click', function () {
    curIndex++;
    selectTextNodeAtIndex(curIndex);
}, true);
于 2013-01-26T19:50:33.537 回答