Get child node index
W prostym javascript (tzn. brak rozszerzeń takich jak jQuery itp.), czy istnieje sposób na określenie indeksu węzła potomnego wewnątrz węzła nadrzędnego bez iteracji i porównywania wszystkich węzłów potomnych?
Np.,
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;
}
}
Czy jest lepszy sposób na określenie indeksu dziecka?
9 answers
Możesz użyć właściwości previousSibling
, aby przejść z powrotem przez rodzeństwo, dopóki nie wrócisz null
i policzyć, ile rodzeństwa napotkałeś:
var i = 0;
while( (child = child.previousSibling) != null )
i++;
//at the end i will contain the index.
Należy pamiętać, że w językach takich jak Java istnieje funkcja getPreviousSibling()
, jednak w JS stała się to właściwością -- previousSibling
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-06 16:09:36
Zacząłem lubić używać indexOf
do tego. Ponieważ {[1] } jest włączone Array.prototype
i parent.children
jest NodeList
, musisz użyć call();
jest trochę brzydki, ale jest to jeden liner i używa funkcji, które każdy programista javascript powinien znać.
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);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-29 17:58:53
ES6:
Array.from(element.parentNode.children).indexOf(element)
Wyjaśnienie:
element.parentNode.children
→ zwraca bracielement
, łącznie z tym elementem.-
Array.from
→ rzuca konstruktorchildren
naArray
obiekt indexOf
→ możesz zastosowaćindexOf
, ponieważ masz teraz obiektArray
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 13:16:33
ES-Shorter
[...element.parentNode.children].indexOf(element);
Operator spreadu jest skrótem
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 10:10:39
Dodawanie elementu (prefiks dla bezpieczeństwa).getParentIndex ():
Element.prototype.PREFIXgetParentIndex = function() {
return Array.prototype.indexOf.call(this.parentNode.children, this);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-16 10:41:32
Użyj binarnego algorytmu wyszukiwania , aby poprawić wydajność, gdy węzeł ma dużą ilość rodzeństwa.
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-15 16:08:05
Najszybszym sposobem jest wyszukiwanie binarne, porównujące pozycje dokumentów elementów. Im więcej elementów masz, tym większy potencjał wydajności. Na przykład, jeśli masz 256 elementów, to (optymalnie) wystarczy sprawdzić tylko 16 z nich! / Align = "left" / 65536 Wydajność rośnie do potęgi 2! Zobacz więcej liczb/statystyk. Odwiedź Wikipedię .
(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);
Następnie, sposób, w jaki go używasz, polega na uzyskaniu właściwości 'parentIndex' dowolnego elementu. Na przykład, sprawdź następujące demo.
(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>
Ograniczenia
- Ta implementacja rozwiązania nie będzie działać w IE8 i poniżej.
Wyszukiwanie binarne VS liniowe na 200 tysiącach elementów (może spowodować awarię niektórych przeglądarek mobilnych, uważaj!):
- w tym teście zobaczymy, jak długo zajmie wyszukiwanie liniowe, aby znaleźć element środkowy VS wyszukiwanie binarne. Dlaczego środkowy element? Ponieważ jest w średnia lokalizacja wszystkich innych lokalizacji, więc najlepiej reprezentuje wszystkie możliwe lokalizacje.
Wyszukiwanie Binarne
(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);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
setTimeout(function(){
var child=test.children.item(99e+3);
var start=performance.now();
for (var i=200; i--; )
console.assert( (child=child.nextElementSibling).parentIndexBinarySearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the binary search ' + ((end-start)*10).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.innerHTML = '';
}, 125);
}, 125);
<output id=output> </output><br />
<div id=test style=visibility:hidden></div>
<style>body{overflow:hidden}</style>
Backwards (`lastIndexOf') Linear Search
(function(t){"use strict";var e=Array.prototype.lastIndexOf;Object.defineProperty(t.prototype,"parentIndexLinearSearch",{get:function(){return e.call(t,this)}})})(window.Element||Node);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
setTimeout(function(){
var child=test.children.item(99e+3);
var start=performance.now();
for (var i=2000; i--; )
console.assert( (child=child.nextElementSibling).parentIndexLinearSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the linear search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.innerHTML = '';
}, 125);
}, 125);
<output id=output> </output><br />
<div id=test style=visibility:hidden></div>
<style>body{overflow:hidden}</style>
Forwards (`indexOf') Wyszukiwanie liniowe
Wyszukiwanie liniowe, z wyjątkiem przechodzenia do przodu do tyłu zamiast do tyłu do przodu.
(function(t){"use strict";var e=Array.prototype.indexOf;Object.defineProperty(t.prototype,"parentIndexLinearSearch",{get:function(){return e.call(t,this)}})})(window.Element||Node);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
setTimeout(function(){
var child = test.children.item(99e+3);
var start=performance.now();
for (var i=2000; i--; )
console.assert( (child=child.nextElementSibling).parentIndexLinearSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the linear search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.innerHTML = '';
}, 125);
}, 125);
<output id=output> </output><br />
<div id=test style=visibility:hidden></div>
<style>body{overflow:hidden}</style>
Jednak po obejrzeniu wyników w Chrome, wyniki są przeciwieństwem tego, czego oczekiwano. Głupsze wyszukiwanie liniowe było zaskakujące 187ms, 3850%, szybciej niż wyszukiwanie binarne. Oczywiście Chrome w jakiś sposób przechytrzył console.assert
i zoptymalizował go, lub (bardziej optymistycznie) Chrome wewnętrznie używa numerycznego systemu indeksowania dla DOM, a ten wewnętrzny system indeksowania jest eksponowany poprzez optymalizacje zastosowane do Array.prototype.indexOf
, gdy jest używany na obiekcie HTMLCollection
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-04 21:40:55
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);
}
}
}
Ex
/* 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());
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-04 20:02:25
<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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-21 17:22:24