David Rodrigues
David Rodrigues

Reputation: 12532

Observe DOMNode property

I need a way to identify a property of an object (of type DOMNode) has undergone a change (or was created or removed, optionally).

I have an INPUT element and must be notified when the property .value is modified. The problem is that not there is an attr definition, which I could use MutationObserver, and yes, a property definition (input.value) and it not trigger the observer.

I can use the latest features available, since I will not use IE (bwhahahah).

Edit #1

I make this test to show:

  1. Manual trigger works, but I can't use/expect this;
  2. Automatic trigger don't works, and I like that;
  3. __defineSetter__ works very fine, except that I can't use it without stop the "default propagation".

If there is any way to allow the __defineSetter__ to continue until the end, would solve the case.

Upvotes: 1

Views: 199

Answers (1)

jfriend00
jfriend00

Reputation: 707228

You don't really say whether you're only trying to track user changes to the input element or also programmatic changes. For newish browsers, you can monitor the input event and it will tell you when the value of the input field has been changed by user control. There is no cross browser way to tell if the value of a field has been changed programmatically other than hooking all code that might change it to add your own notification system that a change may have been applied.

I wrote this cross browser function awhile ago to monitor all various forms of user change to an input field across all browsers. This code happens to be in the form of a jQuery method, but the logic could be modified to be plain javascript fairly easily. This code checks to see if the newish events are supported. If so, it uses them. If not, it hooks a lot of other events to try to trap all the possible ways the user could change the field (drag/drop, copy/paste, typing, etc...).

(function($) {

    var isIE = false;
    // conditional compilation which tells us if this is IE
    /*@cc_on
    isIE = true;
    @*/

    // Events to monitor if 'input' event is not supported
    // The boolean value is whether we have to 
    // re-check after the event with a setTimeout()
    var events = [
        "keyup", false,
        "blur", false,
        "focus", false,
        "drop", true,
        "change", false,
        "input", false,
        "textInput", false,
        "paste", true,
        "cut", true,
        "copy", true,
        "contextmenu", true
    ];
    // Test if the input event is supported
    // It's too buggy in IE so we never rely on it in IE
    if (!isIE) {
        var el = document.createElement("input");
        var gotInput = ("oninput" in el);
        if  (!gotInput) {
            el.setAttribute("oninput", 'return;');
            gotInput = typeof el["oninput"] == 'function';
        }
        el = null;
        // if 'input' event is supported, then use a smaller
        // set of events
        if (gotInput) {
            events = [
                "input", false,
                "textInput", false
            ];
        }
    }

    $.fn.userChange = function(fn, data) {
        function checkNotify(e, delay) {
            var self = this;
            var this$ = $(this);

            if (this.value !== this$.data("priorValue")) {
                this$.data("priorValue", this.value);
                fn.call(this, e, data);
            } else if (delay) {
                // The actual data change happens aftersome events
                // so we queue a check for after
                // We need a copy of e for setTimeout() because the real e
                // may be overwritten before the setTimeout() fires
                var eCopy = $.extend({}, e);
                setTimeout(function() {checkNotify.call(self, eCopy, false)}, 1);
            }
        }

        // hook up event handlers for each item in this jQuery object
        // and remember initial value
        this.each(function() {
            var this$ = $(this).data("priorValue", this.value);
            for (var i = 0; i < events.length; i+=2) {
                (function(i) {
                    this$.on(events[i], function(e) {
                        checkNotify.call(this, e, events[i+1]);
                    });
                })(i);
            }
        });
    }
})(jQuery);    

Upvotes: 1

Related Questions