在反应元素上模拟点击事件

Simulate click event on react element

我正在尝试在 React 元素上模拟 .click() event 但我无法弄清楚为什么它不起作用(当我发射 event).

我想 post 仅使用 JavaScript 的 Facebook 评论,但我卡在了第一步(在 div[class="UFIInputContainer"] 元素上执行 .click())。

我的代码是:

document.querySelector('div[class="UFIInputContainer"]').click();

这里是 URL 我正在尝试这样做的地方:https://www.facebook.com/plugins/feedback.php...

P.S。我对 React 没有经验,我真的不知道这在技术上是否可行。可能吗?

编辑:我正在尝试从 Chrome DevTools Console 执行此操作。

使用refs回调函数获取元素,使用click()函数触发点击

class Example extends React.Component{
  simulateClick(e) {
    e.click()
  }
  render(){
    return <div className="UFIInputContainer"
    ref={this.simulateClick} onClick={()=> console.log('clicked')}>
      hello
      </div>
  }
}

ReactDOM.render(<Example/>, document.getElementById('app'))
<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>

从以前的解决方案中得到启发,并使用一些 javascript 代码注入,也可以首先 inject React 到页面中, 然后在该页面元素上触发点击事件。

let injc=(src,cbk) => { let script = document.createElement('script');script.src = src;document.getElementsByTagName('head')[0].appendChild(script);script.onload=()=>cbk() }
injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js",() => injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js",() => {

class ReactInjected extends React.Component{
  simulateClick(e) {
    e.click()
  }
  render(){
    return <div className="UFIInputContainer"
    ref={this.simulateClick} onClick={()=> console.log('click injection')}>
      hello
      </div>
  }
}
ReactDOM.render(<ReactInjected/>, document.getElementById('app'))

} ))
<div id="app"></div>

React 跟踪 mousedownmouseup 事件来检测鼠标点击,而不是像大多数其他事件一样跟踪 click 事件。因此,不是直接调用 click 方法或调度 click 事件,而是必须调度 down 和 up 事件。为了更好的衡量,我也发送了 click 事件,但我认为这对 React 来说是不必要的:

const mouseClickEvents = ['mousedown', 'click', 'mouseup'];
function simulateMouseClick(element){
  mouseClickEvents.forEach(mouseEventType =>
    element.dispatchEvent(
      new MouseEvent(mouseEventType, {
          view: window,
          bubbles: true,
          cancelable: true,
          buttons: 1
      })
    )
  );
}

var element = document.querySelector('div[class="UFIInputContainer"]');
simulateMouseClick(element);

此答案的灵感来自 Selenium Webdriver code

对于 React 16.8,我会这样做:

const Example = () => {
  const inputRef = React.useRef(null)
        
  return (
    <div ref={inputRef} onClick={()=> console.log('clicked')}>
      hello
    </div>
  )
}
    

只需调用

inputRef.current.click()

使用 React useRef Hooks,您可以像这样在任何按钮上触发点击事件:

export default const () => {

    // Defining the ref constant variable
    const inputRef = React.useRef(null);

    // example use
    const keyboardEvent = () => {
      inputRef.current.handleClick(); //Trigger click
    }

    // registering the ref
    return (
       <div ref={inputRef} onClick={()=> console.log('clicked')}>
          hello
       </div>
    )
}

对@carlin.scott 的模拟 mousedownmouseupclick 的出色回答进行了微调,就像在真正的鼠标单击期间发生的那样(否则React 没有检测到它)。

这个答案在 mousedownmouseup 事件之间添加了一个轻微的停顿以增加真实感,并以正确的顺序排列事件(click 最后触发)。暂停使其异步,这可能是不可取的(因此我不只是建议编辑@carlin.scott的答案)。

async function simulateMouseClick(el) {
  let opts = {view: window, bubbles: true, cancelable: true, buttons: 1};
  el.dispatchEvent(new MouseEvent("mousedown", opts));
  await new Promise(r => setTimeout(r, 50));
  el.dispatchEvent(new MouseEvent("mouseup", opts));
  el.dispatchEvent(new MouseEvent("click", opts));
}

用法示例:

let btn = document.querySelector("div[aria-label=start]");
await simulateMouseClick(btn);
console.log("The button has been clicked.");

如果您没有在组件中定义 class,而是只声明:

function App() { ... }

在这种情况下,您只需要设置 useRef 挂钩并将其用于 point/refer 任何 html 元素,然后将引用用于定期触发 dom-events.

import React, { useRef } from 'react';

function App() {
  const inputNameRef = useRef()
  const buttonNameRef = useRef()
  
  function handleKeyDown(event) {
    // This function runs when typing within the input text,
    // but will advance as desired only when Enter is pressed
    if (event.key === 'Enter') {
      // Here's exactly how you reference the button and trigger click() event,
      // using ref "buttonNameRef", even manipulate innerHTML attribute
      // (see the use of "current" property)
      buttonNameRef.current.click()
      buttonNameRef.current.innerHTML = ">>> I was forced to click!!"
    }
  }
  
  function handleButtonClick() {
    console.log('button click event triggered')
  }
  
  return (
    <div>
      <input ref={inputNameRef} type="text" onKeyDown={handleKeyDown}  autoFocus />
      <button ref={buttonNameRef} onClick={handleButtonClick}>
      Click me</button>
    </div>
  )
}

export default App;

有点肮脏的黑客攻击,但这个对我来说效果很好,而这个 post 之前的建议都失败了。您必须找到在源代码中定义了 onClick 的元素(为此,我必须 运行 移动模式下的网站)。该元素将有一个 __reactEventHandlerXXXXXXX 道具,允许您访问反应事件。

let elem = document.querySelector('YOUR SELECTOR');
//Grab mouseEvent by firing "click" which wouldn't work, but will give the event
let event;
likeBtn.onclick = e => {
  event = Object.assign({}, e);
  event.isTrusted = true; //This is key - React will terminate the event if !isTrusted
};
elem.click();
setTimeout(() => {
  for (key in elem) {
    if (key.startsWith("__reactEventHandlers")) {
      elem[key].onClick(event);
    }
  }
}, 1000);