通常来说,如果我们自己通过 value 改变了 input 元素的值,我们肯定是知道的,但是在某些场景下,页面上有别的逻辑在改变 input 的 value 值,我们可能希望能在这个值发生变化的时候收到通知。于是我们想到了 onchange 事件,然而我们遗憾的发现,onchange 事件却并不会被触发,因为onchange事件触发是有条件的。

onchange 事件的触发条件

onchange 触发需要三个步骤:

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

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

See the Pen onchange by imgss
(@imgss) on CodePen.

如何在改变 value 时获得通知

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

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

可以判断的是,value 绝对不是一个简单的值,所以我们先看看 value 是如何定义的:

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'))

打印结果如下:

于是我们知道了 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 发生变化。

See the Pen onchange1 by imgss
(@imgss) on CodePen.

总结

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

改变input的值不会触发change事件的解决思路的更多相关文章

  1. iframe页面改动parent页面的隐藏input部件value值,不能触发change事件。

    实现一个依据iframe页面返回充值卡类型不同,安排不同的input部件. 点击选择弹出一个iframe.点击充值卡数据行.返回1.充值卡类型.2.充值卡id(用的UUID).3.充值卡号(字符串). ...

  2. 解决上传文件或图片时选择相同文件无法触发change事件的问题

    昨天在做一个上传文件的模块时遇到了这样的问题:打开文件一上传,上传成功后再次点击文件一,change事件无反应 <input type="file" name="f ...

  3. file类型input框设置上传相同文件,并都可以触发change事件。

    在使用file类型input框是,删除了第一次上传到文件,再次上传相同文件,无法触发change事件,所以在删除的js上添加如下js代码: document.getElementById('fileU ...

  4. js 触发 change 事件

    首先,请各位包涵,我本人对 JS 不是很熟,不知道"触发change事件"和"触发onchange事件"哪个更加合适.有园友知道的麻烦指出,先行谢过. 起因是这 ...

  5. jquery 赋值时不触发change事件解决

    $("#optionsId").change(function(){ $("#selectOptionsText").val('测试'); }); $(&quo ...

  6. Jquery触发Change事件

    Jquery直接使用val的话不会触发Change事件需要做如下处理$("#"+p_id).val(p_time); $("#"+p_id).change();

  7. JS事件 文本框内容改变事件(onchange)通过改变文本框的内容来触发onchange事件,同时执行被调用的程序。

    文本框内容改变事件(onchange) 通过改变文本框的内容来触发onchange事件,同时执行被调用的程序. 如下代码,当用户将文本框内的文字改变后,弹出对话框"您改变了文本内容!&quo ...

  8. vb.net WPF webbrowser window.close 关闭后不触发 WindowClosing 事件 WNDPROC解决方式

     vb.net WPF webbrowser window.close 关闭后不触发 WindowClosing 事件 WNDPROC解决方式 #Region "WPF 当浏览器窗体关闭 ...

  9. input输入框file类型第二次不触发onchange事件的解决办法,简单有效

    在网上看了很多办法,现在将网上大部分说法总结如下: 网上说法: 原因:选择一次后onchange事件没有绑定到input标签上:    解决办法:拷贝一份input标签的副本,每次选择后对原input ...

随机推荐

  1. W7500S2E串口转以太网

    概述 W7500S2E是一系列串口转以太网模块,支持TCP Server.TCP Client和UDP三种工作模式,串口波特率最高可达460,800bps,并提供配套的上位机配置软件,也可通过网页或A ...

  2. js html标签select 中option 删除除了第一行外的其他行

    背景:共两个下拉框,第一个下拉框选择完之后,以第一个选定的值为条件返回第二个下拉框中的内容,用js中的createElement()创建,并利用appendChild()来添加进父标签.出现意外:每次 ...

  3. IPC rtsp转发服务器搭建

    sudo apt-get install libmoose-perl liburi-perl libmoosex-getopt-perl libsocket6-perl libanyevent-per ...

  4. HTML之 一 标签

    一 ,标签分类: 1.普通标签: <h1> hello </h1> hello 2.自闭和标签 <hr/> 二,书写html注意事项 1.标签不能交叉嵌套 2. 标 ...

  5. 跨域资源共享(CROS)

    跨域资源共享(CROS) 同源策略(Same Origin Policy, SOP) 同源策略允许运行在页面的脚本可以无限制的访问同一个网站(同源)中其他脚本的任何方法和属性.当不同网站页面(非同源) ...

  6. python之路(十)-正则表达式

    re模块用于对python的正则表达式的操作. 字符: . 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d 匹配数字 \b 匹配单词的开始或结束 ^ 匹配 ...

  7. IO模型的介绍

    Stevens 在文章中的一种IO Model: ****blocking IO    #阻塞 IO   (系统调用不返回结果并让当前线程一直阻塞,只有当该系统调用获得结果或者超时出错才返回) *** ...

  8. 洛谷P1064--金明的预算方案(简单背包)

    https://www.luogu.org/problemnew/show/P1064 #include<iostream> #include<algorithm> #incl ...

  9. PyCharm连接远程服务器

    PyCharm连接远程服务器 1.Tools->Start SSH session 2.新建一个连接或者选择一个已经存在的sftp 3.选择Edit credentials 填写相关连接信息 4 ...

  10. 01 of lenrning python

    我也打算开始写博客啦!记录一下自己的学习过程~ 01密文登陆 这个在vscode中不显示密文 在cmd中看不到密码 # -*- coding: utf-8 -*- import getpass nam ...