在React元素上模拟点击事件

56

我想在一个React元素上模拟一个.click()事件,但是我无法弄清楚为什么它不起作用(当我触发这个event时没有反应)。

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

我的代码如下:

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

这是我尝试做的 URL:https://www.facebook.com/plugins/feedback.php...

附注:我对React没有经验,不确定技术上是否可行。这是可能的吗?

编辑:我正在尝试从Chrome DevTools控制台中执行此操作。


2
可能是从非按钮元素触发按钮点击的重复问题。 - Tschallacka
@Tschallacka的回答(https://dev59.com/N2w15IYBdhLWcg3w1vSB#6367609)似乎可以工作,但不幸的是我没有得到任何结果。 - Alfonso
10个回答

75

为了检测鼠标点击,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代码


1
这很聪明,谢谢。 - Ahmad
1
这个解决方案之前一直很好用,但最近出了问题。@information_interchange,你最终找到了替代方案吗? - HoldTheLine
1
如果你们能提供导致这个问题的 React 版本,那么我可以借此解决该问题。 - carlin.scott
最新版本的Firefox不起作用 :/ - throrin19
非常出色的工作 :-) 非常感谢,值得奖励。 - ajaykools
显示剩余4条评论

48

使用 React 16.8 版本,我会像这样处理:

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

只需要简单地调用

inputRef.current.click()

@tam,你是指inputRef.current.handleClick()吗? - Gajen
@Gajen 是的。看起来我打错了一个字。 - tam
这是正确且最佳的解决方案。谢谢你,你救了我的一天 :) - Francesco Orsi
这是我个人认为最好的答案。 - Thomas Taylor
4
当我使用它时,它点击一次后会出现“无法读取null的click属性”的错误。 - E Alexis MT

21

使用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>


8
e.click 不是一个函数。 - Mote Zart
1
它应该是 e.target.click()。 - toing_toing
这对我没有用,但是 e.props 包含映射到元素上的任何函数。在我的实例中,它是一个带有 onChange 的复选框,因此在 simulateClicke.props.onChange() 完美地工作。 - Michael Aaron Wilson

6

对@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.");

请注意可能需要页面焦点才能工作,因此在控制台中执行可能无效,除非您打开Chrome DevTools的Rendering选项卡并选中“在DevTools打开时模拟页面焦点”复选框。

2
这对我似乎没有起作用,也许现在React检测到它的方式不同了? - Joseph Astrahan
对我来说,它确实有效。 - undefined

5
如果您在组件中没有定义类(class),而是只声明了如下内容:
function App() { ... }

在这种情况下,你只需要设置useRef钩子并使用它来指向/引用任何HTML元素,然后使用该引用来触发常规DOM事件。
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;

4

这有点不太正规的操作,但是对我来说很有效,而此帖子中以前的建议都失败了。你需要在源代码中找到具有 onClick 定义的元素(我必须在移动模式下运行网站才能找到它)。该元素将具有一个 __reactEventHandlerXXXXXXX 属性,允许您访问 React 事件。

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

  

这可能是一个愚蠢的问题,但你是如何在“移动”模式下运行它的? - Joseph Astrahan
对我来说,它在__reactProps下而不是__reactEventHandlers下。但是即使以这种方式激活onClick,似乎也无法完全工作,或者至少不完全。 - Joseph Astrahan
好的,我弄明白了,看起来你需要修改事件并添加一个名为nativeEvent = {detail:1}的属性,你的策略现在运行得很好! - Joseph Astrahan
这段代码在控制台中可以运行,但在实际代码中无法运行。我该如何通过代码找到__reactProps或__reactEventHandlers? - Joseph Astrahan
不确定,我已经放弃了这段代码... - Gilad Barner
我现在已经弄明白了,谢谢! - Joseph Astrahan

3
受到之前解决方案的启发,并使用一些 JavaScript 代码注入,也可以先将 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>


1
使用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>
    )
}

1

这个答案受到了carlin.scott代码的启发。 然而,在我的情况下,它只能使用focusin事件。

const element = document.querySelector('element')
const events = ['mousedown', 'focusin']

events.forEach(eventType =>
    element.dispatchEvent(
        new MouseEvent(eventType, { bubbles: true })
    )
)

0
实际上,我们说“聚焦输入”而不是“点击输入”(从技术角度讲)。而你这里使用的是 div[class="UFIInputContainer"],它是一个输入框,而不是按钮!(下图)

enter image description here

在React函数式组件中聚焦元素

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  const textInput = useRef(null);
  
  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

要查找此示例以及更多内容,请检查 文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接