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

Posted

技术标签:

【中文标题】是 DOM 中另一个元素之前还是之后的元素【英文标题】:Is element before or after another element in DOM 【发布时间】:2012-01-17 21:55:33 【问题描述】:

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

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

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

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

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

【问题讨论】:

标记中的位置 = DOM 中的位置 后面是什么意思?您是指该页面语言的读者扫描它的方式之后吗?对于英文读者来说,在页面上如此低或在相同的高度和右侧? @MikeSamuel - 我的意思是好像你真的打印了标记。我想可能有三种状态。之前,之后或(都不)的孩子 在第三个示例中,y 通常会被视为之后 x。 DOM 选择按文档顺序返回元素,因此x 将被视为最先出现。 node.compareDocumentPosition 有一个跨浏览器 shim,由 @Raynos 在 ***.com/a/8334758/6782 编写 【参考方案1】:

是的,有点。 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';
;

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


好的,谷歌搜索给我this blog post by John Resig(jQuery 的创建者),其中包括与 IE contains 和 sourceIndex。)这段代码应该是跨浏览器的:

$.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.DOCUMENT_POSITION_PRECEDING,而不是使用位掩码。见developer.mozilla.org/en-US/docs/Web/API/Node/…【参考方案2】:

node.compareDocumentPosition

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

更新: 这不适用于所有浏览器,但有一个解决方法。感谢 Alnitak(见答案 cmets)提供链接:cross browser compare document position

【讨论】:

请记住,IE @Raynos 在***.com/a/8334758/6782 处为node.compareDocumentPosition 编写了一个跨浏览器填充程序【参考方案3】:

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

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

【讨论】:

聪明。我要搞砸这个。我一时兴起问了这个问题,所以我需要一点时间来测试用例并确保这是一个合适的答案。【参考方案4】:

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

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

当然y x 之后的任何合理定义。您应该将 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;

【讨论】:

如果我们将 DOM 视为一棵树,那么如果我们使用中序或后序遍历,y 可能出现在x 之前。这仅取决于规范实现者选择使用的遍历方法。【参考方案5】:

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

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

通过让 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

【讨论】:

我非常喜欢这个。谢谢你的创意。今晚我会搞砸这个。【参考方案6】:

这个问题需要一套关于什么被认为是之前之后的规则。遍历body 中的元素并查看接下来会发生什么当然很容易,但是有任何层次的优先级吗?重复元素呢?

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

baz 是否在 foobar 之前?

我会创建两个函数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

【讨论】:

【参考方案7】:

这是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;
    

【讨论】:

【参考方案8】:

一种知道元素是否出现在其他元素之前的简单方法: 使用 sourceIndex 属性检索对象的序号位置。

function isBefore(a, b)
   return a.sourceIndex < b.sourceIndex;


isBefore(head,body); //true

【讨论】:

你能扩展你的答案吗?添加一些解释,显示该行的放置位置。现在就像评论“使用 sourceIndex”

以上是关于是 DOM 中另一个元素之前还是之后的元素的主要内容,如果未能解决你的问题,请参考以下文章

一个DOM元素绑定多个事件时,先执行冒泡还是捕获

一个DOM元素绑定多个事件时,先执行冒泡还是捕获

一个DOM元素绑定多个事件时,先执行冒泡还是捕获

js判断点击事件是被调用还是点击DOM对象触发

转载W3C下的冒泡和捕获机制。到底是冒泡呢。还是捕获呢。一个DOM元素绑定多个事件时如何执行。

我应该使用原始 js 还是 jquery 来定位 Vuejs 3 应用程序中的 DOM 元素?