使用 MutationObserver 检测输入值变化
Posted
技术标签:
【中文标题】使用 MutationObserver 检测输入值变化【英文标题】:Detect input value change with MutationObserver 【发布时间】:2015-11-29 17:51:28 【问题描述】:我想检测输入字段中的文本/值何时发生变化。即使我用js改变了值,我也想检测到变化。
这是我迄今为止在demo in fiddle 中尝试过的。
HTML:
<input type="text" id="exNumber"/>
JavaScript:
var observer = new MutationObserver(function(mutations)
mutations.forEach(function(mutation)
// console.log('Mutation type: ' + mutation.type);
if ( mutation.type == 'childList' )
if (mutation.addedNodes.length >= 1)
if (mutation.addedNodes[0].nodeName != '#text')
// console.log('Added ' + mutation.addedNodes[0].tagName + ' tag.');
else if (mutation.removedNodes.length >= 1)
// console.log('Removed ' + mutation.removedNodes[0].tagName + ' tag.')
if (mutation.type == 'attributes')
console.log('Modified ' + mutation.attributeName + ' attribute.')
);
);
var observerConfig =
attributes: true,
childList: false,
characterData: false
;
// Listen to all changes to body and child nodes
var targetNode = document.getElementById("exNumber");
observer.observe(targetNode, observerConfig);
【问题讨论】:
如果你不耐烦,现在想要一个可怕的、糟糕的、糟糕的修复,那么我为你做了这个:IDL-Property-Observe。运行此库后,您的上述代码将以牺牲原生原型的最佳实践为代价运行良好。干杯! 【参考方案1】:要了解发生了什么,有必要弄清楚 attribute(内容属性)和 property(IDL 属性)之间的区别。我不会对此进行扩展,因为在 SO 中已经有涵盖该主题的出色答案:
Properties and Attributes in html .prop() vs .attr() What is happening behind .setAttribute vs .attribute=?当您通过键入或通过 JS 更改 input
元素的内容时:
targetNode.value="foo";
浏览器更新value
属性,但不更新value
属性(它反映了defaultValue
属性)。
然后,如果我们查看spec of MutationObserver,我们会发现attributes 是可以使用的对象成员之一。所以如果你显式设置value
属性:
targetNode.setAttribute("value", "foo");
MutationObserver 将通知属性修改。但是规范列表中没有类似 properties 的东西:value
属性无法观察。
如果您想检测用户何时更改输入元素的内容,input
event 是最直接的方法。如果您需要捕获 JS 修改,请使用 setInterval
并将新值与旧值进行比较。
查看此SO question 了解不同的替代方案及其限制。
【讨论】:
onkeyup
对我的情况没有用。实际上,由于某种原因我无法修改外部脚本。输入字段的脚本更改值。所以我想检测文本何时更改
在这种情况下,请检查this other one。
tnx david 所以唯一丑陋的解决方案是使用计时器。我不希望这就是我尝试观察者的原因。但不幸的是可能没有其他方法可以做到这一点
你有任何关于为什么 dom 没有改变的细节吗?毕竟我可以在浏览器窗口中看到变化。
@Mene 感谢您指出这一点:这是错误的。我想我想说的是 HTML(定义属性)而不是 DOM(定义属性)。通常将属性的更改称为 HTML 的更改。无论如何,严格来说,HTML 是驻留在服务器中的静态文本(它不会改变),而 DOM 是解析标记产生的内存对象(只要用户或脚本修改它,它就会改变)所以我刚删了那句话。实际上,我重写了整个内容以更具体。【参考方案2】:
我对@987654321@ 做了一点修改,想分享一下。不敢相信实际上有解决方案。
在输入框中键入以查看默认行为。现在,打开 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" />
【讨论】:
【参考方案3】:价值属性可以观察到,别浪费时间了。
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>
【讨论】:
我正在研究一个类似的问题,我对这个答案很感兴趣,但我有点挣扎。这仅适用于 textarea 的权利?由于您设置了出现在 textarea 字段内的 textContent,但 textContent 不会出现在 HTML 的输入字段内。 @Josh 输入字段使用return this.defaultValue = t;
:)
您能否详细说明您的代码是如何工作的?【参考方案4】:
这有效并保留并链接了原始的 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);
【讨论】:
您能解释一下您的解决方案是如何工作的吗?它在做什么? 我认为这是开箱即用的最佳解决方案。在我的应用程序中完美运行,我喜欢它,因为它让原始设置器不受影响。 对于表单调用中的每个字段:setDetectChangeHandler(document.querySelector('.myFrom .fieldN'));然后为了捕捉所有变化,在表单中添加一个监听器 document.querySelector('.myForm').addEventListener('change', event => // 在这里处理字段变化 ); 我喜欢这个解决方案。尽管这在特定情况下会导致一个小问题。我已向具有去抖动行为的字段添加了一个事件侦听器(用于输入事件)。我在字段中输入的值会在 500 毫秒后更新(更正货币格式)。所以我更新了字段,字段更新了自己(它的值)。如果我使用这个 (setDetectChangeHandler
) 方法,我最终会出现一个循环。 "input"
事件连续触发。因此,我没有在set
中触发事件,而是将回调传递给setDetectChangeHandler
并在set
中调用它。这提供了更多控制权。
我该如何使用它? field
是什么?以上是关于使用 MutationObserver 检测输入值变化的主要内容,如果未能解决你的问题,请参考以下文章
MutationObserver 在整个 DOM 中检测节点的性能
javascript 检测dom变化#MutationObserver #js
javascript 检测dom变化#MutationObserver #js