阻止单击一次 span 元素上的 onClick 事件

Posted

技术标签:

【中文标题】阻止单击一次 span 元素上的 onClick 事件【英文标题】:Prevent an onClick event on a span element when it was clicked once 【发布时间】:2021-02-24 19:26:17 【问题描述】:

我正在构建一个游戏,您必须在其中单击十字象形图或检查象形图,具体取决于答案是正确还是错误。但是一旦你回答了你就不能再点击这两个象形图中的任何一个(即使是你没有点击的那个)。

为了做到这一点,我在包含象形图的元素上使用了 useRef,并尝试禁用该元素。它没有用。 我也试图让他们的父元素“不可改变”,但它也没有工作

我将不胜感激。谢谢

const pictoAnswer = useRef()
const divParent = useRef()

pictoAnswer.current.disabled = true
divParent.current.pointerEvents = "none"

全貌

import React,  useState, useRef  from "react";

// modules
import  FontAwesomeIcon  from "@fortawesome/react-fontawesome";
import  faCheck, faTimes  from "@fortawesome/free-solid-svg-icons";

const WordList = () => 
  const liBgColor = useRef();
  const pictoAnswer = useRef();
  const divParent = useRef();

  // change the color bg of the list element
  const isCorrect = (answer) => 
    if (answer) 
      liBgColor.current.style.backgroundColor = "#5DCF36";
     else 
      liBgColor.current.style.backgroundColor = "#d15f5f";
    
  ;

  //list of word to make guess
  let wordsToGuess = ["chambre", "salon", "rire", "fusée"];
  const numberOfWordToGuess = wordsToGuess.length;

  const [indexWord, setIndexWord] = useState(1);
  console.log("indexWord:", indexWord);
  const [wordToDisplay, setWordToDisplay] = useState([wordsToGuess[0]]);

 
  // push the next word to guess into the wordToDisplay array
  const handleAnswer = (answer) => 
   

    pictoAnswer.current.disabled = true;
    divParent.current.pointerEvents = "none";
    //set the new index of the word item
    setIndexWord(indexWord + 1);
    //change the bgColor depending on the answer
    isCorrect(answer);
    //would display the next word or not
    if (indexWord == numberOfWordToGuess || indexWord > numberOfWordToGuess) 
      console.log("no more word to guess");
     else 
      setWordToDisplay([...wordToDisplay, wordsToGuess[indexWord]]);
    
  ;

  return (
    <ul className="word__list">
      wordToDisplay.map((word, i) => (
        <li key=i className="word__item" ref=liBgColor>
          <p className="word">word</p>
          <div className="icon-box" ref=divParent>
            <span
              ref=pictoAnswer
              onClick=() => 
                handleAnswer(false);
              
            >
              <FontAwesomeIcon icon=faTimes color="#FF5252" />
            </span>
            <span
              ref=pictoAnswer
              onClick=() => 
                handleAnswer(true);
              
            >
              <FontAwesomeIcon icon=faCheck color="#008000" />
            </span>
          </div>
        </li>
      ))
    </ul>
  );
;

export default WordList;

codeSandBox

【问题讨论】:

document.querySelector("span").addEventListener("click", function(e) e.preventDefault(); return false; , once : true); 没有足够的信息可以确定,但是您至少应该检查带有图片的组件没有被卸载和重新安装,因为这将重置其禁用/启用状态。 不应该阻止进一步的点击注册,如果它被回答,你不应该传递任何 onClick 处理程序,所以点击不会做任何事情。或者,您可以检查处理程序中的已回答状态,如果为真,则退出任何进一步的操作。 你为什么不使用语义按钮(带有disabled 属性)?您的 React ref 用法似乎不正确,因为只有最后一个映射的 word 元素附加了 ref,您甚至在同一渲染中分配了两次 pictoAnswer ref(!!)。 indexWord 不会是 handleAnswer 中的增量值,但也许这没问题 (???) 虽然我怀疑不是因为您稍后尝试推送一个新单词以显示 @987654329 @。当您应该使用 classNamestyle 设置任何样式(而不是 DOM 操作)时,似乎误用了 React refs。 您能否为此代码创建一个 running 代码框,以便我们更好地了解您要完成的工作? 【参考方案1】:

您的代码的主要问题是不正确地使用 React refs 来操作 DOM 元素。

解决方案

    存储一个初始化为空值的“已解决”状态数组,以指示单词是否已被标记为真/假。相同的条件用于防止进一步的“点击”处理。 使用 CSS 设置列表项的样式。 更新handleAnswer 以设置“已解决”状态并增加单词索引。 使用useEffect 挂钩更新要显示的字词。

代码:

const wordsToGuess = ["chambre", "salon", "rire", "fusée"];

const App = () => 
  const [indexWord, setIndexWord] = useState(0);
  const [wordToDisplay, setWordToDisplay] = useState([]);
  const [settled, setSettled] = useState(Array(wordsToGuess.length).fill(null));

  useEffect(() => 
    setWordToDisplay(wordsToGuess.slice(0, indexWord + 1));
  , [indexWord]);

  const handleAnswer = (index, answer) => () => 
    if (settled[index] === null) 
      setSettled((settled) =>
        settled.map((el, i) => (i === indexWord ? answer : el))
      );
      if (indexWord < wordsToGuess.length - 1) setIndexWord((i) => i + 1);
    
  ;

  return (
    <ul className="word__list">
      wordToDisplay.map((word, i) => (
        <li
          key=i
          className=classNames("word__item", 
            "settled-true": settled[i] === true,
            "settled-false": settled[i] === false
          )
        >
          <p className="word">word</p>
          <div className="icon-box">
            <span onClick=handleAnswer(i, false)>
              <FontAwesomeIcon icon=faTimes color="#FF5252" />
            </span>
            <span onClick=handleAnswer(i, true)>
              <FontAwesomeIcon icon=faCheck color="#008000" />
            </span>
          </div>
        </li>
      ))
    </ul>
  );
;

CSS:

.settled-true 
  background-color: #5dcf36;


.settled-false 
  background-color: #d15f5f;

演示

【讨论】:

以上是关于阻止单击一次 span 元素上的 onClick 事件的主要内容,如果未能解决你的问题,请参考以下文章

a子元素中,其中一个绑定点击事件,点击其他子元素进行页面跳转。当点击这个子元素,怎么阻止a标签跳转。

React - 带有 onBlur 事件的 ul 阻止 onClick 在 li 上触发

使用时间器区别网页上的单击和双击

仍在应用移动设备悬停状态上元素上的onClick事件

click和onclick本质的区别

如何阻止 TinyMCE 用 span 替换已弃用的标签