观察DOM变化的优雅方式

39
我需要监视特定DOM元素的任何子元素的属性更改。到目前为止,我一直在使用变异事件
问题是 - 它们存在缺陷:例如,在Chromium下,DOMAttrModified未触发,但DOMSubtreeModified被触发。问题很容易解决:因为根据规范,如果触发了其他事件,则会触发DOMSubtreeModified,所以我只需监听DOMSubtreeModified即可。
无论如何,最近版本的Chromium如果修改了属性,则不会触发任何内容。
然而,新的Mutation Observer API却可以完美地解决这个问题。
到目前为止,我只需要在特定元素的子树中的任何更改时触发回调-因为不应该有其他更改-因此我通过在同一段代码中使用变异事件和变异观察器(如果可用)来解决了我的问题。
然而,现在我需要对事件进行更强大的过滤(例如,在新节点上,删除节点上)- 是否有库,可能是jQuery插件,可以让我优雅地使用MutationObserver API和变异事件作为后备,具有筛选特定事件类型(例如添加元素,更改属性)的能力。
例如:
$("#test").watch({onNewElement: 1}, function(newElement){})
$("#test").watch({onNewAttribute: 1}, function(modifiedElement) {})

或者不使用jQuery

watchChanges("#test", {onNewElement: 1}, function(newElement){})
watchChanges("#test", {onNewAttribute: 1}, function(modifiedElement){})

经过一些搜索,我发现这个只支持新的API:http://code.google.com/p/mutation-summary/。我还发现了这个,它似乎是你所要求的简单版本:https://dev59.com/nGgv5IYBdhLWcg3wD8uY。 - thirtydot
我还发现了mutation-summary,但它不支持变异事件作为后备。你发布的线程的第一个答案基本上就是我在代码中观察属性更改时所做的事情。在同样的原则下,可以构建一个库,使用变异观察器和(作为后备)事件来监视特定的DOM事件。这就是我需要的。 - Ivo
2
+1:我真的很喜欢“最佳实践”问题。 - Jezen Thomas
2个回答

2
为了使用jQuery进行高效的DOM监控,您可以看一下jQuery Behavior Plugin(免责声明:我是作者),它使用了Live Query的优化版本。我已经在多个产品中使用它3年了,没有任何问题。
编辑:监听属性变化的方法如下:
$.behavior({
    'div:first': {
        'changeAttr': function (event, data) {
            console.log('Attribute "' +  data.attribute + '" changed from "' + data.from + '" to "' + data.to + '"');
        }
    }
});

$('div:first').attr('foo', 'bar');

2
这个可以吗? http://darcyclarke.me/development/detect-attribute-changes-with-jquery/ 可以检测属性变化吗?
$.fn.watch = function(props, callback, timeout){
    if(!timeout)
        timeout = 10;
    return this.each(function(){
        var el      = $(this),
            func    = function(){ __check.call(this, el) },
            data    = { props:  props.split(","),
                        func:   callback,
                        vals:   [] };
        $.each(data.props, function(i) { data.vals[i] = el.css(data.props[i]); });
        el.data(data);
        if (typeof (this.onpropertychange) == "object"){
            el.bind("propertychange", callback);
        } else if ($.browser.mozilla){
            el.bind("DOMAttrModified", callback);
        } else {
            setInterval(func, timeout);
        }
    });
    function __check(el) {
        var data    = el.data(),
            changed = false,
            temp    = "";
        for(var i=0;i < data.props.length; i++) {
            temp = el.css(data.props[i]);
            if(data.vals[i] != temp){
                data.vals[i] = temp;
                changed = true;
                break;
            }
        }
        if(changed && data.func) {
            data.func.call(el, data);
        }
    }
}

我看到了,但它只适用于属性。另外,即使浏览器可能支持DOMAttrModified,它也会回退到setInterval。 - Ivo
@Ivo 对的。这里有一个想法 - 你是否控制所有这些发生在DOM上的更改?是否考虑重写jQuery操作方法以触发更改/追加/删除事件(但这是假设所有更改都是通过jQuery进行的)。我知道那是很多工作,并不完全符合你的要求,但可以完成工作。 - Coffee Bite
1
我很久以前就完成了这项工作 - 我使用变异事件和观察者来监视属性,而且我只使用事件来监视DOM上新添加的内容 - 但由于我正在观察通过contenteditable/WYSIHTML5编辑器进行的更改,所以我并不完全掌控。但是对于我计划支持的浏览器,所有这些都有效。这更像是一个“最佳方法”问题。 而猴子补丁jQuery似乎是个坏主意。猴子补丁DOM API可能更合适,但我仍然不确定。 - Ivo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接