Safari iPad : prevent zoom on double-tap

前端 未结 8 1457
独厮守ぢ
独厮守ぢ 2020-12-05 12:09

I\'m creating a site on Safari for iPad. I need to prevent the zoom on double-tapping event but I have two problems:

  • a double tap doesn’t generate any events,
相关标签:
8条回答
  • 2020-12-05 12:21

    Here's a jQuery plugin I wrote for the same purpose - selectively disabling double-tap zoom on given page elements (in my case, navigation buttons to flip pages) I want to respond to every tap (including double-tap) as a normal click event, with no iOS "touch magic".

    To use it, just run something like $('.prev,.next').nodoubletapzoom(); on the elements you care for. (Edit: now also ignores pinches)

    // jQuery no-double-tap-zoom plugin
    
    // Triple-licensed: Public Domain, MIT and WTFPL license - share and enjoy!
    
    (function($) {
      var IS_IOS = /iphone|ipad/i.test(navigator.userAgent);
      $.fn.nodoubletapzoom = function() {
        if (IS_IOS)
          $(this).bind('touchstart', function preventZoom(e) {
            var t2 = e.timeStamp
              , t1 = $(this).data('lastTouch') || t2
              , dt = t2 - t1
              , fingers = e.originalEvent.touches.length;
            $(this).data('lastTouch', t2);
            if (!dt || dt > 500 || fingers > 1) return; // not double-tap
    
            e.preventDefault(); // double tap - prevent the zoom
            // also synthesize click events we just swallowed up
            $(this).trigger('click').trigger('click');
          });
      };
    })(jQuery);
    
    0 讨论(0)
  • 2020-12-05 12:21

    I modified @ecmanaut's javascript solution to do two things.

    1. I use modernizr, so it'll put a .touch css class on the html node, so I can detect touch screens instead of using user agent detection. Perhaps there is a "modernizr-less" approach, but I don't know it.
    2. I separate each "click" so they happen separately, if you click once, it'll click once, if you rapidly click the button/trigger, it'll count multiple times.
    3. some minor code formatting changes, I prefer each var defined separately etc, this is more a "me" thing than anything else, I suppose you could just revert that, nothing bad will happen.

    I believe these modifications make it better, cause you can increment a counter 1,2,3,4 instead of 2,4,6,8

    here is the modified code:

    //  jQuery no-double-tap-zoom plugin
    //  Triple-licensed: Public Domain, MIT and WTFPL license - share and enjoy!
    //
    //  chris.thomas@antimatter-studios.com: I modified this to 
    //  use modernizr and the html.touch detection and also to stop counting two 
    //  clicks at once, but count each click separately.
    
    (function($) {
        $.fn.nodoubletapzoom = function() {
            if($("html.touch").length == 0) return;
    
            $(this).bind('touchstart', function preventZoom(e){
                var t2 = e.timeStamp;
                var t1 = $(this).data('lastTouch') || t2;
                var dt = t2 - t1;
                var fingers = e.originalEvent.touches.length;
                $(this).data('lastTouch', t2);
                if (!dt || dt > 500 || fingers > 1){
                    return; // not double-tap
                }
                e.preventDefault(); // double tap - prevent the zoom
                // also synthesize click events we just swallowed up
                $(this).trigger('click');
            });
        };
    })(jQuery);
    

    The apply the nodoubletapzoom() to the body tag, like this

    $("body").nodoubletapzoom();
    

    your html construction, should be something like this

    <body>
        <div class="content">...your content and everything in your page</div>
    </body>
    

    Then in your javascript, bind your click handlers like this

    $(".content")
        .on(".mydomelement","click",function(){...})
        .on("button","click",function(){...})
        .on("input","keyup blur",function(){...}); 
        // etc etc etc, add ALL your handlers in this way
    

    you can use any jquery event handler and any dom node. now you dont have to do anything more than that, you have to attach all your event handlers in this way, I've not tried another way, but this way works absolutely rock solid, you can literally rap the screen 10 times a second and it doesnt zoom and registers the clicks (obviously not 10 per second, the ipad isn't that fast, what I mean is, you can't trigger the zoom)

    I think this works because you're attaching the nozoom handler to the body and then attaching all your event handlers to the ".content" node, but delegating to the specific node in question, therefore jquery catches all the handlers at the last stage before the event would bubble up to the body tag.

    the main benefit of this solution is you only have to assign the nodoubletapzoom() handler to the body, you only do it once, not once for each element, so it's much less work, effort and thinking required in order to get things done.

    this has the additional benefit in that if you add content using AJAX, they automatically have the handlers ready and waiting, I suppose if you DIDNT want that, then this method won't work for you and you'll have to adjust it more.

    I've verified this code works on ipad, it's beautiful actually, well done to @ecmanaut for the primary solution!!

    ** Modified on Saturday 26th May because I found what seems to be a perfect solution with absolutely minimal effort

    0 讨论(0)
  • 2020-12-05 12:21

    In case someone needs a solution without jQuery, I found this one (https://exceptionshub.com/disable-double-tap-zoom-option-in-browser-on-touch-devices.html) working very well. The function expects and event object as a parameter:

    function preventZoom(e) {
      var t2 = e.timeStamp;
      var t1 = e.currentTarget.dataset.lastTouch || t2;
      var dt = t2 - t1;
      var fingers = e.touches ? e.touches.length : 0;
      e.currentTarget.dataset.lastTouch = t2;
    
      if (!dt || dt > 500 || fingers > 1) return; // not double-tap
    
      e.preventDefault();
      e.target.click();
    }
    

    Example for Angular 10:

    <!— Template —>
    
    <span (click)="clicked($event)">Click me</span>
    
    Component
    
    clicked(event) {
      this.preventZoom(event);
      // your code
    }
    
    preventZoom(e) {
      var t2 = e.timeStamp;
      var t1 = e.currentTarget.dataset.lastTouch || t2;
      var dt = t2 - t1;
      var fingers = e.touches?.length;
      e.currentTarget.dataset.lastTouch = t2;
    
      if (!dt || dt > 500 || fingers > 1) return; // not double-tap
    
      e.preventDefault();
      e.target.click();
    }
    
    0 讨论(0)
  • 2020-12-05 12:23

    The accepted 'double click' answer is to me a little 'bloated' and using a timestamp..... why? Look at this simple implementation without excessive 'bloat'.

    function simulateDblClickTouchEvent(oo)
    {
     var $oo = !oo?{}:$(oo);
     if( !$oo[0] )
      { return false; }
    
     $oo.bind('touchend', function(e)
     {
        var ot = this,
        ev = e.originalEvent;
    
        if( ev && typeof ev.touches == 'object' && ev.touches.length > 1 )
         { return; }
    
        ot.__taps = (!ot.__taps)?1:++ot.__taps;
    
        if( !ot.__tabstm ) // don't start it twice
        {
         ot.__tabstm = setTimeout( function()
         {
           if( ot.__taps >= 2 )
           {  ot.__taps = 0;
              $(ot).trigger('dblclick'); 
           }
           ot.__tabstm = 0;
           ot.__taps = 0;
         },800);
        }
     });
     return true;
    };
    

    Usage:

    simulateDblClickTouchEvent($('#example'));
    
    or 
    
    simulateDblClickTouchEvent($('.example'));
    

    function returns true or false when event is binded or not.

    To prevent zoom things and scroll do this:

    function disableTouchScroll()
    {
    
     try {
      document.addEventListener('touchmove', function(e) { e.preventDefault(); }, true );
      $j('body')[0].addEventListener('touchmove', function(e) { e.preventDefault(); }, true );
      }
      catch(ee) { return false; }
      return true;
    }
    

    Also with CSS it is easy to avoid zooming:

    body * { -webkit-user-select:none; }
    
    • = not efficient but try it, it works great.

    Cheers!

    0 讨论(0)
  • 2020-12-05 12:27

    just seting viewport may not always be enough - in some cases you may have to also call event.preventDefault(); on the touchstart handler.

    0 讨论(0)
  • 2020-12-05 12:42

    All solutions that I have seen (on this page and also elsewhere) have a side effect that they prevent in fast rate repeated clicks. Allows one click per 500ms or similar. This can be okay in some cases, but not eg. if you have a shooter or arrow buttons to allow fast moving from item to item.

    The easiest solution is this:

    $('#nozoom').on('touchstart', function(e)
    {
      fn_start(e);
      e.preventDefault();
    });
    

    This calls fn_start() (the actual callback function) every time when touch is started, but prevents then default zoomings etc.

    The working comparative example is here: http://jsbin.com/meluyevisi/1/. Green box prevents, red box allows.

    0 讨论(0)
提交回复
热议问题