繁体   English   中英

元素是在 DOM 中的另一个元素之前还是之后

[英]Is element before or after another element in DOM

有没有办法检测一个元素是出现在标记中的另一个元素之前还是之后? 这与 DOM 中的位置无关 它可以是孩子、兄弟姐妹、父母或父母的父母。 这是一个普遍的问题,所以没有标记可以分享。

澄清一下 - 这是关于元素在标记中的位置,而不是它的显示位置。 现在我想想我的问题有点奇怪,因为如果你有元素 X 和元素 Y 那么你可以有这些场景。

//in regards to y
<x />
<y /> //:after

<y /> //:before
<x />

<x><y /></x> //not really before or after is it?

是的,有点。 DOM3 引入了Node.compareDocumentPosition ,它允许您比较两个元素的位置。 该功能不是很友好:它涉及位掩码:这是一个 jQuery 插件,应该简化其使用。

此代码仅在 Firefox 9 和当前版本的 Chromium 上测试。 当然,它不会在旧版本的 IE 中工作。

$.fn.docPosition = function(element) {
    if (element.jquery) element = element[0];

    var position = this[0].compareDocumentPosition(element);

    if (position & 0x04) return 'after';
    if (position & 0x02) return 'before';
};

此外,包含另一个元素的元素被认为在结构中位于它之前。


好吧,稍微谷歌一下,我就能找到 John Resig (jQuery 的创建者)的这篇博文,其中包括与 IE <9 的兼容性。 (有点难看:它使用了两个非标准的功能位: containssourceIndex 。)这段代码应该是跨浏览器的:

$.fn.docPosition = function (element) {
    function comparePosition(a, b) {
        return a.compareDocumentPosition ? 
          a.compareDocumentPosition(b) : 
          a.contains ? 
            (a != b && a.contains(b) && 16) + 
              (a != b && b.contains(a) && 8) + 
              (a.sourceIndex >= 0 && b.sourceIndex >= 0 ?
                (a.sourceIndex < b.sourceIndex && 4) + 
                  (a.sourceIndex > b.sourceIndex && 2) :
                1)
            + 0 : 0;
    }

    if (element.jquery) element = element[0];

    var position = comparePosition(this[0], element);

    if (position & 0x04) return 'after';
    if (position & 0x02) return 'before';
};

node.compareDocumentPosition

概括
将当前节点的位置与任何其他文档中的另一个节点进行比较。

更新:这不适用于所有浏览器,但有一个修复程序。 感谢 Alnitak(见回答评论)提供链接: cross browser compare document position

一种蛮力方法可能是获取所有元素,然后获取集合中每个元素的索引。

var all = $('*');

var a_index = all.index($('#element_a'));
var b_index = all.index($('#element_b'));

if( a_index < b_index ) 
    alert( 'a is first' );
else
    alert( 'b is first' );

对于浏览器兼容的非 jQuery 解决方案,您可以这样做:

function sortInDocumentOrder( a, b ) {
    var all = document.getElementsByTagName('*');

    for( var i = 0; i < all.length; ++i ) {
        if( all[i] === a )
            return [a,b];  
        else if( all[i] === b )
            return [b,a];
    }
}

给它两个元素,它会按照文档顺序返回它们。

var a = document.getElementById('a');
var b = document.getElementById('b');

var inOrder = sortInDocumentOrder( a, b );

我没有完整的代码,但我会采取的方法(如果Node.compareDocumentPosition不可用)是:

  1. 获取两个元素的.parents()
  2. 找到两个链中每个链上最远的元素 - 这是共同的父元素
  3. 然后检查每个链中的下一个元素,它的索引是在另一个之前还是之后

通过让 DOM 为您完成一些工作,您可以使用一些技巧使 (1) 和 (2) 变得更简单:

var $a = $('#a'); // first element
var $b = $('#b'); // first element

$a.parents().andSelf().addClass('search'); // mark A and all of A's ancestors
var $parent = $b.closest('.search');       // find B's first marked ancestor

你混淆了一些事情。 标记的位置是 DOM 中的位置。 这一行显示了您的困惑:

<x><y /></x> //not really before or after is it?

当然根据任何合理的定义, yx之后 您应该将 DOM 视为一棵树,而不是文本文件中的字符。

现在,至于确定位置,请使用Node.compareDocumentPosition

node.compareDocumentPosition(otherNode)

返回值是具有以下值的位掩码:

DOCUMENT_POSITION_DISCONNECTED = 0x01;
DOCUMENT_POSITION_PRECEDING = 0x02;
DOCUMENT_POSITION_FOLLOWING = 0x04;
DOCUMENT_POSITION_CONTAINS = 0x08;
DOCUMENT_POSITION_CONTAINED_BY = 0x16;

这是Node.compareDocumentPosition的完整示例

    /**
     * Checks if the node1 is before the node2 in the DOM tree
     * It is true if
     * + node1 is sibling before node2
     * + node1 contains node2
     * + node1 is in the subtree that is before node2 or the subtree that contains it
     */
    public isNodeBefore(node1: Node, node2: Node): boolean {
        // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
        return (node2.compareDocumentPosition(node1) & (Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS)) != 0;
    }

这个问题需要对什么是之前之后考虑的一套规则。 遍历body的元素并查看接下来会发生什么当然很容易,但是是否有任何分层优先级? 重复元素呢?

<foo>
   <bar>
       <foobar />
   </bar>
</foo>
<foo>
   <baz />
</foo>
<foobar />

bazfoobar之前吗?

我会创建两个函数isBeforeisAfter 在这种情况下,它们都是真的(进行深度优先查找)。


var el = $('*');

function isBefore(elA, elB){
   return ( el.index($(elA).first()) < el.index($(elB).last()) );
}

function isAfter(elA, elB){
   return ( el.index($(elA).last()) > el.index($(elB).first()) );
}


isBefore('body', 'head'); // false
isAfter('body', 'head');  // true
function isBefore(a, b){

返回 a.sourceIndex < b.sourceIndex; }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM