是否有JavaScript / jQuery DOM更改侦听器?

时间:2010-05-16 16:43:11

标签: javascript jquery google-chrome-extension

基本上我希望在DIV的内容发生变化时执行脚本。由于脚本是独立的(Chrome扩展程序和网页脚本中的内容脚本),我只需要观察DOM状态的变化。我可以设置投票,但这似乎很草率。

5 个答案:

答案 0 :(得分:440)

几年后,现在正式有一个更好的解决方案。 DOM4 Mutation Observers是已弃用的DOM3突变事件的替代品。它们currently implemented in modern browsersMutationObserver(或旧版Chrome中以供应商为前缀WebKitMutationObserver):

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
  //...
});

此示例侦听document及其整个子树上的DOM更改,它将触发元素属性更改以及结构更改。规范草案包含有效mutation listener properties的完整列表:

  

<强>的childList

     
      
  • 如果要观察目标儿童的突变,请设置为true
  •   
     

<强>属性

     
      
  • 如果要观察到目标属性的突变,则设置为true
  •   
     

<强> characterData

     
      
  • 如果要观察目标数据的突变,则设置为true
  •   
     

<强>子树

     
      
  • 如果要发现不仅仅是目标的突变,还要观察目标的后代,设置为true
  •   
     

<强> attributeOldValue

     
      
  • 如果true设置为true,则设置为attributes,并且在需要记录突变之前设置目标的属性值。
  •   
     

<强> characterDataOldValue

     
      
  • 如果true设置为true,则设置为characterData,并且在需要记录突变之前设置目标数据。
  •   
     

<强> attributeFilter

     
      
  • 如果不需要观察所有属性突变,则设置为属性本地名称列表(不带名称空间)。
  •   

(此列表截至2014年4月是最新的;您可以检查规范是否有任何更改。)

答案 1 :(得分:202)

修改

此答案现已弃用。请参阅apsillers的答案。

由于这是针对Chrome扩展程序的,因此您也可以使用标准DOM事件 - DOMSubtreeModified。查看跨浏览器对此event的支持。自1.0以来,它一直受Chrome支持。

$("#someDiv").bind("DOMSubtreeModified", function() {
    alert("tree changed");
});

查看工作示例here

答案 2 :(得分:39)

许多网站使用AJAX动态添加/显示/更改内容。有时它会被用来代替现场导航,所以当前的URL会以编程方式更改,并且在这种情况下浏览器不会自动执行内容脚本,因为页面不是完全从远程服务器获取的。 / p>

检测content script中可用的页面更改的常用JS方法。

  • MutationObserver docs)从字面上检测DOM更改:

  • 事件监听器表示通过发送DOM事件来发送内容更改的网站:

  • 通过setInterval定期检查DOM
    显然,这只适用于等待由其id /选择器标识的特定元素出现的情况,并且除非您发明某种指纹现有内容,否则它不会让您普遍检测新动态添加的内容。 / p>

  • History API内隐藏injected DOM script

    document.head.appendChild(document.createElement('script')).text = '(' +
        function() {
            // injected DOM script is not a content script anymore, 
            // it can modify objects and functions of the page
            var _pushState = history.pushState;
            history.pushState = function(state, title, url) {
                _pushState.call(this, state, title, url);
                window.dispatchEvent(new CustomEvent('state-changed', {detail: state}));
            };
            // repeat the above for replaceState too
        } + ')(); this.remove();'; // remove the DOM script element
    
    // And here content script listens to our DOM script custom events
    window.addEventListener('state-changed', function(e) {
        console.log('History state changed', e.detail, location.hash);
        doSomething();
    });
    
  • 聆听hashchangepopstate事件:

    window.addEventListener('hashchange', function(e) {
        console.log('URL hash changed', e);
        doSomething();
    });
    window.addEventListener('popstate', function(e) {
        console.log('State changed', e);
        doSomething();
    });
    


特定于扩展程序:检测background / event page中的网址更改。

有一些高级API可用于导航:webNavigationwebRequest,但我们会将chrome.tabs.onUpdated事件监听器sends a message用于内容脚本:

  • 的manifest.json:
    声明background/event page
    声明content script
    添加"tabs" permission

  • background.js

    var rxLookfor = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/;
    chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
        if (rxLookfor.test(changeInfo.url)) {
            chrome.tabs.sendMessage(tabId, 'url-update');
        }
    });
    
  • content.js

    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
        if (msg === 'url-update') {
            doSomething();
        }
    });
    

答案 3 :(得分:27)

另一种方法取决于你如何改变div。 如果您使用JQuery通过其html()方法更改div的内容,则可以扩展该方法并在每次将html放入div时调用注册函数。

(function( $, oldHtmlMethod ){
    // Override the core html method in the jQuery object.
    $.fn.html = function(){
        // Execute the original HTML method using the
        // augmented arguments collection.

        var results = oldHtmlMethod.apply( this, arguments );
        com.invisibility.elements.findAndRegisterElements(this);
        return results;

    };
})( jQuery, jQuery.fn.html );

我们只是拦截对html()的调用,用这个调用一个注册函数,它在上下文中指的是获取新内容的目标元素,然后我们将调用传递给原始的jquery.html()函数。请记住返回原始html()方法的结果,因为JQuery期望它用于方法链接。

有关方法覆盖和扩展的更多信息,请查看http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm,这是我关闭闭包功能的地方。另请查看JQuery网站上的插件教程。

答案 4 :(得分:6)

MutationObserver API提供的“原始”工具外,还有“便利”库来处理DOM突变。

考虑:MutationObserver以子树的形式表示每个DOM的变化。因此,如果您正在等待插入某个元素,那么它可能位于mutations.mutation[i].addedNodes[j]的子项内部。

另一个问题是当您自己的代码因突变而改变DOM时 - 您经常想要将其过滤掉。

解决此类问题的一个好的便利库是mutation-summary(免责声明:我不是作者,只是一个满意的用户),它使您能够指定您感兴趣的内容的查询,并准确获取这一点。

文档中的基本用法示例:

var observer = new MutationSummary({
  callback: updateWidgets,
  queries: [{
    element: '[data-widget]'
  }]
});

function updateWidgets(summaries) {
  var widgetSummary = summaries[0];
  widgetSummary.added.forEach(buildNewWidget);
  widgetSummary.removed.forEach(cleanupExistingWidget);
}