Chrome系列12 - Vue Tampermonkey操作知识库合集
完整示例代码
// ==UserScript==
// @name Form Auto Fill
// @match https://example.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict'
// 获取原生 setter
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype, 'value'
).set
const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype, 'value'
).set
// 输入文本
function setInputValue(selector, value) {
const element = document.querySelector(selector)
if (!element) return console.error('Element not found:', selector)
if (element instanceof HTMLTextAreaElement) {
nativeTextAreaValueSetter.call(element, value)
} else if (element instanceof HTMLInputElement) {
nativeInputValueSetter.call(element, value)
}
element.dispatchEvent(new Event('input', { bubbles: true }))
element.dispatchEvent(new Event('change', { bubbles: true }))
}
// 点击按钮
function clickButton(selector) {
const element = document.querySelector(selector)
if (!element) return console.error('Element not found:', selector)
element.scrollIntoView({ block: 'center' })
element.focus()
element.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }))
element.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }))
element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
}
// 下拉框选择
function selectOption(selector, optionText) {
const select = document.querySelector(selector)
if (!select || !(select instanceof HTMLSelectElement)) return
const option = Array.from(select.options).find(
opt => opt.textContent?.trim() === optionText.trim()
)
if (option) {
select.value = option.value
select.dispatchEvent(new Event('change', { bubbles: true }))
}
}
// 使用示例
setTimeout(() => {
setInputValue('#username', 'test@example.com')
setInputValue('#password', 'mypassword')
selectOption('#country', 'China')
clickButton('#submit-btn')
}, 1000)
})()方案1:输入框赋值(使用原生 setter)
// 获取原生的 value setter
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
).set
const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype,
'value'
).set
// 正确的赋值方式
function setInputValue(element, value) {
// 1. 使用原生 setter 设置值(绕过框架重写)
if (element instanceof HTMLTextAreaElement) {
nativeTextAreaValueSetter.call(element, value)
} else {
nativeInputValueSetter.call(element, value)
}
// 2. 触发 input 事件,让框架感知到变化
element.dispatchEvent(new Event('input', { bubbles: true }))
// 3. 如果是 select 下拉框
if (element instanceof HTMLSelectElement) {
element.dispatchEvent(new Event('change', { bubbles: true }))
}
}
方案2:按钮点击(完整事件序列)
async function clickElement(element) {
// 1. 滚动到可见区域
element.scrollIntoView({ behavior: 'auto', block: 'center' })
await sleep(100)
// 2. 模拟完整的鼠标事件序列
element.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true, cancelable: true }))
element.dispatchEvent(new MouseEvent('mouseover', { bubbles: true, cancelable: true }))
element.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }))
// 3. 聚焦元素
element.focus()
element.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }))
element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
await sleep(200)
}
为什么这套方案可行?
问题 原因 解决方案原理 输入框赋值无效 框架重写了 value setter 来追踪变化,直接赋值绕过了框架 原生 setter 直接操作 DOM 底层,然后 dispatchEvent 通知框架 按钮点击无效 框架可能使用事件委托或需要完整交互流程 完整事件序列 模拟真实用户行为, bubbles: true 确保事件冒泡到父元素
关键点解释
Object.getOwnPropertyDescriptor 获取原生 setter
在页面加载时,框架会重写 HTMLInputElement.prototype.value 的 setter。我们通过这个 API 获取未被重写的原生 setter。bubbles: true 事件冒泡
React 等框架使用事件委托,事件监听器绑定在 document 或根节点上。 bubbles: true 确保事件能冒泡到这些监听器。完整事件序列
真实用户点击会触发: mouseenter → mouseover → mousedown → focus → mouseup → click 。某些按钮可能监听 mousedown 而非 click 。
License:
晋ICP备16009994号-1