Detect changes in the DOM

后端 未结 7 2338
温柔的废话
温柔的废话 2020-11-21 05:45

I want to execute a function when some div or input are added to the html. Is this possible?

For example, a text input is added, then the function should be called.<

相关标签:
7条回答
  • 2020-11-21 05:45

    The following example was adapted from Mozilla Hacks' blog post and is using MutationObserver.

    // Select the node that will be observed for mutations
    var targetNode = document.getElementById('some-id');
    
    // Options for the observer (which mutations to observe)
    var config = { attributes: true, childList: true };
    
    // Callback function to execute when mutations are observed
    var callback = function(mutationsList) {
        for(var mutation of mutationsList) {
            if (mutation.type == 'childList') {
                console.log('A child node has been added or removed.');
            }
            else if (mutation.type == 'attributes') {
                console.log('The ' + mutation.attributeName + ' attribute was modified.');
            }
        }
    };
    
    // Create an observer instance linked to the callback function
    var observer = new MutationObserver(callback);
    
    // Start observing the target node for configured mutations
    observer.observe(targetNode, config);
    
    // Later, you can stop observing
    observer.disconnect();
    

    Browser support: Chrome 18+, Firefox 14+, IE 11+, Safari 6+

    0 讨论(0)
  • 2020-11-21 05:52

    2015 update, new MutationObserver is supported by modern browsers:

    Chrome 18+, Firefox 14+, IE 11+, Safari 6+

    If you need to support older ones, you may try to fall back to other approaches like the ones mentioned in this 5 (!) year old answer below. There be dragons. Enjoy :)


    Someone else is changing the document? Because if you have full control over the changes you just need to create your own domChanged API - with a function or custom event - and trigger/call it everywhere you modify things.

    The DOM Level-2 has Mutation event types, but older version of IE don't support it. Note that the mutation events are deprecated in the DOM3 Events spec and have a performance penalty.

    You can try to emulate mutation event with onpropertychange in IE (and fall back to the brute-force approach if non of them is available).

    For a full domChange an interval could be an over-kill. Imagine that you need to store the current state of the whole document, and examine every element's every property to be the same.

    Maybe if you're only interested in the elements and their order (as you mentioned in your question), a getElementsByTagName("*") can work. This will fire automatically if you add an element, remove an element, replace elements or change the structure of the document.

    I wrote a proof of concept:

    (function (window) {
        var last = +new Date();
        var delay = 100; // default delay
    
        // Manage event queue
        var stack = [];
    
        function callback() {
            var now = +new Date();
            if (now - last > delay) {
                for (var i = 0; i < stack.length; i++) {
                    stack[i]();
                }
                last = now;
            }
        }
    
        // Public interface
        var onDomChange = function (fn, newdelay) {
            if (newdelay) delay = newdelay;
            stack.push(fn);
        };
    
        // Naive approach for compatibility
        function naive() {
    
            var last = document.getElementsByTagName('*');
            var lastlen = last.length;
            var timer = setTimeout(function check() {
    
                // get current state of the document
                var current = document.getElementsByTagName('*');
                var len = current.length;
    
                // if the length is different
                // it's fairly obvious
                if (len != lastlen) {
                    // just make sure the loop finishes early
                    last = [];
                }
    
                // go check every element in order
                for (var i = 0; i < len; i++) {
                    if (current[i] !== last[i]) {
                        callback();
                        last = current;
                        lastlen = len;
                        break;
                    }
                }
    
                // over, and over, and over again
                setTimeout(check, delay);
    
            }, delay);
        }
    
        //
        //  Check for mutation events support
        //
    
        var support = {};
    
        var el = document.documentElement;
        var remain = 3;
    
        // callback for the tests
        function decide() {
            if (support.DOMNodeInserted) {
                window.addEventListener("DOMContentLoaded", function () {
                    if (support.DOMSubtreeModified) { // for FF 3+, Chrome
                        el.addEventListener('DOMSubtreeModified', callback, false);
                    } else { // for FF 2, Safari, Opera 9.6+
                        el.addEventListener('DOMNodeInserted', callback, false);
                        el.addEventListener('DOMNodeRemoved', callback, false);
                    }
                }, false);
            } else if (document.onpropertychange) { // for IE 5.5+
                document.onpropertychange = callback;
            } else { // fallback
                naive();
            }
        }
    
        // checks a particular event
        function test(event) {
            el.addEventListener(event, function fn() {
                support[event] = true;
                el.removeEventListener(event, fn, false);
                if (--remain === 0) decide();
            }, false);
        }
    
        // attach test events
        if (window.addEventListener) {
            test('DOMSubtreeModified');
            test('DOMNodeInserted');
            test('DOMNodeRemoved');
        } else {
            decide();
        }
    
        // do the dummy test
        var dummy = document.createElement("div");
        el.appendChild(dummy);
        el.removeChild(dummy);
    
        // expose
        window.onDomChange = onDomChange;
    })(window);
    

    Usage:

    onDomChange(function(){ 
        alert("The Times They Are a-Changin'");
    });
    

    This works on IE 5.5+, FF 2+, Chrome, Safari 3+ and Opera 9.6+

    0 讨论(0)
  • 2020-11-21 05:53

    Use the MutationObserver interface as shown in Gabriele Romanato's blog

    Chrome 18+, Firefox 14+, IE 11+, Safari 6+

    // The node to be monitored
    var target = $( "#content" )[0];
    
    // Create an observer instance
    var observer = new MutationObserver(function( mutations ) {
      mutations.forEach(function( mutation ) {
        var newNodes = mutation.addedNodes; // DOM NodeList
        if( newNodes !== null ) { // If there are new nodes added
            var $nodes = $( newNodes ); // jQuery set
            $nodes.each(function() {
                var $node = $( this );
                if( $node.hasClass( "message" ) ) {
                    // do something
                }
            });
        }
      });    
    });
    
    // Configuration of the observer:
    var config = { 
        attributes: true, 
        childList: true, 
        characterData: true 
    };
    
    // Pass in the target node, as well as the observer options
    observer.observe(target, config);
    
    // Later, you can stop observing
    observer.disconnect();
    
    0 讨论(0)
  • 2020-11-21 06:03

    Ultimate approach so far, with smallest code:

    (IE9+, FF, Webkit)

    Using MutationObserver and falling back to the deprecated Mutation events if needed:
    (Example below if only for DOM changes concerning nodes appended or removed)

    var observeDOM = (function(){
      var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    
      return function( obj, callback ){
        if( !obj || obj.nodeType !== 1 ) return; 
    
        if( MutationObserver ){
          // define a new observer
          var mutationObserver = new MutationObserver(callback)
    
          // have the observer observe foo for changes in children
          mutationObserver.observe( obj, { childList:true, subtree:true })
          return mutationObserver
        }
        
        // browser support fallback
        else if( window.addEventListener ){
          obj.addEventListener('DOMNodeInserted', callback, false)
          obj.addEventListener('DOMNodeRemoved', callback, false)
        }
      }
    })()
    
    
    //------------< DEMO BELOW >----------------
    
    // add item
    var itemHTML = "<li><button>list item (click to delete)</button></li>",
        listElm = document.querySelector('ol');
    
    document.querySelector('body > button').onclick = function(e){
      listElm.insertAdjacentHTML("beforeend", itemHTML);
    }
    
    // delete item
    listElm.onclick = function(e){
      if( e.target.nodeName == "BUTTON" )
        e.target.parentNode.parentNode.removeChild(e.target.parentNode);
    }
        
    // Observe a specific DOM element:
    observeDOM( listElm, function(m){ 
       var addedNodes = [], removedNodes = [];
    
       m.forEach(record => record.addedNodes.length & addedNodes.push(...record.addedNodes))
       
       m.forEach(record => record.removedNodes.length & removedNodes.push(...record.removedNodes))
    
      console.clear();
      console.log('Added:', addedNodes, 'Removed:', removedNodes);
    });
    
    
    // Insert 3 DOM nodes at once after 3 seconds
    setTimeout(function(){
       listElm.removeChild(listElm.lastElementChild);
       listElm.insertAdjacentHTML("beforeend", Array(4).join(itemHTML));
    }, 3000);
    <button>Add Item</button>
    <ol>
      <li><button>list item (click to delete)</button></li>
      <li><button>list item (click to delete)</button></li>
      <li><button>list item (click to delete)</button></li>
      <li><button>list item (click to delete)</button></li>
      <li><em>&hellip;More will be added after 3 seconds&hellip;</em></li>
    </ol>

    0 讨论(0)
  • 2020-11-21 06:03

    or you can simply Create your own event, that run everywhere

     $("body").on("domChanged", function () {
                    //dom is changed 
                });
    
    
     $(".button").click(function () {
    
              //do some change
              $("button").append("<span>i am the new change</span>");
    
              //fire event
              $("body").trigger("domChanged");
    
            });
    

    Full example http://jsfiddle.net/hbmaam/Mq7NX/

    0 讨论(0)
  • 2020-11-21 06:06

    How about extending a jquery for this?

       (function () {
            var ev = new $.Event('remove'),
                orig = $.fn.remove;
            var evap = new $.Event('append'),
               origap = $.fn.append;
            $.fn.remove = function () {
                $(this).trigger(ev);
                return orig.apply(this, arguments);
            }
            $.fn.append = function () {
                $(this).trigger(evap);
                return origap.apply(this, arguments);
            }
        })();
        $(document).on('append', function (e) { /*write your logic here*/ });
        $(document).on('remove', function (e) { /*write your logic here*/ ) });
    

    Jquery 1.9+ has built support for this(I have heard not tested).

    0 讨论(0)
提交回复
热议问题