改变input的值不会触发change事件的解决思路

一般来讲,若是咱们本身经过 value 改变了 input 元素的值,咱们确定是知道的,可是在某些场景下,页面上有别的逻辑在改变 input 的 value 值,咱们可能但愿能在这个值发生变化的时候收到通知。因而咱们想到了 onchange 事件,然而咱们遗憾的发现,onchange 事件却并不会被触发,由于onchange事件触发是有条件的。数组

onchange 事件的触发条件

onchange 触发须要三个步骤:函数

  1. input 元素得到焦点
  2. input 元素的值发生变化
  3. input 元素失去焦点

并且必须是点击触发的,这句话的意思是,尽管咱们能够经过 input.focus() 使 input 元素得到焦点,能够经过 input.value 改变值,能够经过 input.blur() 使元素失去焦点,可是这并不会触发 onchange 事件,能够看下面的 demo 一探究竟:this

如何在改变 value 时得到通知

一种方法是使用 timer。经过 setInterval 的方式来不断查看 value 值是否发生变化。这种方法虽然能够 work,可是实时性不是很好,也比较浪费资源。因此有没有第二种方法呢,答案是本文接下来要说的 -- 重写 value 属性 spa

其实这种操做尽管不推荐,可是仍是比较常见的。好比 Vue,经过重写 Array 的 push,pop,concat 等方法,从而实现了只要对数组进行上述操做,就能触发界面更新。那么接下来,咱们来尝试重写 input 元素的这个 value 属性,实现改变 value 值时,咱们能够获得通知。prototype

能够判断的是,value 绝对不是一个简单的值,因此咱们先看看 value 是如何定义的:code

let input = document.querySelector(input);
console.log(Object.getOwnPropertyDescriptor(input, 'value'))

能够看到打印出来是 undefined,因此 value 这个属性是 input 元素继承过来的,也就是位于 HTMLInputElement 的 prototype 上 -- input.constructor.prototype 或者 input.__proto__。因而将上面的代码改一下:对象

console.log(Object.getOwnPropertyDescriptor(input.__proto__, 'value'))

打印结果以下:
uploading-image-256926.pngblog

因而咱们知道了 value 是挂在 input 元素原型对象上的一个 getter 和 setter 的属性。那么接下来,咱们只要改写 setter,在 setter 中加入通知代码,而后同时调用原来的 setter,就能够检测 value 的变化。代码以下:继承

let descriper = Object.getOwnPropertyDescriptor(input.__proto__, 'value');
// 取出原先的 get 和 set 函数
let getValue = descriper.get;
let setValue = descriper.set;

Object.defineProperty(
  input.__proto__, 
  'value', 
  {
    configurable: true,
    enumerable: true,
    get: function (){
      return getValue.call(this);
    },
    // 重写 set 方法
    set: function (){
      console.log(arguments, this);
      // 加入通知代码
      $(this).trigger('valChange');
      setValue.call(this, ...arguments);
    }
  })

下面是一个 demo,能够看到,点击 button 设置 value 时,能够被看到控制台打印出 value 发生变化。事件

总结

在经过 js 设置 value 时,没法触发 onchange 事件,这里这个问题提供了另一种解决思路,基本思想上写一个新的函数替换原有 value 属性的 setter,在新函数中加入本身的逻辑后调用原有的 setter。(本文完)

相关文章
相关标签/搜索