使用vanilla javascript和MutationObserver检测输入标签的值变化。

问题描述 投票:16回答:3

我想检测输入字段中的文本值是否发生变化。即使我用js改变了值,我也想检测到它的变化。

以下是我目前所做的尝试。演奏.

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);
javascript html mutation-observers
3个回答
24
投票

为了理解发生了什么,有必要弄清楚这两者之间的区别。属性 (内容属性)和 财产 IDL属性)。我不展开说了,因为在SO中已经有很好的答案涵盖了这个话题。

当你改变一个 input 元素,通过键入或JS。

targetNode.value="foo";

浏览器会更新 value 财产 而非 value 属性 (这反映了 defaultValue 属性)。)

然后,如果我们看一下 突变观察者的规格我们将看到 属性 是可以使用的对象成员之一。所以如果你明确地将 value 属性。

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

MutationObserver会通知一个属性修改。但没有像 属性 在规格列表中: value 财产 看不到.

如果你想检测用户何时更改输入元素的内容,可以使用 input 事件 是最直接的方式。如果你需要抓JS的修改,可以选择用 setInterval 并比较新值和旧值。

检查这个 所以问题 了解不同的替代品及其局限性。


7
投票

价值属性 可见不要浪费你的时间。

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>

6
投票

这可以保留并链住原来的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
投票

我已经修改了 肖恩的方法 一点点,想和大家分享一下。想不到真的有解决方法。

在输入框中输入,可以看到默认的行为。现在,打开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" />
© www.soinside.com 2019 - 2024. All rights reserved.