Получить дочерний индекс node
В прямом javascript (т.е. нет расширений, таких как jQuery и т.д.) существует ли способ определить дочерний индекс node внутри родительского node без повторения и сравнения всех дочерних узлов?
например.
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;
}
}
Есть ли лучший способ определить дочерний индекс?
Ответы
Ответ 1
вы можете использовать свойство previousSibling
для повторного повтора через братьев и сестер, пока не вернетесь null
, и не подсчитайте, сколько бракосочетаний вы столкнулись:
var i = 0;
while( (child = child.previousSibling) != null )
i++;
//at the end i will contain the index.
Обратите внимание, что в таких языках, как Java, есть функция getPreviousSibling()
, однако в JS это стало свойством - previousSibling
.
Ответ 2
Я полюбил использование indexOf
для этого. Поскольку indexOf
находится на Array.prototype
, а parent.children
- NodeList
, вы должны использовать call();
. Это выглядит как уродливый, но это один лайнер и использует функции, которые любой разработчик javascript должен быть знаком. p >
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);
Ответ 3
ES6:
Array.from(element.parentNode.children).indexOf(element)
Пояснение:
-
element.parentNode.children
→ Возвращает братья element
, включая этот элемент.
-
Array.from
→ Array.from
конструктор children
к объекту Array
-
indexOf
→ Вы можете применить indexOf
потому что теперь у вас есть объект Array
.
Ответ 4
ES-Шортер
[...element.parentNode.children].indexOf(element);
Оператор распространения - это ярлык для этого
Ответ 5
Добавление элемента (prefixed for safety).getParentIndex():
Element.prototype.PREFIXgetParentIndex = function() {
return Array.prototype.indexOf.call(this.parentNode.children, this);
}
Ответ 6
Я предполагаю, что, учитывая элемент, где все его дочерние элементы упорядочены в документе последовательно, самый быстрый способ должен состоять в том, чтобы выполнить двоичный поиск, сравнивая позиции документа в элементах. Однако, как указано в заключении, гипотеза отвергается. Чем больше элементов у вас есть, тем больше потенциал для производительности. Например, если у вас было 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() {
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>
Ответ 7
Используйте алгоритм двоичного поиска, чтобы улучшить производительность, когда node имеет большое количество братьев и сестер.
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;
}
Ответ 8
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());
});
Ответ 9
<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>
function childIndex(e){
let i = 0;
while (e.parentNode.children[i] != e) i++;
alert('child index '+i);
}
</script>
</body>