Search code examples
javascriptjqueryeventsbind

Fire event once on DOM change


I'm using the following jQuery:

$('table').bind("DOMSubtreeModified",function(){
   myfunction();
});

The contents of the table are modified by another function. However, this triggers the above for every element in the table that is changed.

Is there a way to fire only once after all table-changes have been made?


Solution

  • Update (Sep. 26, 2024)

    The following comment by TJ. Crowder is correct which makes Answer B and Demo B invalid.

    "Don't use DOM mutation events at all, they're deprecated and actively being removed from browsers. Use mutation observers and watch the table for subtree modifications."

    Please refer to Answer A and Demo A

    Answer A (valid)

    Use Mutation Observer to detect any changes to a DOM node. In Demo A click the button to add a <td> to the <table>, which will be observed by the observer as a mutation (change) to the <table>. .disconnect() method is then invoked so that the observer is ran only once. Note any further modifications to table do not trigger anything.

    Demo A (valid)

    $('button').click(function() {
      $('table tr').append('<td>TEST</td>');
    });
    
    /**
     * The following is a modified version from this post:
     * https://stackoverflow.com/a/25083738/2813224
     */
     
    // Dereferenced jQuery object of <table>
    const table = $("table")[0];
    /**
     * Options for the observer.
     * This particular setup directs the observer to observe any
     * additions or removals of any DOM node.
     */
    const config = {
      childList: true, 
      subtree: true 
    };
    
    // Create an observer instance
    const observer = new MutationObserver(function(changes) {
      // If there is a node added...
      if (changes[0].addedNodes) {
        // log message to the console...
        console.log("Table has been modified");
        // then stop the observer from observing
        this.disconnect();
      }
    });
    
    // Start the observer to observe mutations (changes) to <table>
    observer.observe(table, config);
    table,
    td {
      border: 1px solid black;
    }
    
    table {
      width: 300px;
      height: 50px;
    }
    <table>
      <tr></tr>
    </table>
    <button>addTD</button>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>

    Answer B (invalid)

    Use .one() to register once for an event. In this Snippet click the button to add a <td> to table, thereby triggering the DOMSubtreeModified event only once. Note any further modifications to table do not trigger anything.

    Demo B (invalid)

    $('button').click(function() {
      $('table tr').append('<td>TEST</td>');
    });
    
    $('table').one("DOMSubtreeModified", function() {
      alert('MODIFIED');
    });
    table,
    td {
      border: 1px solid black;
    }
    
    table {
      width: 300px;
      height: 50px;
    }
    <table>
      <tr>
        <td></td>
      </tr>
    </table>
    <button>addTD</button>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>