简体   繁体   中英

How to use same event handler for click and touchend events but only run one of them?

Here is my code for overlay controls I'm working on https://repl.it/repls/AdoredMundaneFinance#index.js . you can try interacting with it to see what functionality it offers.

I've come across an issue where on touch devices, event for touchend and click both are fired. Now Ive googled a lot,

a. I don't want to throttle due to user experience reasons. Tried and didn't like it.

b. Tried event.preventDefault() , it ensures only one event is used BUT it stops event from reaching the target children, ie child divs which are supposed to receive these clicks/taps, so can't use this.

c. I tried event.stopPropogation() but that is causing other issues, I even tried calling it only when event is cancelable, you can try and see what I'm trying to say.

d. I'm now trying to detect if the device is touch and based on that assign functions, but again I don't want to extend my problem to touch detection as well, as there is no "true" detection method, I don't want to risk that. What if someone is using mouse on touch device and click was being fired? etc etc.

Please Help.

react:

class App extends React.Component {
  constructor() {
    super();
    this.hideAfterXTimeout = null;
    this.state = {
      visible: false
    };
  }

  overlayRef(ref) {
    if (!this.overlay) {
      this.overlay = ref;
      this.overlay.addEventListener("click", this.clickTouchEffect.bind(this));
      this.overlay.addEventListener(
        "touchend",
        this.clickTouchEffect.bind(this)
      );
      let throttledHandler = this.throttle(300, this.mouseMoveEffect.bind(this));
            this.overlay.addEventListener(
        "mousemove",
        throttledHandler.bind(this)
      );
    }
  }

  mouseMoveEffect(event) {
    console.dir("called");
    this.showControls();
  }

  clickTouchEffect(event) {
    if (!event.target.hasAttribute("data-video")) {
      if (this.state.visible) {
        this.hideControls();
      } else {
        this.showControls();
      }
    } else {
      this.showControls();
    }
  }

  throttle(delay, fn) {
    let lastCall = 0;
    return function (...args) {
      const now = (new Date).getTime();
      if (now - lastCall < delay) {
        return;
      }
      lastCall = now;
      return fn(...args);
    }
  }

  showControls() {
    if(this.state.visible !== true){
      this.setState({
        visible: true
      });
    }
    if (this.hideAfterXTimeout) {
      clearTimeout(this.hideAfterXTimeout);
      this.hideAfterXTimeout = null;
    }
    let _me = this;
    this.hideAfterXTimeout = setTimeout(() => {
      _me.hideControls();
    }, 5000);
    this.overlay.classList.remove("hide");
  }

  hideControls() {
    this.setState({
      visible: false
    });
    if (this.hideAfterXTimeout) {
      clearTimeout(this.hideAfterXTimeout);
      this.hideAfterXTimeout = null;
    }
    this.overlay.classList.add("hide");
  }

  cc(){
    console.log("clicked");
  }

  render() {
    return (
      <div ref={this.overlayRef.bind(this)} className="overlay hide">
      <div className="visibility">
        <div onClick={this.cc.bind(this)} data-video="control" className="controls">
          CC
        </div>
      </div>
      </div>
    );
  }
}


ReactDOM.render(<App />, document.getElementById('app'));

css:

.App {
  font-family: sans-serif;
  text-align: center;
}

.overlay {
  width: 720px;
  height: 405px;
  outline: 1px solid red;

  opacity: 1;
  visibility: visible;
  transition: 0.3s ease-out opacity;
}

.overlay.hide {
  opacity: 0;
}

.overlay.hide > .visibility {
  visibility: hidden;
}

.overlay > .visibility {
  visibility: visible;
}

.controls {
  width: 64px;
  height: 64px;
  outline: 1px solid blue;

Here is my code for overlay controls I'm working on https://repl.it/repls/AdoredMundaneFinance#index.js . you can try interacting with it to see what functionality it offers.

I've come across an issue where on touch devices, event for touchend and click both are fired. Now Ive googled a lot,

a. I don't want to throttle due to user experience reasons. Tried and didn't like it.

b. Tried event.preventDefault() , it ensures only one event is used BUT it stops event from reaching the target children, ie child divs which are supposed to receive these clicks/taps, so can't use this.

c. I tried event.stopPropogation() but that is causing other issues, I even tried calling it only when event is cancelable, you can try and see what I'm trying to say.

d. I'm now trying to detect if the device is touch and based on that assign functions, but again I don't want to extend my problem to touch detection as well, as there is no "true" detection method, I don't want to risk that. What if someone is using mouse on touch device and click was being fired? etc etc.

Please Help.

react:

class App extends React.Component {
  constructor() {
    super();
    this.hideAfterXTimeout = null;
    this.state = {
      visible: false
    };
  }

  overlayRef(ref) {
    if (!this.overlay) {
      this.overlay = ref;
      this.overlay.addEventListener("click", this.clickTouchEffect.bind(this));
      this.overlay.addEventListener(
        "touchend",
        this.clickTouchEffect.bind(this)
      );
      let throttledHandler = this.throttle(300, this.mouseMoveEffect.bind(this));
            this.overlay.addEventListener(
        "mousemove",
        throttledHandler.bind(this)
      );
    }
  }

  mouseMoveEffect(event) {
    console.dir("called");
    this.showControls();
  }

  clickTouchEffect(event) {
    if (!event.target.hasAttribute("data-video")) {
      if (this.state.visible) {
        this.hideControls();
      } else {
        this.showControls();
      }
    } else {
      this.showControls();
    }
  }

  throttle(delay, fn) {
    let lastCall = 0;
    return function (...args) {
      const now = (new Date).getTime();
      if (now - lastCall < delay) {
        return;
      }
      lastCall = now;
      return fn(...args);
    }
  }

  showControls() {
    if(this.state.visible !== true){
      this.setState({
        visible: true
      });
    }
    if (this.hideAfterXTimeout) {
      clearTimeout(this.hideAfterXTimeout);
      this.hideAfterXTimeout = null;
    }
    let _me = this;
    this.hideAfterXTimeout = setTimeout(() => {
      _me.hideControls();
    }, 5000);
    this.overlay.classList.remove("hide");
  }

  hideControls() {
    this.setState({
      visible: false
    });
    if (this.hideAfterXTimeout) {
      clearTimeout(this.hideAfterXTimeout);
      this.hideAfterXTimeout = null;
    }
    this.overlay.classList.add("hide");
  }

  cc(){
    console.log("clicked");
  }

  render() {
    return (
      <div ref={this.overlayRef.bind(this)} className="overlay hide">
      <div className="visibility">
        <div onClick={this.cc.bind(this)} data-video="control" className="controls">
          CC
        </div>
      </div>
      </div>
    );
  }
}


ReactDOM.render(<App />, document.getElementById('app'));

css:

.App {
  font-family: sans-serif;
  text-align: center;
}

.overlay {
  width: 720px;
  height: 405px;
  outline: 1px solid red;

  opacity: 1;
  visibility: visible;
  transition: 0.3s ease-out opacity;
}

.overlay.hide {
  opacity: 0;
}

.overlay.hide > .visibility {
  visibility: hidden;
}

.overlay > .visibility {
  visibility: visible;
}

.controls {
  width: 64px;
  height: 64px;
  outline: 1px solid blue;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM