开发者

Strange behavior with Javascript's __defineSetter__

I have a large project in which I need to intercept assignments to things like element.src, element.href, element.style, etc. I figured out to do this with defineSetter, but it is behaving very strangely (using Chrome 8.0.552.231)

An example:

var attribs = ["href", "src", "background", "action", "onblur", "style", "onchange", "onclick", "ondblclick", "onerror", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onmousedown", "onmousemove", "onmouseover", "onmouseup开发者_Go百科", "onresize", "onselect", "onunload"];
for(a = 0; a < attribs.length; a++) {
  var attrib_name = attribs[a];
  var func = new Function("attrib_value", "this.setAttribute(\"" + attrib_name + "\", attrib_value.toUpperCase());");
  HTMLElement.prototype.__defineSetter__(attrib_name, func);
}

What this code should do is whenever common element attribute in attribs is assigned, it uses setAttribute() to set a uppercased version of that attribute.

For some very strange reason, the setter works for only ~1/3 of the assignments.

For example with

element.src = "test"

the new src is "TEST", like it should be

however with

element.href = "test"

the new href is "test", not uppercase

then even when I try element.__lookupSetter__("href"), it returns the proper, uppercasing setter

the strangest thing is different variables are intercepted properly between Chrome and Firefox

help!!


This is not a good idea. Host objects (such as DOM elements) are not subject to the usual rules that apply to native JavaScript objects and can essentially do what they like, and all browsers to a greater or lesser extent take advantage of this fact. Browsers are not obliged to provide a prototype for host objects, and neither are they obliged to allow you to override getters and setters for host object properties, or respect any attempts to override default behaviour.

I'd strongly recommend abandoning this approach and take a different approach instead, such as writing wrapper objects for DOM elements.

UPDATE: Example of wrapper approach

You can create wrapper objects for DOM elements and do all DOM manipulation through these wrappers. This is something many libraries (such as YUI) do. For example:

function ElementWrapper(el) {
    this.domElement = el;
}

ElementWrapper.prototype = {
    // Wrap the DOM methods you need
    appendChild: function(child) {
        this.domElement.appendChild(child);
    },

    // Add custom behaviour, such as converting certain
    // property values to upper case
    setProperty: function(name, value) {
        if (/^(src|href|action)$/i.test(name)) {
            this.domElement[name] = value.toUpperCase();
        } else {
            this.domElement[name] = value;
        }
    }
};

var wrappedEl = new WrappedElement( document.getElementById("foo") );
wrappedEl.setProperty("href", "http://www.google.com/");


Echoing Tim Down and adding my own two cents, you will have better luck, in a more cross-browser, standards-compliant manner, if you wait for WebIDL to fully define ECMAScript bindings for the DOM. Overriding at that point should give you precise, well-specified behavior. But right now you're going to get oddness.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜