Search code examples
javascriptpolyfills

Is it possible to polyfill "oninput" event?


I am trying to polyfill "oninput" by this way:

var $el = document.getElementById("ctx");

 if( "oninput" in $el ) {
 // native oninput 
 $el.addEventListener("input", function(e) {
  // skip nodeType === 3
  this.nextSibling.nextSibling.textContent = this.value;
   }, false);
 } else {
// trying to polyfill   
$el.addEventListener("keyup", function(e) {
 this.nextSibling.nextSibling.textContent = this.value;
  }, false);
};

However, there is one problem - it works only after key is up; Is it possible to fix?

Demo: http://jsfiddle.net/zhak55/m9up2d78/


Solution

  • I wrote this plain javascript function awhile ago to monitor for any changes to an input field, even in older browsers that don't support the input event and it attempts to cover all the ways that a change can occur including copy/paste, key typing, drag/drop, etc...

    // create self-executing function wrapper so we have a private scope for
    // things that we just want to define/execute once
    (function() {
        var isIE = false;
        // conditional compilation which tells us if this is old IE
        // which is needed because old IE has a buggy `oninput` 
        // implementation that must be worked around (so we can't use feature detection)
        /*@cc_on
          isIE = true;
          @*/
    
        // Which events to monitor
        // the boolean value is whether we have to 
        // re-check after the event with a setTimeout()
        var events = [
           "keyup", false,
           "blur", true,
           "focus", true,
           "drop", true,
           "change", false,
           "input", false,
           "paste", true,
           "cut", true,
           "copy", 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
                ];
            }
        }
    
        // add event cross browser
        function addEvent(elem, event, fn) {
            if (elem.addEventListener) {
                elem.addEventListener(event, fn, false);
            } else {
                elem.attachEvent("on" + event, function() {
                    // set the this pointer same as addEventListener when fn is called
                    return(fn.call(elem, window.event));   
                });
            }
        }
    
        // simplified shallow copy of an object
        function copyObj(o) {
            var x = {};
            for (var i in o) {
                x[i] = o[i];
            }
            return(x);
        }
    
        // define global function
        window.onChange = function(elem, fn, data) {
            var priorValue = elem.value;
    
            function checkNotify(e, delay) {
                if (elem.value !== priorValue) {
                    priorValue = elem.value;
                    fn.call(elem, e, data);
                } else if (delay) {
                    var eCopy = copyObj(e);
                    // the actual data change happens aftersome events
                    // so we queue a check for after
                    setTimeout(function() {checkNotify(eCopy, false)}, 1);
                }
            }
    
            for (var i = 0; i < events.length; i+=2) {
                (function(i) {
                    addEvent(elem, events[i], function(e) {
                        checkNotify(e, events[i+1]);
                    });
                })(i);
            }
        }
    })();    
    
    // sample usage
    onChange(elem, function(e) {
        // The this ptr is set to the element that had the change
    });