【问题标题】:Hooking document.createElement using function prototype使用函数原型挂钩 document.createElement
【发布时间】:2012-07-31 03:10:46
【问题描述】:

我想以这样一种方式挂钩到 document.createElement 函数,即每次我创建一个 div 元素时,我的挂钩都会将一个“foo”属性附加到该 div。这是我目前拥有的:

<script>
    window.onload = function () {
        console.log("document loaded");
        document.prototype.createElement = function (input) {
            var div = document.createElement(input);
            console.log("createElement hook attached!");
            if (input == "div")div.foo = "bar";
            return div;
        }

        document.body.addEventListener('onready', function () {
            var div = document.createElement("div");
            console.log(div.foo);
        });

    }
</script>

当我在 Chrome 中运行时,我收到一条错误消息

Uncaught TypeError: Cannot set property 'createElement' of undefined test.html:4 window.onload

(我更改了上面错误消息中的行号以匹配我的代码)

我在这里做错了什么?我该如何解决这个问题?

【问题讨论】:

  • 扩展documentprototype!哇,祝你好运……
  • 在浏览器中处理 DOM 对象是一件非常痛苦的事情。 Prototype JS 库没有成功的原因之一。您可能希望围绕您希望扩展的任何对象创建一个包装器对象。 perfectionkills.com/whats-wrong-with-extending-the-dom
  • 不要手动构建 dom,尤其是在没有库的情况下。除非这是一个个人学习项目,否则请使用模板引擎和库来进行 DOM 操作,或者您正在乞求维护和可移植性的噩梦。拦截 createElement 绝不应该是必要的,而且将来可能并不总是这样。

标签: javascript


【解决方案1】:
  • document 没有 .prototype,因为它是实例对象而不是构造函数
  • 您在新函数中调用了新的document.createElement,它会以递归方式结束。您需要在某处存储对旧引用的引用,然后调用它。
  • 您正在设置属性而不是属性
  • 这是非常脆弱的事情,不能保证有效。它似乎在 chrome 和 firefox 中工作,但在旧 IE 中无法工作

试试这个

document.createElement = function(create) {
    return function() {
        var ret = create.apply(this, arguments);
        if (ret.tagName.toLowerCase() === "div") {
            ret.setAttribute("foo", "bar");
        }
        return ret;
    };
}(document.createElement)

http://jsfiddle.net/NgxaK/2/

【讨论】:

  • 这不会修改页面上已有的 div。此外,apply 是不必要的,并且比直接调用原始的 createElement 慢得多。
  • 我不认为他期望它会修改已经存在的 div。至于.apply,我只是保证将参数准确地传递给document.createElement,就像它们传递给新函数一样。我怀疑它比.call慢得多。
  • call 也很慢,比直接调用慢 10 倍左右。 apply 通常甚至慢 3-5 倍。没有快速的方法来包装一个其调用范围受保护的全局,这就是我不建议这样做的原因。
  • 在大多数情况下可能是不必要的,但 HTMLDocument.prototype.createElement 可用于覆盖早在 IE8 中。但是,当前浏览器应该覆盖Document(注意大写的“D”)。
  • @JustinSummerlin 如果使用直接调用,会不会在执行过程中错过this
【解决方案2】:

我建议不要覆盖现有函数,因为它们将来可能会变为只读。我建议对 DOM 进行后处理(快速遍历 div 比拦截每个元素的创建要快)和/或修改插入 div 的代码以添加您的属性。或者,如果你真的想修改创建的节点,更好的方法是 Mutation Observers (HTML5):

http://updates.html5rocks.com/2012/02/Detect-DOM-changes-with-Mutation-Observers

与使用 HTML4 中已弃用的突变事件相比,这是一个更好的选择,并且覆盖全局变量通常被认为是一种不好的做法,除非您正在创建 shim 或 polyfill。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    相关资源
    最近更新 更多