JavaScript:如何监听属性的变化?

124

在JavaScript中,是否可以监听属性值的变化?例如:

var element=document.querySelector('…');
element.addEventListener( ? ,doit,false);

element.setAttribute('something','whatever');

function doit() {

}

我希望能够响应对something属性的任何更改。

我已经研究了MutationObserver对象以及替代该对象的方法(包括使用动画事件的方法)。据我所知,它们都是关于实际DOM更改的。我更关心特定DOM元素属性的更改,因此我认为这不是解决方案。在我的实验中,它似乎并没有起作用。

我希望在不使用jQuery的情况下完成这个任务。

谢谢


3
MutationObserver 可以实现这个功能。只需将其配置为监听属性更改即可。将其设置为仅观察您感兴趣的元素即可。 - Sebastian Simon
3个回答

209
你需要使用 MutationObserver,这个代码片段中我使用了 setTimeout 来模拟修改属性。

var element = document.querySelector('#test');
setTimeout(function() {
  element.setAttribute('data-text', 'whatever');
}, 5000)

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if (mutation.type === "attributes") {
      console.log("attributes changed");

      // Example of accessing the element for which 
      // event was triggered
      mutation.target.textContent = "Attribute of the element changed";
    }
    
    console.log(mutation.target);
  });
});

observer.observe(element, {
  attributes: true //configure it to listen to attribute changes
});
<div id="test">Dummy Text</div>

此外,mutation.target 属性提供了对变异/更改节点的引用。


感谢你的回答和示例。一旦我优化了这个,我认为它将是通过更改属性来对元素进行更复杂更改的好技巧。这使得API更容易使用。你是否知道一个适用于简陋浏览器的合理Polyfill? - Manngo
@Manngo,针对原始浏览器,你可以使用变异事件,还可以参考https://davidwalsh.name/dom-events-javascript。 - Satpal
我看到它目前支持IE9及以上版本,并且在现代浏览器中仍然得到支持。再次感谢。 - Manngo
当我们无法访问观察者中的元素时,它就变得有点无用了。 - inux
mutation.target 是观察器变异中的元素。 - undefined

12
这个问题已经有答案了,但我想分享一下我的经验,因为变异观察器没有给我所需的见解。
注意这是某种Hacky的解决方案,但至少对于调试目的来说非常好。
您可以重写特定元素的setAttribute函数。这样,您还可以打印调用堆栈,并了解“谁”更改了属性值。
// select the target element
const target = document.querySelector("#element");
// store the original setAttribute reference
const setAttribute = target.setAttribute;
// override setAttribte
target.setAttribute = (key: string, value: string) => {
  console.trace("--trace");
  // use call, to set the context and prevent illegal invocation errors
  setAttribute.call(target, key, value); 
};

1
一个巧妙的技巧。请注意,如果调用 setAttributeNS(),这将失败。 - Phrogz

0
使用发布/订阅模式(这是从某人那里借来的,毫无疑问,这个人肯定是从别人那里偷来的):
        PubSub: function () {

        //var topics = {};
        //var hOP = topics.hasOwnProperty;
        var hasProp = Me.Subscriptions.hasOwnProperty;

        return {
            Subscribe: function (topic, listener) {
                // Create the topic's object if not yet created
                if (!hasProp.call(Me.Subscriptions, topic)) Me.Subscriptions[topic] = [];

                // Add the listener to queue
                var index = Me.Subscriptions[topic].push(listener) - 1;

                // Provide handle back for removal of topic
                return {
                    remove: function () {
                        delete Me.Subscriptions[topic][index];
                    }
                };
            },
            Publish: function (topic, info) {
                // If the topic doesn't exist, or there's no listeners in queue, just leave
                if (!hasProp.call(Me.Subscriptions, topic)) return;

                // Cycle through topics queue, fire!
                Me.Subscriptions[topic].forEach(function (item) {
                    item(info != undefined ? info : {});
                });
            }
        };
    },

使用方法:
发布:
PubSub.Subscriber().Publish('AppLoaded', true);

订阅:

PubSub.Subscriber().Subscribe('AppLoaded', function (result) {

    //I can now do things I'll tell my friends I did anyway
            
});

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