吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2296|回复: 4
收起左侧

[其他原创] [JS] 监听 checkbox.checked 的变化

  [复制链接]
三滑稽甲苯 发表于 2023-5-3 21:30
本帖最后由 三滑稽甲苯 于 2023-5-3 21:32 编辑

问题

看到这个标题,可能有的观众会问了:“这不是添加一个 change 的 EventListener 就好了吗?”

让我先叙述一下完整的问题:

有一个形如 <input type="checkbox" id="checkbox"> 的 checkbox,我想要在 checkbox.checked 改变时被通知,包括被用户勾选以及被脚本改变时。

在这种情况下,checkbox.addEventListener("change", () => {console.log(checkbox.checked);}) 设置的 EventListener 只会在点击 checkbox 时被调用,并不会在脚本更改它的值时被调用。

解决过程

我第一个想到的解法也是 EventListener,但是直接监听 change 事件没法监听脚本的改动。那么自然想到了 Object.defineProperty

var _val = checkbox.checked;
Object.defineProperty(checkbox, "checked", {
    get: () => {
        return _val;
    },
    set: (val) => {
        _val = val;
        console.log(_val);
    }
})

这么干应该可以解决这个问题。但是实际上脚本更改了值后 checkbox 的外观没有改变,用户点击了 checkbox 后脚本得到的值没有改变。于是上 stackoverflow 上查找解决方案。经提示后找到了这个回答

屏幕截图 2023-05-03 212924.jpg

于是我稍作改动,得到了可行的代码:

const { get, set } = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked');
Object.defineProperty(checkbox, 'checked', {
    get() {
        return get.call(this);
    },
    set(newVal) {
        console.log(`Setting "checked" property to "${newVal}"...`);
        return set.call(this, newVal);
    }
});

只能说我的思路没错,还是欠了点火候。

通用适配

高端的解决方案往往采用最简单的调用方式。还记得开头的 EventListener 吗?如果我们在得知 checkbox.checked 被更改后发送事件 "change",那么添加的 EventListener 不就可以同时得知脚本更改事件了吗?于是有了以下代码:

function patch(checkbox) {
    const { get, set } = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked');
    Object.defineProperty(checkbox, 'checked', {
        get() {
            // If you want to monitor the retrieval of the `checked` property, uncomment the line below and do whatever you like...
            // console.log('Getting "checked" property...');
            return get.call(this);
        },
        set(newVal) {
            // console.log(`Setting "checked" property to "${newVal}"...`); // Debug
            this.dispatchEvent(new Event("change"));
            return set.call(this, newVal);
        }
    });
}

这样子,当我们想要监听某个 checkbox.checked 的更改(包括用户勾选以及脚本更改)时,只需调用 patch(checkbox),随后正常 addEventListener 即可。若想要区分是用户更改还是脚本更改,只需检查 Event 的 isTrusted 即可。

MWE 最小工作示例

<!DOCTYPE html>
<html>
    <head>
        <title>Checkbox patch test</title>
    </head>
    <body>
        <p><a href="https://github.com/PRO-2684/gadgets/tree/main/checkbox_patch">Github repo</a>, licensed under gpl 3.0.</p>
        <p>Checkbox -> <input type="checkbox"></p>
        <p>Click to set value <code>checked</code> via script -> <button>CLICK ME</button></p>
        <p>You will be able to see below when <code>change</code> event is detected.</p>
        <div id="result"></div>
        <script>
            function patch(checkbox) {
                const { get, set } = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked');
                Object.defineProperty(checkbox, 'checked', {
                    get() {
                        return get.call(this);
                    },
                    set(newVal) {
                        this.dispatchEvent(new Event("change"));
                        return set.call(this, newVal);
                    }
                });
            }
        </script>
        <script>
            let checkbox = document.querySelector('input[type="checkbox"]');
            patch(checkbox);
            function setChecked() {
                checkbox.checked = !checkbox.checked;
            }
            checkbox.addEventListener("change", (e) => {
                console.log(e);
                let ele = document.createElement("p");
                ele.innerText = `Change event detected! isTrusted: ${e.isTrusted}; Timestamp: ${e.timeStamp};`;
                result.appendChild(ele);
            })
        </script>
    </body>
</html>

屏幕截图 2023-05-03 213235.jpg

开源地址

https://github.com/PRO-2684/gadgets/blob/main/checkbox_patch

免费评分

参与人数 7吾爱币 +12 热心值 +6 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
soenluzy + 1 + 1 用心讨论,共获提升!
月光花下 + 1 谢谢@Thanks!
blindcat + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
侠骨留香喵 + 1 + 1 我很赞同!
luan123a + 1 谢谢@Thanks!
wenshao1344 + 1 + 1 沙发,顶起来

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

q438721 发表于 2023-5-9 17:52
defineProperty可以替换为proxy,真正意义的拦截器
kkoo 发表于 2023-5-3 23:29
songxp03 发表于 2023-5-4 12:27
Manjibody 发表于 2023-5-4 15:17
学习了,感谢分享!
xiao_007 发表于 2023-5-6 09:26
简单的问题
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 22:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表