The reaction stops other events on this element.

I have a volume element that shows the volume panel when the user hangs on it. All this works great on the desktop. However, to obtain the same functionality on a mobile device, the user clicks on a volume element, which also switches the mute event.

I want to stop this event without sound when the user clicks (i.e. points to) this element on a mobile device.

I do not want to modify the Mute or VolumeBar classes to fix this, because these are the general classes in my library that the developer uses.

https://jsfiddle.net/jwm6k66c/2145/

  • Actual: the mute event is turned off and the volume panel opens.
  • Expected: the mute click event does not fire and the volume panel opens.

Open the console → go to the mobile view (CTRL + SHIFT + M on chrome) → press the volume button and view the console logs.

What I tried:

Using volumeControlOnClick to stop protagonizing when the row height of volume 0 is (I am not visible), this does not cancel onClick, though.

What I want:

Cancel the mute event if the user first clicks on the volume icon on the mobile device. Instead, it should only display the volume bar.

 const volumeControlOnClick = (e) => { const volumeBarContainer = e.currentTarget.nextElementSibling.querySelector('.jp-volume-bar-container'); /* Stop propogation is for mobiles to stop triggering mute when showing volume bar */ if (volumeBarContainer.clientHeight === 0) { e.stopPropagation(); console.log("stop propogation") } }; class Volume extends React.Component { constructor(props) { super(props); this.state = {}; } render() { return ( <div className="jp-volume-container"> <Mute onTouchStart={volumeControlOnClick}><i className="fa fa-volume-up" /></Mute> <div className="jp-volume-controls"> <div className="jp-volume-bar-container"> <VolumeBar /> </div> </div> </div> ); } }; class Mute extends React.Component { render() { return ( <button className="jp-mute" onClick={() => console.log("mute toggled")} onTouchStart={this.props.onTouchStart}> {this.props.children} </button> ); } }; class VolumeBar extends React.Component { render() { return ( <div className="jp-volume-bar" onClick={() => console.log("bar moved")}> {this.props.children} </div> ); } }; React.render(<Volume />, document.getElementById('container')); 
+6
source share
3 answers

Here's what I got. It works because onTouchStart always indicated before onClick , if it is a touch event, and if not, then the user logic will still be called. It also fires before guidance occurs. This saves the event: hover. e.preventDefault() not.

 let isVolumeBarVisible; const onTouchStartMute = e => ( isVolumeBarVisible = e.currentTarget.nextElementSibling .querySelector('.jp-volume-bar-container').clientHeight > 0 ); const onClickMute = () => () => { if (isVolumeBarVisible !== false) { // Do custom mute logic } isVolumeBarVisible = undefined; }; <Mute aria-haspopup onTouchStart={onTouchStartMute} onClick={onClickMute} > <i className="fa">{/* Icon set in css*/}</i> </Mute> 
+2
source

What you can do is use a flag indicating that you were in the touch event before being in the mouse event if you are using the bubble phase . Therefore, attach a listener to your container element as follows:

 let isTouch = false; const handleContainerClick = () => isTouch = false; const handleMuteClick = () => { if (isTouch == false) { console.log("mute toggled"); } }; const volumeControlOnClick = () => { isTouch = true; }; class Volume extends React.Component { constructor(props) { super(props); this.state = {}; } render() { return ( <div className="jp-volume-container" onClick={handleContainerClick}> <Mute onTouchStart={volumeControlOnClick} onClick={handleMuteClick}><i className="fa fa-volume-up" /></Mute> <div className="jp-volume-controls"> <div className="jp-volume-bar-container"> <VolumeBar /> </div> </div> </div> ); } }; class Mute extends React.Component { render() { return ( <button className="jp-mute" onTouchStart={this.props.onTouchStart} onClick={this.props.onClick}> {this.props.children} </button> ); } }; class VolumeBar extends React.Component { render() { return ( <div className="jp-volume-bar" onClick={() => console.log("bar moved")}> {this.props.children} </div> ); } }; render(<Volume />, document.getElementById('container')); 

If you are not using the bubble phase, so that you can register a timeout of 100 ms with the same logic above, where after 100 ms again make your flag variable false. Just add a touchStart handler:

 setTimeout(() => {isTouch = false}, 100); 

EDIT: Although touch events must be passive by default in Chrome 56, you call preventDefault () from the touchEnd event to prevent click firing. So, if you cannot modify the click handler of your Mute class in any way, but you can add a touchEnd event than you could:

 const handleTouchEnd = (e) => e.preventDefault(); const volumeControlOnClick = () => console.log("volumeControlOnClick"); class Volume extends React.Component { constructor(props) { super(props); this.state = {}; } render() { return ( <div className="jp-volume-container"> <Mute onTouchStart={volumeControlOnClick} onTouchEnd={handleTouchEnd}><i className="fa fa-volume-up" /></Mute> <div className="jp-volume-controls"> <div className="jp-volume-bar-container"> <VolumeBar /> </div> </div> </div> ); } }; class Mute extends React.Component { render() { return ( <button className="jp-mute" onTouchStart={this.props.onTouchStart} onTouchEnd={this.props.onTouchEnd} onClick={() => console.log("mute toggled")}> {this.props.children} </button> ); } }; class VolumeBar extends React.Component { render() { return ( <div className="jp-volume-bar" onClick={() => console.log("bar moved")}> {this.props.children} </div> ); } }; render(<Volume />, document.getElementById('container')); 
+1
source

try it

  render() { return ( <div className="jp-volume-container" onClick={handleContainerClick}> <Mute onTouchStart={volumeControlOnClick} onClick={handleMuteClick}><i className="fa fa-volume-up" /></Mute> <div className="jp-volume-controls"> <div className="jp-volume-bar-container"> <VolumeBar /> </div> </div> </div> ); } }; class Mute extends React.Component { render() { return ( <button className="jp-mute" onTouchStart={this.props.onTouchStart} onClick={this.props.onClick}> {this.props.children} </button> ); } }; class VolumeBar extends React.Component { render() { return ( <div className="jp-volume-bar" onClick={() => console.log("bar moved")}> {this.props.children} </div> ); } }; render(<Volume />, document.getElementById('container')); 
-one
source

Source: https://habr.com/ru/post/1015068/


All Articles