反应:移动(iPhone)上的onClick需要2次点击?

Posted

技术标签:

【中文标题】反应:移动(iPhone)上的onClick需要2次点击?【英文标题】:React: onClick on Mobile (iPhone) requires 2 taps? 【发布时间】:2020-05-18 22:13:10 【问题描述】:

Video demonstrating issue

我有一堆可点击的组件,当点击这些组件时,会在一行中添加一个“卡片”。在台式机上,它工作正常,但在移动设备上(在 iPhone 上测试,似乎对 android 平板电脑不是问题),它需要连续点击同一按钮 2 次才能触发 onClick 功能。

这些组件还具有onMouseEnter/onMouseLeave 效果,以控制全局状态,进而决定是否应应用多个组件附加 CSS(因此我无法将其设为简单的 CSS 悬停效果) .

我相信鼠标效果会干扰点击事件,但我不知道如何解决这个问题。下面是这个组件的相关代码:

const CardSource = ( addCard, note, setHoveredNote, hoveredNote ) => 
  return (
    <Source
      onClick=() => addCard(note)
      onMouseEnter=() => setHoveredNote(note)
      onMouseLeave=() => setHoveredNote(null)
      className=
        hoveredNote && hoveredNote.index === note.index ? "highlight" : null
      
    >
      note.letters[0]
    </Source>
  );
;

此外,一旦按钮被点按两次,悬停效果 CSS 就会“粘”在该按钮上,并且永远不会移动到另一个按钮。这似乎发生在 iPhone 和 Android 平板电脑上。我也希望不再发生这种情况。

我在沙盒中创建了此问题的工作演示,如果在移动设备上查看,您应该能够重现这些问题:https://codesandbox.io/s/mobile-requires-2-taps-i9zri?file=/src/Components/CardSource/CardSource.js

【问题讨论】:

【参考方案1】:

您的代码可能存在问题,您使用的鼠标事件不会冒泡。 e.g. mouseenter event.

您可能想尝试使用onMouseOver 代替onMouseEnteronMouseOut 代替onMouseLeave 的事件冒泡解决方案。

const CardSource = ( addCard, note, setHoveredNote, hoveredNote ) => 
  return (
    <Source
      onClick=() => addCard(note)
      onMouseOver=() => setHoveredNote(note)
      onMouseOut=() => setHoveredNote(null)
      className=
        hoveredNote && hoveredNote.index === note.index ? "highlight" : null
      
    >
      note.letters[0]
    </Source>
  );
;

如果上述方法不起作用,您可以使用事件类型进行调试并基于它执行事件处理。例如

const CardSource = ( addCard, note, setHoveredNote, hoveredNote ) => 
  const eventHandler = (event) => 
     const  type, bubbles  = event;
     switch(type) 
         case "mouseover":
         case "mouseenter":
              setHoveredNote(note);
              break;
         case "mouseout":
         case "mouseleave":
              setHoveredNote(null);
         case "click":
              addCard(note);
              if (bubbles)  // handle hover state
                 setHoveredNote(note);
              
              break;
         default:
             break;
     
  

  const onClick = (event) => eventHandler(event);
  const onMouseOver = (event) => eventHandler(event);
  const onMouseOut = (event) => eventHandler(event);

  return (
    <Source
      onClick=onClick
      onMouseOver=onMouseOver
      onMouseOut=onMouseOut
      className=
        hoveredNote && hoveredNote.index === note.index ? "highlight" : null
      
    >
      note.letters[0]
    </Source>
  );
;

另请注意,提供箭头函数作为道具会在每次渲染时创建该函数的新实例。所以最好在这种情况下使用 bind 或者只使用捕获参数的函数引用。

【讨论】:

我没有考虑冒泡,所以谢谢你提出这个问题。不幸的是,测试了OverOut 而不是EnterLeave,似乎没有区别(除了在方块内输入/离开字母时再次触发的函数)。我还使用了您的调试功能,有趣的是,当我为每个鼠标案例记录 bubbles 属性时 - overenteroutleave,它们都返回了 true,即使 @ 987654339@ 和leave 应该是false?也许那里有提示? 您也可能会考虑处理基于触摸的事件,因为我们试图处理的事件是鼠标事件。看看developer.mozilla.org/en-US/docs/Web/API/Touch_events 是的,我认为触摸事件可能需要成为解决方案。我唯一的问题是它们似乎没有得到很好的支持,并且在与悬停/单击事件同时使用时可能会导致一些问题。感谢您的回答和建议使用事件处理调试功能,非常有帮助【参考方案2】:

你可以只使用 css hover 而不是通过 onMouseEnter 事件添加一个类,它修复了两次点击问题。

Link to sandbox

如果您以编程方式使用触发器进行悬停。您可以通过使用onTouchEnd 事件(在沙盒中评论)来解决两次点击问题。

希望对您有所帮助。

【讨论】:

这绝对是在正确的轨道上,但有几个问题:1)我应该在帖子中提到它,但我不能使用简单的css悬停,因为它设置的状态得到传递给我真实项目中的多个组件,这些组件需要基于此更新 css。 2) onTouchEnd 确实可以在 iPhone 上单击 1 次,但 onTouchEnd 本身不允许点击,所以现在它不能在桌面上运行。 3) 同时激活onTouchEndonClick 使其在我的Android 平板电脑上触发两次(令人沮丧的是,不是在你的沙箱中,而是在我的真实项目中......) 另外,onTouchEnd 是否有任何文档?我之前搜索过触摸事件,但唯一提到的是这个页面:reactjs.org/docs/events.html,它根本没有提供任何相关信息 @damon - MDN Docs。如果 android 是最后一块拼图,您可以检测用户代理并有条件地应用 touchEnd(Hacky,我知道)。 看过所有答案后,我想我必须将touch 事件和用户代理检测结合起来。非常感谢您花时间提供解决方案。【参考方案3】:

我记得有一个类似的问题。对我来说,问题是组件的state 不会立即更改,而只会在执行render() 方法时更改。我相信你可能对你所描述的两种效果的异步性有同样的问题。

我在您的代码中看到的唯一 render() 调用位于 App.test.js 中,我通常将其放在相应的 component.tsx 中。

参考文献

Submit button takes 2 clicks to call function in React React.js events need 2 clicks to execute

【讨论】:

@damon 告诉我你的想法。如果我今天有时间,无论如何我都会更深入地研究你的代码。 抱歉耽搁了:关于render() 调用的注释,我相信我的代码示例中的主要render 函数在index.js 中。我不认为这里涉及test.js 文件。关于状态没有立即更新的说明,我不确定这与您的两个参考文献是否相同。我之前也遇到过同样的问题,但在这种情况下,状态在第一次点击时成功更新,但技术上应该更新 2 个:1 个用于悬停,1 个用于单击。它可以在台式机上运行,​​但不能在移动设备上运行,这让我认为这是两者之间的干扰。【参考方案4】:

使用事件onTouchStartonTouchMoveonTouchEnd 来计算触摸。

【讨论】:

以上是关于反应:移动(iPhone)上的onClick需要2次点击?的主要内容,如果未能解决你的问题,请参考以下文章

android 与 iphone 上的 jquery 移动事件

jQuery对话框上的Onclick事件不适用于IOS

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

移动端iPhone系列适配问题

多次渲染反应组件onClick

反应 onClick 事件处理程序没有触发......?