获取子节点索引

在直接的JavaScript(即没有扩展,如jQuery等),有没有一种方法来确定其父节点内的子节点的索引,而不需要迭代和比较所有的子节点?

例如,

var child = document.getElementById('my_element'); var parent = child.parentNode; var childNodes = parent.childNodes; var count = childNodes.length; var child_index; for (var i = 0; i < count; ++i) { if (child === childNodes[i]) { child_index = i; break; } } 

有没有更好的方法来确定孩子的指数?

你可以使用previousSibling属性遍历兄弟姐妹,直到你得到null并计算你遇到的兄弟姐妹数量。

 var i = 0; while( (child = child.previousSibling) != null ) i++; //at the end i will contain the index. 

请注意,在像Java这样的语言中,有一个getPreviousSibling()函数,但是在JS中这已经成为一个属性previousSibling

我很喜欢使用indexOf 。 因为indexOfArray.prototypeparent.children是一个NodeList ,所以你必须使用call(); 这是一种丑陋的,但它是一个class轮,并使用任何JavaScript开发人员应该熟悉的function。

 var child = document.getElementById('my_element'); var parent = child.parentNode; // The equivalent of parent.children.indexOf(child) var index = Array.prototype.indexOf.call(parent.children, child); 

ES6:

 Array.from(element.parentNode.children).indexOf(element) 

说明:

  • element.parentNode.children =>返回包含该元素的element的兄弟。

  • Array.from =>将children对象的构造函数转换为Array对象

  • indexOf =>你可以pply indexOf,因为你现在有了Array对象。

ES-短

 [...element.parentNode.children].indexOf(element); 

传播运营商是一个捷径

添加一个(前缀为安全)element.getParentIndex():

 Element.prototype.PREFIXgetParentIndex = function() { return Array.prototype.indexOf.call(this.parentNode.children, this); } 

使用二进制searchalgorithm来提高节点具有大量的兄弟节点时的性能。

 function getChildrenIndex(ele){ //IE use Element.sourceIndex if(ele.sourceIndex){ var eles = ele.parentNode.children; var low = 0, high = eles.length-1, mid = 0; var esi = ele.sourceIndex, nsi; //use binary search algorithm while (low <= high) { mid = (low + high) >> 1; nsi = eles[mid].sourceIndex; if (nsi > esi) { high = mid - 1; } else if (nsi < esi) { low = mid + 1; } else { return mid; } } } //other browsers var i=0; while(ele = ele.previousElementSibling){ i++; } return i; } 

最快的方法是进行二进制search,比较元素的文档位置。 你拥有的元素越多,performance的潜力就越大。 例如,如果你有256个元素,那么(最佳)你只需要检查其中的16个! 对于65536,只有256! 性能增长到2的力量! 查看更多的数字/统计。 访问维基百科 。

 (function(constructor){ 'use strict'; Object.defineProperty(constructor.prototype, 'parentIndex', { get: function() { var searchParent = this.parentElement; if (!searchParent) return -1; var searchArray = searchParent.children, thisOffset = this.offsetTop, stop = searchArray.length, p = 0, delta = 0; while (searchArray[p] !== this) { if (searchArray[p] > this) stop = p + 1, p -= delta; delta = (stop - p) >>> 1; p += delta; } return p; } }); })(window.Element || Node); 

然后,你使用它的方式是获取任何元素的“parentIndex”属性。 例如,看看下面的演示。

 (function(constructor){ 'use strict'; Object.defineProperty(constructor.prototype, 'parentIndex', { get: function() {debugger; var searchParent = this.parentNode; if (searchParent === null) return -1; var childElements = searchParent.children, lo = -1, mi, hi = childElements.length; while (1 + lo !== hi) { mi = (hi + lo) >> 1; if (!(this.compareDocumentPosition(childElements[mi]) & 0x2)) { hi = mi; continue; } lo = mi; } return childElements[hi] === this ? hi : -1; } }); })(window.Element || Node); output.textContent = document.body.parentIndex; output2.textContent = document.documentElement.parentIndex; 
 Body parentIndex is <b id="output"></b><br /> documentElements parentIndex is <b id="output2"></b> 
 Object.defineProperties(Element.prototype,{ group : { value: function (str, context) { // str is valid css selector like :not([attr_name]) or .class_name var t = "to_select_siblings___"; var parent = context ? context : this.parentNode; parent.setAttribute(t, ''); var rez = document.querySelectorAll("[" + t + "] " + (context ? '' : ">") + this.nodeName + (str || "")).toArray(); parent.removeAttribute(t); return rez; } }, siblings: { value: function (str, context) { var rez=this.group(str,context); rez.splice(rez.indexOf(this), 1); return rez; } }, nth: { value: function(str,context){ return this.group(str,context).indexOf(this); } } } 

防爆

 /* html */ <ul id="the_ul"> <li></li> ....<li><li>....<li></li> </ul> /*js*/ the_ul.addEventListener("click", function(ev){ var foo=ev.target; foo.setAttribute("active",true); foo.siblings().map(function(elm){elm.removeAttribute("active")}); alert("a click on li" + foo.nth()); }); 
 <body> <section> <section onclick="childIndex(this)">child a</section> <section onclick="childIndex(this)">child b</section> <section onclick="childIndex(this)">child c</section> </section> <script src="//code.jquery.com/jquery-1.11.3.min.js"></script> <script> function childIndex(e){ var i = 0; debugger while (e.parentNode.children[i] != e) i++; alert('child index '+i); } </script> </body>