如何以编程方式填充使用 React 构建的输入元素?

IT技术 javascript reactjs automation web-crawler
2021-03-26 13:28:46

我的任务是抓取用 React 构建的网站。我正在尝试填写输入字段并使用 javascript 注入页面(移动中的 selenium 或 webview)提交表单。这在所有其他网站 + 技术上都像一种魅力,但 React 似乎是一种真正的痛苦。

所以这是一个示例代码

var email = document.getElementById( 'email' );
email.value = 'example@mail.com';

我的 DOM 输入元素上的值发生了变化,但 React 不会触发更改事件。

我一直在尝试多种不同的方法来让 React 更新状态。

var event = new Event('change', { bubbles: true });
email.dispatchEvent( event );

徒劳无功

var event = new Event('input', { bubbles: true });
email.dispatchEvent( event );

不工作

email.onChange( event );

不工作

我不敢相信与 React 交互变得如此困难。我将不胜感激任何帮助。

谢谢

3个回答

由于对重复数据删除输入和更改事件的更改,此已接受的解决方案似乎不适用于 React > 15.6(包括 React 16)。

你可以在这里看到 React 讨论:https : //github.com/facebook/react/issues/10135

和这里建议的解决方法:https : //github.com/facebook/react/issues/10135#issuecomment-314441175

为方便起见,转载于此:

代替

input.value = 'foo';
input.dispatchEvent(new Event('input', {bubbles: true}));

你会用

function setNativeValue(element, value) {
  const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
  const prototype = Object.getPrototypeOf(element);
  const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;

  if (valueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    valueSetter.call(element, value);
  }
}

接着

setNativeValue(input, 'foo');
input.dispatchEvent(new Event('input', { bubbles: true }));
您如何将其更改为只需单击即可?此外,React 似乎发生了变化,因此您现在需要发送一个“change”事件来代替
2021-05-30 13:28:46
似乎对我有用,但 Typescript 编译器对它一点都不满意:)
2021-06-10 13:28:46
它在直接在控制台中运行而不是从插件运行时有效(Object.getOwnPropertyDescriptor(element, 'value').set;失败)。
2021-06-18 13:28:46
这在greasemonkey中对我不起作用 - 可能与安全有关......即使只是尝试调用它也失败了Object.getOwnPropertyDescriptor(..)对我来说,使用setNativeValue(..)来自stackoverflow.com/a/52486921函数在greasemonkey 中工作。
2021-06-20 13:28:46

React 正在侦听input文本字段事件。

您可以更改该值并手动触发输入事件,react 的onChange处理程序将触发:

class Form extends React.Component {
  constructor(props) {
    super(props)
    this.state = {value: ''}
  }
  
  handleChange(e) {
    this.setState({value: e.target.value})
    console.log('State updated to ', e.target.value);
  }
  
  render() {
    return (
      <div>
        <input
          id='textfield'
          value={this.state.value}
          onChange={this.handleChange.bind(this)}
        />
        <p>{this.state.value}</p>
      </div>      
    )
  }
}

ReactDOM.render(
  <Form />,
  document.getElementById('app')
)

document.getElementById('textfield').value = 'foo'
const event = new Event('input', { bubbles: true })
document.getElementById('textfield').dispatchEvent(event)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id='app'></div>

当然我完全明白 - 事情是从 Selenium 进程调用事件不会触发,除非超时 - 你的例子现在与我在最初的问题中的例子非常相似,所以我很确定 selenium 中有一些东西弄乱 js 引擎的桥梁
2021-05-28 13:28:46
请注意,如果您有一个select字段,则需要调度change事件而不是input事件。
2021-05-30 13:28:46
仅供参考const event = new Event('input', { bubbles: true }) document.getElementById('textfield').dispatchEvent(event),为我解决了大量调试问题。谢谢你。
2021-06-03 13:28:46
谢谢,超时似乎可以解决问题 - 我不太明白为什么会这样 - 但是如果我在没有超时的情况下(来自 selenium)做同样的事情,React 永远不会得到这个事件。我确实在使用超时时遇到了一些问题,所以我必须找到解决方法。
2021-06-04 13:28:46
超时不是必需的,我添加它只是为了使示例更清晰。查看没有超时的更新。您唯一需要确保的是,更改输入值的代码是在 react 组件完全渲染后执行的。
2021-06-16 13:28:46

这是输入,选择,复选框等的最干净的解决方案(不仅适用于react输入)

/**
 * See [Modify React Component's State using jQuery/Plain Javascript from Chrome Extension](https://stackoverflow.com/q/41166005)
 * See https://github.com/facebook/react/issues/11488#issuecomment-347775628
 * See [How to programmatically fill input elements built with React?](https://stackoverflow.com/q/40894637)
 * See https://github.com/facebook/react/issues/10135#issuecomment-401496776
 *
 * @param {HTMLInputElement | HTMLSelectElement} el
 * @param {string} value
 */
function setNativeValue(el, value) {
  const previousValue = el.value;

  if (el.type === 'checkbox' || el.type === 'radio') {
    if ((!!value && !el.checked) || (!!!value && el.checked)) {
      el.click();
    }
  } else el.value = value;

  const tracker = el._valueTracker;
  if (tracker) {
    tracker.setValue(previousValue);
  }

  // 'change' instead of 'input', see https://github.com/facebook/react/issues/11488#issuecomment-381590324
  el.dispatchEvent(new Event('change', { bubbles: true }));
}

用法:

setNativeValue(document.getElementById('name'), 'Your name');
document.getElementById('radio').click(); // or setNativeValue(document.getElementById('radio'), true)
document.getElementById('checkbox').click(); // or setNativeValue(document.getElementById('checkbox'), true)
谢谢,这符合我对使用 ReactJS 的自动完成和组合框字段的需求。:)
2021-06-01 13:28:46