即使不建议这样做,JavaScript仍允许您按元素的ID访问元素,而无需使用getElementById。
<iframe id="myframe" />
<script>
var i = window.myframe;
</script>
这为扩展程序带来了问题,这些扩展程序在访问元素本身时需要访问它们。执行此操作的方法是在原型上为getElementById调用Object.defineProperty(),以便在调用该元素时可以访问该元素。但是,当直接引用元素时,因此不会调用getElementById,它们是扩展的盲点。我尝试了以下方法,但是没有任何运气。
有人知道这样引用元素时JavaScript使用的内部机制吗?我很好奇是否在内部调用了另一个函数,它可能像getElementById一样被钩住。还是有一种方法可以确保在DOM加载之后但在页面脚本运行之前运行内容脚本代码?
或者是否有一种方法可以确保在加载DOM之后但在页面脚本运行之前运行内容脚本代码?
问题是页面脚本可以运行之前,DOM已完全加载。例如,下面:
<div id="foo"></div>
<script>
foo.textContent = 'foo';
</script>
<div id="bar"></div>
脚本将运行之前创建bar
元素(在加载DOM之前)。
可以使用[id]
查找DOM中当前具有ID的所有元素,并且可以通过简单地重新分配属性来在窗口上重新分配这些属性-或者,如果要在检索元素时运行代码,则可以可以使用Object.defineProperty
将其转换为吸气剂。您需要能够在脚本加载之前找到ID并重新分配ID,这可以通过子树MutationObserver完成-在插入<script>
之前,在DOM中的[id]
上进行迭代,然后重新分配它们:] >
console.log('(empty JS block inserted by Stack Snippet editor)');
<script> new MutationObserver((mutations) => { // Check to see if a script was inserted if (mutations.every( mutation => [...mutation.addedNodes].every( addedNode => addedNode.tagName !== 'SCRIPT' ) )) { // No scripts inserted return; } console.log('id reassignment running'); for (const elm of document.querySelectorAll('[id]')) { Object.defineProperty(window, elm.id, { get() { console.log('Custom code running'); return elm; }, configurable: true }); } }) .observe(document.body, { childList: true, subtree: true }); </script> <div id="foo"></div> <script> console.log('Lower script about to reference foo'); foo.textContent = 'foo'; console.log('Lower script has finished referencing foo'); </script> <div id="bar"></div>
它可以从抛光中受益,但这是一般的想法。