最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React: onClick on Mobile (iPhone) requires 2 taps? - Stack Overflow

programmeradmin2浏览0评论

Video demonstrating issue

I have a bunch of clickable ponents that, when clicked, adds a "card" to a row. On desktop, it works fine, but on mobile (tested on iPhone, does not seem to be an issue for Android tablet), it requires 2 consecutive taps of the same button to fire the onClick function.

These ponents also have onMouseEnter/onMouseLeave effects on them, to control a global state, which in turn decides if several ponents should have additional CSS applied (so I can't make it a simple CSS hover effect).

I believe that the mouse effects are interfering with the click event, but I have no idea how I could fix that. Here is the relevant code for this ponent:

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

Furthermore, once a button has been tapped twice, the hover effect CSS "sticks" to that button, and never moves to another button. This seems to happen on both iPhone and Android tablet. I would love to have this not happen anymore either.

I've created a working demonstration of this issue in a sandbox, which if viewed on mobile you should be able to recreate these issues: =/src/Components/CardSource/CardSource.js

Video demonstrating issue

I have a bunch of clickable ponents that, when clicked, adds a "card" to a row. On desktop, it works fine, but on mobile (tested on iPhone, does not seem to be an issue for Android tablet), it requires 2 consecutive taps of the same button to fire the onClick function.

These ponents also have onMouseEnter/onMouseLeave effects on them, to control a global state, which in turn decides if several ponents should have additional CSS applied (so I can't make it a simple CSS hover effect).

I believe that the mouse effects are interfering with the click event, but I have no idea how I could fix that. Here is the relevant code for this ponent:

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

Furthermore, once a button has been tapped twice, the hover effect CSS "sticks" to that button, and never moves to another button. This seems to happen on both iPhone and Android tablet. I would love to have this not happen anymore either.

I've created a working demonstration of this issue in a sandbox, which if viewed on mobile you should be able to recreate these issues: https://codesandbox.io/s/mobile-requires-2-taps-i9zri?file=/src/Components/CardSource/CardSource.js

Share Improve this question edited May 19, 2020 at 18:22 damon asked May 18, 2020 at 22:13 damondamon 2,8978 gold badges28 silver badges35 bronze badges
Add a ment  | 

5 Answers 5

Reset to default 2

Probably the problem with your code is, the mouse events you're using are non-bubbling. e.g. mouseenter event.

You might want to try with an event bubbling solution using onMouseOver instead of onMouseEnter, and onMouseOut instead of 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>
  );
};

Should the above NOT work, you could debug this with event type and performing event handling based on it. e.g.

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

Also note that, providing arrow functions as props creates new instance of the function on every render. So better use bind in that case or just function references that capture the arguments.

I think I've found the problem when I use an onClick & onMouseEnter & onMouseLeave then test in the browser in mobile mode the onMouseEnter and onClick event fire with the first onClick, you can add a console log to all your events and see the same behavior. The CSS style is staying because the DOM thinks that your element still has the hover attribute. If you click off of the element in question, you will see your onMouseLeave event fire, your css will reset but the element will require two clicks again. I'm not sure what the solution is, or if its even a problem testing on an actual mobile device.

EDIT: A solution I found, is only using the onMouseEnter & onMouseLeave event, since this event fires onClick for mobile and I only want the hover effect on desktop the oute is what I was after.

EDIT EDIT: To maintain accessibility with the keyboard I added an onKeyDown event to open/close the dropdown button (which is what I was working on)

onKeyDown={(event) => {
          if (event.keyCode == 13) {
            setShowChildren(showChildren === "hide" ? "show" : "hide");
          }
        }}

You could just use css hover rather than adding a class via onMouseEnter event, It fixes the two taps issue.

Link to sandbox

If you were to programatically use trigger for hover. You could solve the two clicks issue by using onTouchEnd event (mented in sandbox).

Hope that helps.

I remember having a similar problem. The issue was for me that the state of ponent does not change immediately, but only upon execution of the render() method. I believe you might have the same issue with asynchronicity for both effects you are describing.

The only render() call I see found in your code is in App.test.js, I usually place that in the respective ponent.tsx.

References

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

Use events onTouchStart, onTouchMove, onTouchEnd for calculate touch.

发布评论

评论列表(0)

  1. 暂无评论