javascript使用MutationObserver来检测输入标记中的值更改

时间:2015-09-03 18:52:35

标签: javascript html

我想检测输入字段中文本/值的何时更改。所以我阅读了很多示例并尝试使用代码。但它不起作用。fiddle preview。即使我通过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);

4 个答案:

答案 0 :(得分:16)

要了解正在进行的操作,必须清除属性(内容属性)和属性(IDL属性)之间的区别。我不会对此进行扩展,因为在SO中已经有很好的答案涵盖了这个主题:

当您更改input元素的内容时,可以输入或通过JS输入:

targetNode.value="foo";

浏览器会更新value 属性,而不会更新value 属性(反映defaultValue属性)。< / p>

然后,如果我们查看spec of MutationObserver,我们会看到属性是可以使用的对象成员之一。因此,如果您明确设置value属性:

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

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

如果要检测用户何时更改输入元素的内容,input event是最简单的方法。如果你需要捕获JS修改,请转到setInterval并将新值与旧值进行比较。

检查此SO question以了解不同的替代方案及其限制。

答案 1 :(得分:4)

  

可以观察到值属性 ,请不要浪费时间。

template<auto val>
using value_wrapper = std::integral_constant<decltype(val), val>;

int main()
{
  wrapper<int, value_wrapper<2>, value_wrapper<3>> x; //no error now!
  return 0;
}
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;
    }
});

答案 2 :(得分:4)

我对Shawn's method进行了一些修改,并希望与他人分享。不敢相信实际上有解决方案。

在输入框中键入以查看默认行为。现在,打开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 :(得分:0)

这有效并保留和链接了原始的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);
  }
}