Javascript 对输入值进行编程更改时触发操作

Javascript 对输入值进行编程更改时触发操作,javascript,html-input,defineproperty,Javascript,Html Input,Defineproperty,我的目标是观察输入值,并在其值以编程方式更改时触发处理程序。我只在现代浏览器中需要它 我使用defineProperty尝试了许多组合,这是我的最新迭代: var myInput=document.getElementById("myInput"); Object.defineProperty(myInput,"value",{ get:function(){ return this.getAttribute("value"); }, set:functi

我的目标是观察输入值,并在其值以编程方式更改时触发处理程序。我只在现代浏览器中需要它

我使用
defineProperty
尝试了许多组合,这是我的最新迭代:

var myInput=document.getElementById("myInput");
Object.defineProperty(myInput,"value",{
    get:function(){
        return this.getAttribute("value");
    },
    set:function(val){
        console.log("set");
        // handle value change here
        this.setAttribute("value",val);
    }
});
myInput.value="new value"; // should trigger console.log and handler
这似乎达到了我的预期,但它感觉像是一个黑客,因为我正在覆盖现有的value属性并使用
value
(属性和属性)的双重状态。它还中断了似乎不喜欢修改的属性的
change
事件

我的其他尝试:

  • setTimeout/setInterval循环,但这也不是干净的
  • 各种
    watch
    observe
    polyfill,但它们因输入值属性而中断
达到同样结果的正确方法是什么

现场演示:

[编辑]澄清:我的代码正在监视一个输入元素,其他应用程序可以在其中推送更新(例如,由于ajax调用,或者由于其他字段中的更改)。我无法控制其他应用程序如何推送更新,我只是一个观察者

[编辑2]为了澄清我所说的“现代浏览器”是什么意思,我很高兴有一个能在IE 11和Chrome 30上运行的解决方案

[更新]根据接受的答案更新了演示:


@mohit jain建议的诀窍是为用户交互添加第二个输入。

既然您已经在使用polyfills进行监视/观察等,让我借此机会向您提出建议

它以ng模型的形式提供了这一功能。您可以在模型的值上设置观察者,当它发生变化时,您可以调用其他函数

以下是一个非常简单但有效的解决方案:

基本上,进行文本输入并将其绑定到模型:

<input type="text" data-ng-model="variable">
我只在现代浏览器中需要它

你想要多现代?Ecma脚本7(6将在12月定稿)可能包含以下内容。这将允许您创建本机可观察对象。是的,你可以运行它!怎么做

要尝试此功能,您需要启用enable Chrome Canary中的实验JavaScript标志并重新启动浏览器。 该标志可在
'about:flags'

更多信息:

是的,这是高度实验性的,在当前的浏览器中还没有准备好。而且,它还没有完全准备好,而且如果是ES7的话也不是100%,ES7的最终日期甚至还没有确定。不过,我还是想让您知道,以备将来使用。

有一种方法可以做到这一点。 没有DOM事件,但是有一个javascript事件会在对象属性更改时触发

document.form1.textfield.watch("value", function(object, oldval, newval){})
                ^ Object watched  ^                      ^        ^
                                  |_ property watched    |        |
                                                         |________|____ old and new value
在回调中,您可以执行任何操作


在本例中,我们可以看到这种效果(请检查):

obj
更改时,它会激活
监视
侦听器。


此链接中有更多信息:

如果解决方案的唯一问题是值集上的更改事件中断。thn您可以在现场手动触发该事件。(但如果用户通过浏览器更改输入,则不会设置此监视器--请参见下面的编辑)


//API将要更改的隐藏输入
var myInput=document.getElementById(“myInput”);
//用户的可视输入
var myInputVisible=document.getElementById(“myInputVisible”);
//隐藏输入的属性变异
Object.defineProperty(myInput,“值”{
get:function(){
返回此.getAttribute(“值”);
},
设置:功能(val){
console.log(“set”);
//更新myInput集合上myInputVisible的值
myInputVisible.value=val;
//在这里处理值更改
此.setAttribute(“值”,val);
//解雇事件
如果(文档中的“createEvent”){//现代浏览器
var evt=document.createEvent(“HTMLEvents”);
evt.initEvent(“变更”、真、假);
myInput.dispatchEvent(evt);
}
否则{//IE 8及以下
var evt=document.createEventObject();
myInput.fireEvent(“onchange”,evt);
}
}
});  
//侦听可见的输入更改并更新隐藏的
myInputVisible.onchange=函数(e){
myInput.value=myInputVisible.value;
};
//这是您希望使用的任何自定义事件处理程序
//它将捕获两个编程更改(直接在myInput上完成)
//和用户的更改(在myInputVisible上完成)
myInput.onchange=函数(e){
log(myInput.value);
};
//演示编程更改的测试方法
函数testSet(){
myInput.value=Math.floor((Math.random()*100000)+1);
}


编辑

手动事件触发和mutator方法的问题在于,当用户从浏览器更改字段值时,value属性不会更改。解决方法是使用两个字段。一个隐藏的,我们可以有编程交互。另一个是可见的,用户可以与之交互。考虑到这一点,这种方法非常简单

  • 对隐藏输入字段的值属性进行变异,以观察更改并触发手动onchange事件。在设置值时,更改可见字段的值以向用户提供反馈
  • 在可见字段值更改时,为观察者更新隐藏的值
  • 我不久前写了以下内容,它允许跨浏览器(包括IE8+)侦听自定义事件

    看看我是如何收听IE8上的
    onpropertychange

    util.listenToCustomEvents = function (event_name, callback) {
      if (document.addEventListener) {
        document.addEventListener(event_name, callback, false);
      } else {
        document.documentElement.attachEvent('onpropertychange', function (e) {
        if(e.propertyName == event_name) {
          callback();
        }
      }
    };
    
    我不确定IE8解决方案是否可以跨浏览器工作,但您可以在输入的属性
    上设置一个假的
    eventlistener
    ,并在值
    var obj = { prop: 123 };
    
    obj.watch('prop', function(propertyName, oldValue, newValue){
        console.log('Old value is '+oldValue); // 123
        console.log('New value is '+newValue); // 456
    });
    
    obj.prop = 456;
    
    <html>
      <body>
        <input type='hidden' id='myInput' />
        <input type='text' id='myInputVisible' />
        <input type='button' value='Test' onclick='return testSet();'/>
        <script>
          //hidden input which your API will be changing
          var myInput=document.getElementById("myInput");
          //visible input for the users
          var myInputVisible=document.getElementById("myInputVisible");
          //property mutation for hidden input
          Object.defineProperty(myInput,"value",{
            get:function(){
              return this.getAttribute("value");
            },
            set:function(val){
              console.log("set");
    
              //update value of myInputVisible on myInput set
              myInputVisible.value = val;
    
              // handle value change here
              this.setAttribute("value",val);
    
              //fire the event
              if ("createEvent" in document) {  // Modern browsers
                var evt = document.createEvent("HTMLEvents");
                evt.initEvent("change", true, false);
                myInput.dispatchEvent(evt);
              }
              else {  // IE 8 and below
                var evt = document.createEventObject();
                myInput.fireEvent("onchange", evt);
              }
            }
          });  
    
          //listen for visible input changes and update hidden
          myInputVisible.onchange = function(e){
            myInput.value = myInputVisible.value;
          };
    
          //this is whatever custom event handler you wish to use
          //it will catch both the programmatic changes (done on myInput directly)
          //and user's changes (done on myInputVisible)
          myInput.onchange = function(e){
            console.log(myInput.value);
          };
    
          //test method to demonstrate programmatic changes 
          function testSet(){
            myInput.value=Math.floor((Math.random()*100000)+1);
          }
        </script>
      </body>
    </html>
    
    util.listenToCustomEvents = function (event_name, callback) {
      if (document.addEventListener) {
        document.addEventListener(event_name, callback, false);
      } else {
        document.documentElement.attachEvent('onpropertychange', function (e) {
        if(e.propertyName == event_name) {
          callback();
        }
      }
    };