使用 MutationObserver 检测输入值变化

问题描述 投票:0回答:5
javascript html mutation-observers
5个回答
39
投票

要了解正在发生的情况,有必要明确 attribute(内容属性)和 property(IDL 属性)之间的区别。我不会对此进行扩展,因为在 SO 中已经有很好的答案涵盖了该主题:

当您通过输入或通过 JS 更改

input
元素的内容时:

targetNode.value="foo";

浏览器更新

value
属性,但不更新
value
属性(而是反映
defaultValue
属性)。

然后,如果我们查看 MutationObserverspec,我们会发现 attributes 是可以使用的对象成员之一。因此,如果您显式设置

value
属性:

targetNode.setAttribute("value", "foo");

MutationObserver 将通知属性修改。但是规范列表中没有类似 properties 的内容:无法观察到

value
属性

如果您想检测用户何时更改输入元素的内容,

input

事件
是最直接的方法。如果您需要捕获 JS 修改,请使用 setInterval
 并将新值与旧值进行比较。 

检查这个

SO问题以了解不同的替代方案及其局限性。


20
投票
我稍微修改了

Shawn的方法并想分享它。不敢相信实际上有解决方案。

在输入框中键入内容以查看默认行为。现在,打开 DevTools 并选择输入元素,然后更改其值,例如

$0.value = "hello"

。检查 UI 与 API 的差异。看来 UI 交互不会直接修改 
value
 属性。如果是的话,它也会记录 
"...changed via API..."

let inputBox = document.querySelector("#inputBox"); inputBox.addEventListener("input", function () { console.log("Input value changed via UI. New value: '%s'", this.value); }); observeElement(inputBox, "value", function (oldValue, newValue) { console.log("Input value changed via API. Value changed from '%s' to '%s'", oldValue, newValue); }); function observeElement(element, property, callback, delay = 0) { let elementPrototype = Object.getPrototypeOf(element); if (elementPrototype.hasOwnProperty(property)) { let descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property); Object.defineProperty(element, property, { get: function() { return descriptor.get.apply(this, arguments); }, set: function () { let oldValue = this[property]; descriptor.set.apply(this, arguments); let newValue = this[property]; if (typeof callback == "function") { setTimeout(callback.bind(this, oldValue, newValue), delay); } return newValue; } }); } }
<input type="text" id="inputBox" placeholder="Enter something" />


9
投票
价值属性

可以观察到,不要浪费时间。

function changeValue (event, target) { document.querySelector("#" + target).value = new Date().getTime(); } function changeContentValue () { document.querySelector("#content").value = new Date().getTime(); } Object.defineProperty(document.querySelector("#content"), "value", { set: function (t) { alert('#changed content value'); var caller = arguments.callee ? (arguments.callee.caller ? arguments.callee.caller : arguments.callee) : '' console.log('this =>', this); console.log('event => ', event || window.event); console.log('caller => ', caller); return this.textContent = t; } });
<form id="form" name="form" action="test.php" method="post">
        <input id="writer" type="text" name="writer" value="" placeholder="writer" /> <br />
        <textarea id="content" name="content" placeholder="content" ></textarea> <br />
        <button type="button" >Submit (no action)</button>
</form>
<button type="button" onClick="changeValue(this, 'content')">Change Content</button>


9
投票
这可以工作并保留和链接原始的 setter 和 getter,因此有关您领域的其他所有内容仍然有效。

var registered = []; var setDetectChangeHandler = function(field) { if (!registered.includes(field)) { var superProps = Object.getPrototypeOf(field); var superSet = Object.getOwnPropertyDescriptor(superProps, "value").set; var superGet = Object.getOwnPropertyDescriptor(superProps, "value").get; var newProps = { get: function() { return superGet.apply(this, arguments); }, set: function (t) { var _this = this; setTimeout( function() { _this.dispatchEvent(new Event("change")); }, 50); return superSet.apply(this, arguments); } }; Object.defineProperty(field, "value", newProps); registered.push(field); } }
    

0
投票
一个监控输入属性的小管理器:

var observerList = []; var observePropertyChangeStarted = false; function observePropertyChange(input, propertyName, callback) { observerList.push({ input: input, propertyName: propertyName, state: input[propertyName], callback: callback }); if (!observePropertyChangeStarted) { observePropertyChangeStarted = true; setInterval(_ => { for (const obj of observerList) { const state = obj.input[obj.propertyName]; if (state !== obj.state) { obj.state = state; obj.callback(obj.input); } } }, 100); } }
然后只需监听任何输入属性:

const checkBoxInput = document.querySelector('input[type="checkbox"]'); observePropertyChange(checkBoxInput, 'checked', (target) => { // myActions(); }); checkBoxInput.checked = false; const rangeInput = document.querySelector('input[type="range"]'); observePropertyChange(rangeInput , 'value', (target) => { // myActions(); }); rangeInput.value = 10;
该函数仅调用一次“setInterval”来限制资源的使用。

© www.soinside.com 2019 - 2024. All rights reserved.