Preferred Alternative to OnMouseOver for touch

前端 未结 5 1762
终归单人心
终归单人心 2020-11-28 10:16

Is there a preferred alternative or best practice for handling OnMouseOver javascript events on touch devices? All I can think is to convert all events to OnMouseClick. Un

5条回答
  •  南方客
    南方客 (楼主)
    2020-11-28 11:12

    Unfortunately I do not know about best practices or a preferred alternative to onmouseover on touch-devices, but in encountering the same question I ended up developing this vanillaJS solution where I count the milliseconds between onmouseenter and onclick, and am therefore able to distinguish between desktop-clicking and mobile-clicking.

    Upon investigating the two events in the desktop vs. mobile environments, I discovered that a mobile touch natively fires both events instantly (both within zero milliseconds), in contrast to the small desktop-delay of a few dozen milliseconds, depending on the user's trigger-happiness.

    ;(function(){
    	let
    		hover_on_mobile = {
    			timer: 0,
          // I don't trust the timer with this,
          // so I'm counting myself:
    			milliseconds: 0,
          // also cover the case of the user
          // mouseentering, reading or otherwise
          // waiting, and clicking *then*.
    			is_counting: false,
    		},
        item = document.querySelector('.mobile-hoverable')
    	;
    	hover_on_mobile.reset = function(){
    		clearInterval(hover_on_mobile.timer);
    		hover_on_mobile.milliseconds = 0;
    		hover_on_mobile.is_counting = false;
    	};
    
    	// hover.
    	item.onmouseenter = function(){
    
    		// preparation for onclick's touch-click detection.
    		hover_on_mobile.is_counting = true;
    		// count the milliseconds starting on each 
        // mouseenter anew.
    		hover_on_mobile.timer = window.setInterval(function() {
    			// we only need the first few milliseconds for
          // our touch-click detection.
    			if (hover_on_mobile.milliseconds > 50) {
    				hover_on_mobile.reset();
    
    			} else {
    				hover_on_mobile.milliseconds++;
    			}
    		}, 1);
    
    		hover_behavior();
    	};
    
    	// click.
    	item.onclick = function(ev){
    		let
    			condition1 = hover_on_mobile.milliseconds < 10,
    			condition2 = hover_on_mobile.is_counting
    		;
    		console.log('clicked', {
    			condition1: condition1,
    			condition2: condition2,
    			timer: hover_on_mobile.timer,
          milliseconds: hover_on_mobile.milliseconds,
    			is_counting: hover_on_mobile.is_counting,
    		});
    		// touch-click detection.
    		if (condition1 && condition2) {
    			// don't do anything; let the onmouseenter 
          // do the hover routine unhinderedly.
          //
    			// if this was an onclick event on an ‹a› tag, 
          // the ev.preventDefault(); call would go here.
          
    		} else {
    			click_behavior();
    		}
    		hover_on_mobile.reset();
    	};
      
      
      // ----------------------------------------
      // fiddle-specfic.
    
    	// reset indicator, not hover_on_mobile.
    	item.onmouseout = reset_indicator;
    
    	function click_behavior() {
    		document.querySelector('#indicator .click-text').innerText = 'clicked';
    	}
    
    	function hover_behavior() {
    		document.querySelector('#indicator .hover-text').innerText = 'hovered';
    	}
    
    	function reset_indicator() {
    		document.querySelector('#indicator .hover-text').innerText = '-';
    		document.querySelector('#indicator .click-text').innerText = '-';
    	}
    
    	document.querySelector('#indicator .reset').onclick = reset_indicator;
    
    })();
    h1 {
      font-size: 20px;
      line-height: 26px;
    }
    
    #indicator {
      margin-top: 15px;
      padding: 20px;
      background-color: #ffffd;
    }
    
    .mobile-hoverable {
      cursor: pointer;
      background-color: antiquewhite;
      border: 1px outset blanchedalmond;
      border-radius: 4px;
      padding: 10px;
    }
    
    .notes {
      font-style: italic;
      font-size: 14px;
    }

    Imagine you wanted mobile users to click once in order to simulate a desktop-hover and twice for a desktop-click

    Hover me, click me, compare with mobile-touch device mode.
    - -
    • Don't forget to reload the page after changing the mode, for optimal testing evaluation.
    • Click event console.logs hover_on_mobile object.
    • The fiddle's CSS is irrelevant for this feature.
    • Relevant JavaScript is bundled together; irrelevant JavaScript at the end.
    • Didn't test browser compatibility specifically for this fiddle but the feature works in Chrome, Firefox, Safari, IE10+.
    • Subsequent clicks without onmouseout will only fire the click event, in both environments.

    (… or alternatively as a fiddle)

    Here is another fiddle to showcase specifically the timing difference between desktop and mobile environments.

提交回复
热议问题