Tôi giả thuyết rằng cho một phần tử mà tất cả các phần tử con của nó được sắp xếp theo thứ tự trên tài liệu, thì cách nhanh nhất là thực hiện tìm kiếm nhị phân, so sánh vị trí tài liệu của các phần tử. Tuy nhiên, như đã giới thiệu trong phần kết luận, giả thuyết đã bị bác bỏ. Bạn càng có nhiều yếu tố, tiềm năng hiệu suất càng lớn. Ví dụ: nếu bạn có 256 phần tử, thì (tối ưu) bạn sẽ chỉ cần kiểm tra 16 phần tử trong số đó! Đối với 65536, chỉ 256! Hiệu suất tăng lên đến sức mạnh của 2! Xem thêm các con số / thống kê. Truy cập 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);
Sau đó, cách bạn sử dụng nó là lấy thuộc tính 'parentIndex' của bất kỳ phần tử nào. Ví dụ, hãy xem bản demo sau.
(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>
Hạn chế
- Việc triển khai giải pháp này sẽ không hoạt động trong IE8 trở xuống.
Tìm kiếm nhị phân VS tuyến tính Trên 200 nghìn phần tử (có thể làm hỏng một số trình duyệt di động, HÃY CẨN THẬN!):
- Trong thử nghiệm này, chúng ta sẽ xem một tìm kiếm tuyến tính mất bao lâu để tìm phần tử ở giữa VS một tìm kiếm nhị phân. Tại sao lại là phần tử ở giữa? Bởi vì nó nằm ở vị trí trung bình của tất cả các vị trí khác, vì vậy nó thể hiện tốt nhất tất cả các vị trí có thể có.
Tìm kiếm nhị phân
(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);
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
Backwards (`lastIndexOf`) Tìm kiếm tuyến tính
(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);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
Chuyển tiếp (`indexOf`) Tìm kiếm tuyến tính
(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);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
TrướcElementSibling Counter Search
Đếm số PreviousElementSiblings để có được 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);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden;white-space:pre></div>
Không tìm kiếm
Để đo điểm chuẩn, kết quả của bài kiểm tra sẽ như thế nào nếu trình duyệt tối ưu hóa tìm kiếm.
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);
});
<output id=output> </output><br />
<div id=test style=visibility:hidden></div>
Cuộc thảo luận
Tuy nhiên, sau khi xem kết quả trên Chrome, kết quả lại trái ngược với những gì mong đợi. Tìm kiếm tuyến tính chuyển tiếp dumber là 187 ms đáng ngạc nhiên, 3850%, nhanh hơn so với tìm kiếm nhị phân. Rõ ràng là bằng cách nào đó, Chrome đã vượt trội một cách kỳ diệu console.assert
và tối ưu hóa nó, hoặc (lạc quan hơn) Chrome sử dụng nội bộ hệ thống lập chỉ mục số cho DOM và hệ thống lập chỉ mục nội bộ này được hiển thị thông qua các tối ưu hóa được áp dụng Array.prototype.indexOf
khi được sử dụng trên một HTMLCollection
đối tượng.
parent.childNodes
, đúng hơn làparent.children
?. Phần sau chỉ liệt kêElements
, loại trừText
các nút cụ thể ... Một số câu trả lời ở đây, ví dụ như cách sử dụngpreviousSibling
, dựa trên việc sử dụng tất cả các nút con, trong khi những người khác chỉ bận tâm với các nút con làElement
... (!)