检测 DOM 中的变化

发布于 2024-09-08 18:39:55 字数 74 浏览 7 评论 0 原文

我想在 html 中添加一些 div 或输入时执行一个函数。 这可能吗?

例如,添加一个文本输入,然后应该调用该函数。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

赠佳期 2024-09-15 18:39:56

为此扩展一个 jquery 怎么样?

   (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+ 已经建立了对此的支持(我听说没有测试过)。

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).

枯叶蝶 2024-09-15 18:39:56

发现这个问题,因此在 2022 年更新了解决方案。

我们看到了不同的解决方案,其中主要涉及 MutationObserver

如果有人想记录 DOM 更改并将其存储在一段时间后重播,可以使用 rrweb

编辑:

添加示例,以下是提示:

rrweb 您可以通过 CDNnpm

让我们以 CDN 用于记录 DOM 更改事件:

步骤 1:只需在 中包含以下脚本标记; 标签

步骤 2:并添加在您的代码中添加以下代码以捕获 rrweb 生成的事件。

<script>
var events = [];
rrweb.record({
    emit(event) {
       events.push(event);
       // you can store this event anywhere and you can replay them later. ex: some JSON file, or DB
    }
});

</script>

此示例主要用于记录任何 Web 应用程序的事件。

要详细了解和理解(如何录制/重放),请阅读 rrweb 文档

重播示例:

这是为了调试,但在此处添加以便任何人也可以检查重播端:

重播器示例

Found the question so updating with a solution in 2022.

We have seen different solutions which mostly involve MutationObserver.

If anyone wants to record the DOM changes and store them to replay after some time, they can use rrweb

Edit:

Adding example, here are the hints:

rrweb you can use via CDN or npm

Let's take the example of CDN for recording the DOM changes events:

Step 1: just include following script tag in the <HTML><head> tag

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/rrweb-all.js" crossorigin="anonymous"></script>

Step 2: and add the below code in your code to capture the events generated by rrweb.

<script>
var events = [];
rrweb.record({
    emit(event) {
       events.push(event);
       // you can store this event anywhere and you can replay them later. ex: some JSON file, or DB
    }
});

</script>

This example is mostly for recording events for any web application.

To know and understand in the detail (how to record/replay), please read from rrweb documentation.

Replayer Example:

This was for debugging, however adding here so that anyone can check replayed side as well:

Replayer Example

孤蝉 2024-09-15 18:39:56

2024 MutationObserver 使用 MDN 最佳实践更新:

我找到了示例在 https://developer.mozilla.org/en-US/docs /Web/API/MutationObserver 比任何现有答案更有帮助和简洁。

// Select the node that will be observed for mutations
const targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
const config = { attributes: true, childList: true, subtree: true };

// Callback function to execute when mutations are observed
const callback = (mutationList, observer) => {
  for (const mutation of mutationList) {
    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
const observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();

2024 MutationObserver Update with MDN Best Practices:

I found the example at https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver was more helpful to be more helpful and concise than any of the existing answers.

// Select the node that will be observed for mutations
const targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
const config = { attributes: true, childList: true, subtree: true };

// Callback function to execute when mutations are observed
const callback = (mutationList, observer) => {
  for (const mutation of mutationList) {
    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
const observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();
岛徒 2024-09-15 18:39:56

使用 TrackChanges 检测 html 更改。
链接: https://www.npmjs.com/package/track-changes-js< /a>

示例

 let button = document.querySelector('.button');

 trackChanges.addObserver('buttonObserver', () => button);
 
 trackChanges.addHandler('buttonObserver', buttonHandler);

 function buttonHandler(button) {
   console.log(`Button created: ${button}`);
 }

Use TrackChanges for detect html changes.
Link: https://www.npmjs.com/package/track-changes-js

Example

 let button = document.querySelector('.button');

 trackChanges.addObserver('buttonObserver', () => button);
 
 trackChanges.addHandler('buttonObserver', buttonHandler);

 function buttonHandler(button) {
   console.log(`Button created: ${button}`);
 }
梦明 2024-09-15 18:39:55

迄今为止的终极方法,代码最少:

(IE11+、FF、Webkit)

使用 MutationObserver 并回退到已弃用的版本
突变事件(如果需要):
(下面的示例,如果仅针对与附加或删除的节点有关的 DOM 更改)

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 for changes in children
      mutationObserver.observe(obj, {childList: true, subtree: true});
      return mutationObserver;
    } else if (window.addEventListener) { // browser support fallback
      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>',
    listEl = document.querySelector('ol');

document.querySelector('body > button').onclick = function(e) {
  listEl.insertAdjacentHTML('beforeend', itemHTML);
};

// delete item
listEl.onclick = function(e) {
  if (e.target.nodeName == 'BUTTON') {
    e.target.parentNode.parentNode.removeChild(e.target.parentNode);
  }
}
    
// Observe a specific DOM element:
observeDOM(listEl, 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(){
   listEl.removeChild(listEl.lastElementChild);
   listEl.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>…More will be added after 3 seconds…</em></li>
</ol>

Ultimate approach so far, with smallest code:

(IE11+, 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 for changes in children
      mutationObserver.observe(obj, {childList: true, subtree: true});
      return mutationObserver;
    } else if (window.addEventListener) { // browser support fallback
      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>',
    listEl = document.querySelector('ol');

document.querySelector('body > button').onclick = function(e) {
  listEl.insertAdjacentHTML('beforeend', itemHTML);
};

// delete item
listEl.onclick = function(e) {
  if (e.target.nodeName == 'BUTTON') {
    e.target.parentNode.parentNode.removeChild(e.target.parentNode);
  }
}
    
// Observe a specific DOM element:
observeDOM(listEl, 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(){
   listEl.removeChild(listEl.lastElementChild);
   listEl.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>…More will be added after 3 seconds…</em></li>
</ol>

塔塔猫 2024-09-15 18:39:55

2015 更新,新的 MutationObserver 是现代浏览器支持:

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

如果您需要支持较旧的浏览器,您可以尝试回退到其他方法,例如本文中提到的方法下面是 5 (!) 岁的答案。那里有龙。享受吧:)


其他人正在更改文档吗?因为如果您可以完全控制更改,则只需使用函数或自定义事件创建自己的 domChanged API - 并在修改内容的任何地方触发/调用它。

DOM Level-2 有 突变事件类型,但老版本的IE不支持。请注意,突变事件在 DOM3 事件规范中已弃用 并有性能损失

您可以尝试在 IE 中使用 onpropertychange 来模拟突变事件(如果没有可用的方法,则退回到暴力方法)。

对于完整 domChange 来说,间隔可能有点过分了。想象一下,您需要存储整个文档的当前状态,并检查每个元素的每个属性是否相同。

也许如果您只对元素及其顺序感兴趣(正如您在问题中提到的),则 getElementsByTagName("*") 可以工作。如果您添加元素、删除元素、替换元素或更改文档结构,这将自动触发。

我写了一个概念证明:

(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);

用法:

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

这适用于 IE 5.5+、FF 2+、Chrome、Safari 3+ 和 Opera 9.6+

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+

故人爱我别走 2024-09-15 18:39:55

以下示例改编自 Mozilla Hacks 的 博客文章并且正在使用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();

浏览器支持:Chrome 18+、Firefox 14+、IE 11+、Safari 6+

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+

失而复得 2024-09-15 18:39:55
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
    // fired when a mutation occurs
    console.log(mutations, observer);
    // ...
});

// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
  subtree: true,
  attributes: true
  //...
});

完整说明:https://stackoverflow.com/a/11546242/6569224

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
    // fired when a mutation occurs
    console.log(mutations, observer);
    // ...
});

// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
  subtree: true,
  attributes: true
  //...
});

Complete explanations: https://stackoverflow.com/a/11546242/6569224

不回头走下去 2024-09-15 18:39:55

我最近编写了一个插件,它正是这样做的 - jquery.initialize
(NPM 链接 -不同的 NPM/GitHub 用户名,同一作者)

您使用它的方式与 .each 函数相同。

$(".some-element").initialize( function(){
    $(this).css("color", "blue"); 
});

.each 的区别是 - 它需要您的选择器,在本例中 < code>.some-element 并等待将来使用此选择器添加新元素,如果添加了此类元素,它也会被初始化。

在我们的例子中,初始化函数只需将元素颜色更改为蓝色。因此,如果我们添加新元素(无论是使用 ajax 还是 F12 检查器或任何其他元素),例如:

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

插件将立即初始化它。插件还确保一个元素仅初始化一次。因此,如果您添加元素,然后 .detach() 将其从 body 中删除,然后再次添加它,它将不会再次初始化。

$("<div/>").addClass('some-element').appendTo("body").detach()
    .appendTo(".some-container");
//initialized only once

插件基于 MutationObserver - 它将在 IE9 和 10 上运行,其依赖项详见

I have recently written a plugin that does exactly that - jquery.initialize.
(NPM link - different NPM/GitHub user name, same author)

You use it the same way as .each function

$(".some-element").initialize( function(){
    $(this).css("color", "blue"); 
});

The difference from .each is - it takes your selector, in this case .some-element and wait for new elements with this selector in the future, if such element will be added, it will be initialized too.

In our case initialize function just change element color to blue. So if we'll add new element (no matter if with ajax or even F12 inspector or anything) like:

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

Plugin will init it instantly. Also plugin makes sure one element is initialized only once. So if you add element, then .detach() it from body and then add it again, it will not be initialized again.

$("<div/>").addClass('some-element').appendTo("body").detach()
    .appendTo(".some-container");
//initialized only once

Plugin is based on MutationObserver - it will work on IE9 and 10 with dependencies as detailed on the readme page.

后eg是否自 2024-09-15 18:39:55

或者您可以简单地创建自己的活动,该活动随处可见

 $("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");

        });

完整示例
http://jsfiddle.net/hbmaam/Mq7NX/

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/

云淡风轻 2024-09-15 18:39:55

使用 MutationObserver 界面,如 Gabriele Romanato 的 博客

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();

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();
审判长 2024-09-15 18:39:55

8 年后,这是我使用 MutationObserverRxJS 的解决方案。与

observeDOMChange(document.querySelector('#dom-changes-here'))
  .subscribe(val => log('DOM-change detected'));

其他方法的主要区别是在 DOM 更改时触发 CustomEvent,并且通过以下功能监听去抖事件以有效地执行用户逻辑;

  • 反跳连续 DOM 更改以防止执行过多
  • 在给定时间后停止监视
  • 停止监视 DOM 更改后删除事件侦听器/订阅者
  • 用于监视框架中发生的 DOM 更改,例如 Angular
import { fromEvent, timer} from 'rxjs';
import { debounceTime, takeUntil, tap } from 'rxjs/operators';

function observeDOMChange(el, options={}) {
  options = Object.assign({debounce: 100, expires: 2000}, options);

  const observer = new MutationObserver(list =>  {
    el.dispatchEvent(new CustomEvent('dom-change', {detail: list}));
  });
  observer.observe(el, {attributes: false, childList: true, subtree: true });

  let pipeFn;
  if (options.expires) {
    setTimeout(_ => observer.disconnect(), options.expires);
    pipeFn = takeUntil(timer(options.expires));
  } else {
    pipeFn = tap(_ => _); 
  }

  return fromEvent(el, 'dom-change')
    .pipe(pipeFn, debounceTime(options.debounce));
}

stackblitz 上的演示。
输入图像描述这里

8 years later, here is my solution using MutationObserver and RxJS

observeDOMChange(document.querySelector('#dom-changes-here'))
  .subscribe(val => log('DOM-change detected'));

The main difference from the other approaches is to fire a CustomEvent when DOM changes, and listen to the event debounced to execute user logic efficiently with the following features;

  • Debounce consecutive DOM changes to prevent too many executions
  • Stop watching after the given time
  • Removes event listeners/subscribers after stop watching DOM changes
  • Useful to watch DOM change happened in a framework, e.g., Angular
import { fromEvent, timer} from 'rxjs';
import { debounceTime, takeUntil, tap } from 'rxjs/operators';

function observeDOMChange(el, options={}) {
  options = Object.assign({debounce: 100, expires: 2000}, options);

  const observer = new MutationObserver(list =>  {
    el.dispatchEvent(new CustomEvent('dom-change', {detail: list}));
  });
  observer.observe(el, {attributes: false, childList: true, subtree: true });

  let pipeFn;
  if (options.expires) {
    setTimeout(_ => observer.disconnect(), options.expires);
    pipeFn = takeUntil(timer(options.expires));
  } else {
    pipeFn = tap(_ => _); 
  }

  return fromEvent(el, 'dom-change')
    .pipe(pipeFn, debounceTime(options.debounce));
}

Demo at stackblitz.
enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文