javascript 为什么`.getElementById` 在节点上不起作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17508079/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why doesn't `.getElementById` work on a node?
提问by Oriol
I thing that if .getElementById
were available on all nodes, there would be two main advantages:
我认为,如果.getElementById
在所有节点上都可用,将有两个主要优点:
It could be possible to select nodes which don't belong to the document.
For example, I would like to do something like
function foo(html){ var el = document.createElement('div'); el.innerHTML = html; var target = el.getElementById('target'); /* Do something with `target` */ }
But I can't because I get
TypeError: el.getElementById is not a function
.Then, I don't want to use a class instead of an id, I must do
function foo(html){ var el = document.createElement('div'); el.innerHTML = html; document.body.appendChild(el); var target = document.getElementById('target'); document.body.removeChild(el); /* Do something with `target` */ }
But the document could already have an element with
id="target"
. Then, I should dofunction foo(html){ var iframe = document.createElement('iframe'); iframe.onload = function(){ var doc = iframe.contentDocument || iframe.contentWindow.document, el = document.createElement('div'); el.innerHTML = html; doc.body.appendChild(el); var target = doc.getElementById('target'); document.body.removeChild(iframe); /* Do something with `target` */ }; iframe.src = 'about:blank'; document.body.appendChild(iframe); }
But the code above doesn't work if I want
foo
to return something related withhtml
, because the main code runs after, with theonload
event.It could increase the performance, if the document has lots of elements and you know that the element you are searching is a descendant of an element that you already have in a variable
For example, if I have a document with the following structure:
<body> <div id="div-1"> <div id="div-1-1"> <div id="div-1-1-1"> ... </div> <div id="div-1-1-2"> ... </div> ... </div> <div id="div-1-2"> <div id="div-1-2-1"> ... </div> <div id="div-1-2-2"> ... </div> ... </div> ... </div> <div id="div-2"> <div id="div-2-1"> <div id="div-2-1-1"> ... </div> <div id="div-2-1-2"> ... </div> ... </div> <div id="div-2-2"> <div id="div-2-2-1"> ... </div> <div id="div-2-2-2"> ... </div> ... </div> ... </div> ... </body>
And I do...
var el1 = document.getElementById('div-9999999'), el2 = document.getElementById('div-9999999-1-2'), el3 = document.getElementById('div-9999999-1-2-999999'), el4 = document.getElementById('div-9999999-1-2-999999-1-2-3-4-5');
... it could be much slower than
var el1 = document.getElementById('div-9999999'), el2 = el1.getElementById('div-9999999-1-2'), el3 = el2.getElementById('div-9999999-1-2-999999'), el4 = el3.getElementById('div-9999999-1-2-999999-1-2-3-4-5');
(Of course, this example is a simplification, and in this case using
.childNodes[]
or.children[]
would be much better);
可以选择不属于文档的节点。
例如,我想做类似的事情
function foo(html){ var el = document.createElement('div'); el.innerHTML = html; var target = el.getElementById('target'); /* Do something with `target` */ }
但我不能,因为我得到
TypeError: el.getElementById is not a function
.然后,我不想使用类而不是 id,我必须这样做
function foo(html){ var el = document.createElement('div'); el.innerHTML = html; document.body.appendChild(el); var target = document.getElementById('target'); document.body.removeChild(el); /* Do something with `target` */ }
但是文档可能已经有一个带有
id="target"
. 那么,我应该做function foo(html){ var iframe = document.createElement('iframe'); iframe.onload = function(){ var doc = iframe.contentDocument || iframe.contentWindow.document, el = document.createElement('div'); el.innerHTML = html; doc.body.appendChild(el); var target = doc.getElementById('target'); document.body.removeChild(iframe); /* Do something with `target` */ }; iframe.src = 'about:blank'; document.body.appendChild(iframe); }
但是,如果我想
foo
返回与 相关的内容html
,则上面的代码不起作用,因为主代码在onload
事件之后运行。如果文档有很多元素并且您知道要搜索的元素是变量中已有元素的后代,则它可以提高性能
例如,如果我有一个具有以下结构的文档:
<body> <div id="div-1"> <div id="div-1-1"> <div id="div-1-1-1"> ... </div> <div id="div-1-1-2"> ... </div> ... </div> <div id="div-1-2"> <div id="div-1-2-1"> ... </div> <div id="div-1-2-2"> ... </div> ... </div> ... </div> <div id="div-2"> <div id="div-2-1"> <div id="div-2-1-1"> ... </div> <div id="div-2-1-2"> ... </div> ... </div> <div id="div-2-2"> <div id="div-2-2-1"> ... </div> <div id="div-2-2-2"> ... </div> ... </div> ... </div> ... </body>
而我...
var el1 = document.getElementById('div-9999999'), el2 = document.getElementById('div-9999999-1-2'), el3 = document.getElementById('div-9999999-1-2-999999'), el4 = document.getElementById('div-9999999-1-2-999999-1-2-3-4-5');
...它可能比
var el1 = document.getElementById('div-9999999'), el2 = el1.getElementById('div-9999999-1-2'), el3 = el2.getElementById('div-9999999-1-2-999999'), el4 = el3.getElementById('div-9999999-1-2-999999-1-2-3-4-5');
(当然,这个例子是一个简化,在这种情况下使用
.childNodes[]
or.children[]
会更好);
Then, why doesn't .getElementById
work on a node? I don't see any disadvantage, only advantages
那么,为什么.getElementById
在一个节点上不起作用呢?我看不出有什么缺点,只有优点
采纳答案by DaveRandom
The primary reason is efficiency.
首要原因是效率。
At the document level, a single ID reference table needs to be maintained, and any alterations to the document require an O(1)
modification to this table. To implement it at the node level, an equivalent table would need to be maintained for each nodeand updates to any given element, regardless of whether it is attached to the document, would need to bubble through every one of its parent nodes. This very quickly eats a lot of memory and takes a long time to update the DOM.
在文档级别,需要维护单个 ID 引用表,对文档的任何更改都需要O(1)
修改此表。为了在节点级别实现它,需要为每个节点维护一个等效的表,并且对任何给定元素的更新,无论它是否附加到文档,都需要通过其每个父节点进行冒泡。这会很快占用大量内存,并且需要很长时间来更新 DOM。
Another important thing to note is that (from a Javascript point of view, at least) everyelement is owned by a document (or document fragment). Therefore the argument of "but I can have duplicated IDs as long as only one of them is attached to the document" doesn't really stack up - it's onlypossible to manage this based on the nodes on the DOM when you take this into account.
另一个需要注意的重要事情是(至少从 Javascript 的角度来看)每个元素都归一个文档(或文档片段)所有。因此,“但我可以有重复的 ID,只要其中一个附加到文档”的论点并没有真正叠加 -当您考虑到这一点时,只能根据 DOM 上的节点来管理它.
回答by Xotic750
Regarding the problems that you have described in your first example/requirement. As getElementById
only exists on the document
node, because it make use of caches that are only provided by a node tree being part of document
. You have three choices for searching a node tree that is not attached to the document
. All suffer 0(log n) as they are not taking advantage of the document
caches, there is little or no way around this (you tried with an iFrame
).
关于您在第一个示例/要求中描述的问题。由于getElementById
仅存在于document
节点上,因为它使用仅由作为document
. 您可以通过三种选择来搜索未附加到document
. 所有人都遭受 0(log n) 因为他们没有利用document
缓存,几乎没有办法解决这个问题(您尝试过使用iFrame
)。
One: A recursive node walker.
一:递归节点walker。
Advantage is that this is cross-browser friendly
优点是这是跨浏览器友好的
Disadvantage is that it will always be 0(log n) - if used on document
缺点是它将始终为 0(log n) - 如果用于 document
Javascript
Javascript
function getElementById(node, id) {
if (node.id === id) {
return node;
}
var target;
node = node.firstChild;
while (node) {
target = getElementById(node, id);
if (target) {
return target;
}
node = node.nextSibling;
}
return undefined;
}
function foo(html) {
var el = document.createElement("div");
el.innerHTML = html;
var target = getElementById(el, "target");
/* Do something with `target` */
if (target) {
console.log(target);
}
}
foo('<div id="nottarget1"><div id="nottarget2"><div id="nottarget3"><div id="nottarget4"><div id="target">Target</div></div></div></div></div>');
? On jsfiddle
? 在jsfiddle 上
Two: using querySelector
which is available per element.
二:使用querySelector
每个元素可用的。
Advantage is that it requires less code
优点是需要更少的代码
Disadvantage is that it requires IE8+ (and IE8 itself has limitations on the CSS query)
缺点是需要IE8+(而且IE8本身对CSS查询有限制)
Javascript
Javascript
function getElementById(node, id) {
return node.querySelector("#" + id);
}
function foo(html) {
var el = document.createElement("div");
el.innerHTML = html;
var target = getElementById(el, "target");
/* Do something with `target` */
if (target) {
console.log(target);
}
}
foo('<div id="nottarget1"><div id="nottarget2"><div id="nottarget3"><div id="nottarget4"><div id="target">Target</div></div></div></div></div>');
On jsfiddle
Three is to use TreeWalker
三是使用 TreeWalker
Disadvantages are that it requires IE9+, is less understood (often forgotten) than the previous methods, and requires more code than querySelector
缺点是需要IE9+,比前面的方法理解的少(经常忘记),需要的代码比前面的多 querySelector
Javascript
Javascript
function getElementById(node, id) {
return document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, {
acceptNode: function (n) {
if (n.id === id) {
return NodeFilter.FILTER_ACCEPT;
}
}
}, false).nextNode();
}
function foo(html) {
var el = document.createElement("div");
el.innerHTML = html;
var target = getElementById(el, "target");
/* Do something with `target` */
if (target) {
console.log(target);
}
}
foo('<div id="nottarget1"><div id="nottarget2"><div id="nottarget3"><div id="nottarget4"><div id="target">Target</div></div></div></div></div>');
On jsfiddle
Now, regarding performance of these methods, please see this jsperf
现在,关于这些方法的性能,请参阅此jsperf
Note: The perfomance of the three methods will alter dramatically if the nodes are part of the document
!
注意:如果节点是document
!
Regarding your second desire, what you have described is a mute point due to the nature of document
caches.
关于您的第二个愿望,由于document
缓存的性质,您所描述的是静音点。
Update:If being asynchronous is not a problem for your requirement, then you can do it with an iframe
like so.
更新:如果异步对于您的要求来说不是问题,那么您可以使用iframe
类似的方法来实现。
Advantage is that you can now use getElementById
优点是你现在可以使用 getElementById
Disadvantage is the huge overhead of creating and destroying the iframe
缺点是创建和销毁 iframe
Javascript
Javascript
var getElementById = (function () {
var parent = document.body || document.documentElement,
javascript = "javascript";
return function (node, id, func) {
var iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.src = javascript + ":";
iframe.onload = function () {
iframe.contentWindow.document.body.appendChild(node);
func(iframe.contentWindow.document.getElementById(id));
parent.removeChild(iframe);
};
parent.appendChild(iframe);
};
}());
function foo(html) {
var el = document.createElement("div");
el.innerHTML = html;
getElementById(el, "target", function (target) {
/* Do something with `target` */
if (target) {
console.log(target);
}
});
}
foo('<div id="nottarget1"><div id="nottarget2"><div id="nottarget3"><div id="nottarget4"><div id="target">Target</div></div></div></div></div>')
;
;
On jsfiddle
回答by Guffa
"It could be possible to select nodes which don't belong to the document."
“可以选择不属于文档的节点。”
Yes, but the method uses the fact that the id is unique in the document to optimise the performance. That's not possible if the element is not in a document.
是的,但是该方法利用文档中 id 唯一的事实来优化性能。如果元素不在文档中,这是不可能的。
"It could increase the performance, if the document has lots of elements and you know that the element you are searching is a descendant of an element that you already have in a variable"
“如果文档有很多元素,并且您知道要搜索的元素是变量中已有元素的后代,则它可以提高性能”
No, because it's already using a method that doesn't have to look through all the elements to find the id. Looping through elements would just make it slower.
不,因为它已经在使用一种无需查看所有元素即可找到 id 的方法。循环遍历元素只会让它变慢。