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

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

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

//in regards to y   //:after  //:before   //not really before or after is it? 

是的,有点。 DOM3引入了Node.compareDocumentPosition ,它允许您比较两个元素的位置。 function不是很友好:它涉及bitmasks:这是一个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(请参阅答案评论)提供链接: 跨浏览器比较文档位置

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

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

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

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

我没有完整的代码,但我采取的方法(如果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 

这个问题需要一套关于之前之后考虑的规则。 遍及body的元素并看到接下来会发生什么,这当然很容易,但是有任何类型的等级优先级吗? 重复元素怎么样?

          

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