我想找到id为'whereami'
的子div的索引。
<div id="parent">
<div></div>
<div></div>
<div id="whereami"></div>
<div></div>
</div>
目前我正在使用此功能查找孩子的索引。
function findRow(node){
var i=1;
while(node.previousSibling){
node = node.previousSibling;
if(node.nodeType === 1){
i++;
}
}
return i; //Returns 3
}
var node = document.getElementById('whereami'); //div node to find
var index = findRow(node);
小提琴:http://jsfiddle.net/grantk/F7JpH/2/
问题
当存在数千个div节点时,while循环必须遍历每个div以对它们进行计数。这可能需要一段时间。
有没有更快的方法来解决这个问题?
*请注意,id将更改为不同的div节点,因此需要能够重新计算。
答案 0 :(得分:37)
出于好奇,我针对jQuery的.index()
和我的下面代码运行了代码:
function findRow3(node)
{
var i = 1;
while (node = node.previousSibling) {
if (node.nodeType === 1) { ++i }
}
return i;
}
事实证明,jQuery比你的实现(在Chrome / Mac上)大约慢50%,并且可以说它高出1%。
修改强>
不能完全放弃这个,所以我又增加了两个方法:
使用Array.indexOf
[].indexOf.call(node.parentNode.children, node);
我早期的实验代码的改进,如HBP's answer所示,DOMNodeList
被视为一个数组,它使用Array.indexOf()
来确定其.parentNode.children
中的位置都是元素。我的第一次尝试是使用.parentNode.childNodes
但由于文本节点而导致结果不正确。
使用previousElementSibling
受user1689607's answer的启发,最近的浏览器除了.previousSibling
之外还有另一个名为previousElementSibling
的属性,它将两个原始语句组合在一起。 IE&lt; = 8没有此属性,但.previousSibling
已经这样做,因此feature detection可以正常工作。
(function() {
// feature detection
// use previousElementSibling where available, IE <=8 can safely use previousSibling
var prop = document.body.previousElementSibling ? 'previousElementSibling' : 'previousSibling';
getElementIndex = function(node) {
var i = 1;
while (node = node[prop]) { ++i }
return i;
}
结论
IE&lt; = 8浏览器不支持使用Array.indexOf()
,仿真速度不够快;但是,它确实提高了20%的性能。
使用功能检测和.previousElementSibling
可以提高7倍(在Chrome上),我还没有在IE8上进行测试。
答案 1 :(得分:5)
通过选择Array
indexOf
,您可以使用:
var wmi = document.getElementById ('whereami');
index = [].indexOf.call (wmi.parentNode.children, wmi);
[仅在Chrome浏览器上测试]
答案 2 :(得分:3)
我在jsPerf test添加了两个测试。两者都使用previousElementSibling
,但第二个包含IE8及更低版本的兼容性代码。
它们在现代浏览器(目前使用的大多数浏览器)中表现都非常出色,但在旧浏览器中只会受到轻微打击。
这是第一个不包含兼容性修补程序的内容。它适用于IE9及更高版本,以及几乎所有的Firefox,Chrome和Safari。
function findRow6(node) {
var i = 1;
while (node = node.previousElementSibling)
++i;
return i;
}
以下是具有兼容性修补程序的版本。
function findRow7(node) {
var i = 1,
prev;
while (true)
if (prev = node.previousElementSibling) {
node = prev;
++i;
} else if (node = node.previousSibling) {
if (node.nodeType === 1) {
++i;
}
} else break;
return i;
}
因为它会自动抓取元素兄弟,所以nodeType
不需要测试,循环总体上更短。这解释了性能的大幅提升。
我还添加了一个循环.children
的最后一个版本,并将node
与每个版本进行比较。
这不如previousElementSibling
版本快,但仍然比其他(至少在Firefox中)更快。
function findRow8(node) {
var children = node.parentNode.children,
i = 0,
len = children.length;
for( ; i < len && children[i] !== node; i++)
; // <-- empty statement
return i === len ? -1 : i;
}
回到previousElementSibling
版本,这是一个可能会稍微提高性能的调整。
function findRow9(node) {
var i = 1,
prev = node.previousElementSibling;
if (prev) {
do ++i;
while (prev = prev.previousElementSibling);
} else {
while (node = node.previousSibling) {
if (node.nodeType === 1) {
++i;
}
}
}
return i;
}
我没有在jsPerf中对它进行过测试,但根据previouselementSibling
的存在将其分解为两个不同的循环只会让我觉得有用。
也许我会稍微添加一下。
我继续将其添加到此答案顶部链接的测试中。它确实有点帮助,所以我认为它可能值得做。
答案 3 :(得分:2)
杰克解决方案略有改进,提升3%。确实有点奇怪。
function findRow5(node)
{
var i = 2;
while (node = node.previousSibling)
i += node.nodeType ^ 3;
return i >> 1;
}
因为在这种情况下(在大多数情况下)只有两个可能的nodeType
:
Node.ELEMENT_NODE == 1
Node.TEXT_NODE == 3
因此,nodeType
的xor 3将提供2
和0
。
答案 4 :(得分:1)
因为你正在使用jQuery。索引应该做的伎俩
jQuery('#whereami').index()
但是你将如何在以后使用该索引?
答案 5 :(得分:1)
试试这个:
function findRow(node) {
var i = 1;
while ((node = node.previousSibling) != null) {
if (node.nodeType === 1) i++;
}
return i; //Returns 3
}
答案 6 :(得分:1)
一般来说,除非代码在循环中运行,否则性能上的微小差异可以忽略不计。必须一次而不是每次运行代码都会快得多。
做一次这样的事情:
var par = document.getElementById('parent');
var childs = par.getElementsByTagName('*');
for (var i=0, len = childs.length;i < len;i++){
childs[i].index = i;
}
随后找到索引就像:
document.getElementById('findme').index
听起来好像你正在做的事情可以通过在DOM和javascript之间建立更清晰的关系而受益。考虑学习Backbone.js,这是一个小型的JavaScript MVC库,它使Web应用程序更容易控制。
编辑:我删除了我使用的jQuery。我通常会避免使用它,但是在SO上有相当偏好,所以我认为它最终会被使用。在这里你可以看到明显的区别:http://jsperf.com/sibling-index/8
答案 7 :(得分:1)
我假设给定一个元素,其中所有子元素按顺序排列在文档上,最快的方法应该是进行二元搜索,比较元素的文档位置。但是,正如结论中所介绍的那样,假设被拒绝了。您拥有的元素越多,性能潜力就越大。例如,如果您有256个元素,那么(最佳)您只需要检查其中的16个元素!对于65536,只有256!性能增长到2的力量!查看更多数字/统计数据。访问Wikipedia
(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);
然后,您使用它的方式是获取&#39; parentIndex&#39;任何元素的属性。例如,请查看以下演示。
(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;
&#13;
Body parentIndex is <b id="output"></b><br />
documentElements parentIndex is <b id="output2"></b>
&#13;
<强>限制强>
(function(constructor){
'use strict';
Object.defineProperty(constructor.prototype, 'parentIndexBinarySearch', {
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);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var child=test.children.item(99.9e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=200 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99.9e+3+i+Math.random())).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.remove();
test = null; // free up reference
}, 125);
}, 125);
&#13;
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
&#13;
(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:
requestAnimationFrame(function(){
var child=test.children.item(99e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=2000 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99e+3+i+Math.random())).parentIndexLinearSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the backwards linear search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
&#13;
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
&#13;
(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:
requestAnimationFrame(function(){
var child=test.children.item(99e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=2000 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99e+3+i+Math.random())).parentIndexLinearSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the forwards linear search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
&#13;
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
&#13;
计算PreviousElementSiblings的数量以获取parentIndex。
(function(constructor){
'use strict';
Object.defineProperty(constructor.prototype, 'parentIndexSiblingSearch', {
get: function() {
var i = 0, cur = this;
do {
cur = cur.previousElementSibling;
++i;
} while (cur !== null)
return i; //Returns 3
}
});
})(window.Element || Node);
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var child=test.children.item(99.95e+3);
var start=performance.now(), end=Math.round(Math.random());
for (var i=100 + end; i-- !== end; )
console.assert( test.children.item(
Math.round(99.95e+3+i+Math.random())).parentIndexSiblingSearch );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the PreviousElementSibling search ' + ((end-start)*20).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
&#13;
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
&#13;
如果浏览器优化了搜索,则用于对测试结果进行基准测试。
test.innerHTML = '<div> </div> '.repeat(200e+3);
// give it some time to think:
requestAnimationFrame(function(){
var start=performance.now(), end=Math.round(Math.random());
for (var i=2000 + end; i-- !== end; )
console.assert( true );
var end=performance.now();
setTimeout(function(){
output.textContent = 'It took the no search ' + (end-start).toFixed(2) + 'ms to find the 999 thousandth to 101 thousandth children in an element with 200 thousand children.';
test.remove();
test = null; // free up reference
}, 125);
});
&#13;
<output id=output> </output><br />
<div id=test style=visibility:hidden></div>
&#13;
但是,在Chrome中查看结果后,结果与预期相反。数字转发线性搜索是一个令人惊讶的187毫秒,3850%,比二进制搜索更快。很明显,Chrome以某种方式神奇地超越了console.assert
并对其进行了优化,或者(更乐观地)Chrome内部使用了数字索引系统来实现DOM,而这个内部索引系统通过应用于Array.prototype.indexOf
的优化来公开。用于HTMLCollection
对象。
答案 8 :(得分:1)
信用额度: Alain Cruz
来源: https://stackoverflow.com/a/58845058/3626361(如果有帮助的话,请给他投票)
问题:如何通过单击返回表单元格的行和列索引?
const cells = document.querySelectorAll('td');
cells.forEach(cell => {
cell.addEventListener('click', () =>
console.log("Row index: " + cell.closest('tr').rowIndex + " | Column index: " + cell.cellIndex));
});
<table>
<tr>
<td>0:0</td>
<td>0:1</td>
<td>0:2</td>
<td>0:3</td>
</tr>
<tr>
<td>1:0</td>
<td>1:1</td>
<td>1:2</td>
<td>1:3</td>
</tr>
<tr>
<td>2:0</td>
<td>2:1</td>
<td>2:2</td>
<td>2:3</td>
</tr>
</table>
干杯!斯特凡诺