it-swarm.com.de

iOS Safari - Wie deaktiviere ich Overscroll, aber erlaube es scrollbaren Divs, normal zu scrollen?

Ich arbeite an einer iPad-basierten Web-App und muss ein Überrollen verhindern, damit es weniger wie eine Webseite wirkt. Ich verwende dies derzeit, um das Ansichtsfenster einzufrieren und die Übersteuerung zu deaktivieren:

document.body.addEventListener('touchmove',function(e){
      e.preventDefault();
  });

Dies funktioniert sehr gut, um Overscroll zu deaktivieren, aber meine App hat mehrere scrollbare Divs und der obige Code verhindert, dass sie scrollen .

Ich bin nur auf iOS 5 und höher ausgerichtet, um hackige Lösungen wie iScroll zu vermeiden. Stattdessen benutze ich dieses CSS für meine scrollbaren Divs:

.scrollable {
    -webkit-overflow-scrolling: touch;
    overflow-y:auto;
}

Dies funktioniert ohne das Dokument-Overscroll-Skript, löst jedoch nicht das div-Bildlaufproblem.

Ohne ein jQuery-Plugin gibt es eine Möglichkeit, das Overscroll-Fix zu verwenden, aber mein $ (' .scrollable ') divs?

EDIT:

Ich habe etwas gefunden, das eine anständige Lösung ist:

 // Disable overscroll / viewport moving on everything but scrollable divs
 $('body').on('touchmove', function (e) {
         if (!$('.scrollable').has($(e.target)).length) e.preventDefault();
 });

Das Ansichtsfenster bewegt sich weiterhin, wenn Sie über den Anfang oder das Ende des Div hinausblättern. Ich möchte auch einen Weg finden, dies zu deaktivieren.

97
Jeff

Dies löst das Problem, wenn Sie über den Anfang oder das Ende des div hinausblättern

var selScrollable = '.scrollable';
// Uses document because document will be topmost level in bubbling
$(document).on('touchmove',function(e){
  e.preventDefault();
});
// Uses body because jQuery on events are called off of the element they are
// added to, so bubbling would not work if we used document instead.
$('body').on('touchstart', selScrollable, function(e) {
  if (e.currentTarget.scrollTop === 0) {
    e.currentTarget.scrollTop = 1;
  } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {
    e.currentTarget.scrollTop -= 1;
  }
});
// Stops preventDefault from being called on document if it sees a scrollable div
$('body').on('touchmove', selScrollable, function(e) {
  e.stopPropagation();
});

Beachten Sie, dass dies nicht funktioniert, wenn Sie das Scrollen einer ganzen Seite blockieren möchten, wenn ein Div nicht überläuft. Um dies zu blockieren, verwenden Sie den folgenden Ereignishandler anstelle des unmittelbar darüber stehenden (angepasst von diese Frage ):

$('body').on('touchmove', selScrollable, function(e) {
    // Only block default if internal div contents are large enough to scroll
    // Warning: scrollHeight support is not universal. (https://stackoverflow.com/a/15033226/40352)
    if($(this)[0].scrollHeight > $(this).innerHeight()) {
        e.stopPropagation();
    }
});
84
Tyler Dodge

Wenn ich Tyler Dodge's vorzügliches antworte benutzte, blieb ich auf meinem iPad zurück, also fügte ich etwas Drosselungscode hinzu, jetzt ist es ziemlich flüssig. Beim Scrollen kann es manchmal zu minimalen Auslassungen kommen.

// Uses document because document will be topmost level in bubbling
$(document).on('touchmove',function(e){
  e.preventDefault();
});

var scrolling = false;

// Uses body because jquery on events are called off of the element they are
// added to, so bubbling would not work if we used document instead.
$('body').on('touchstart','.scrollable',function(e) {

    // Only execute the below code once at a time
    if (!scrolling) {
        scrolling = true;   
        if (e.currentTarget.scrollTop === 0) {
          e.currentTarget.scrollTop = 1;
        } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {
          e.currentTarget.scrollTop -= 1;
        }
        scrolling = false;
    }
});

// Prevents preventDefault from being called on document if it sees a scrollable div
$('body').on('touchmove','.scrollable',function(e) {
  e.stopPropagation();
});

Das Hinzufügen des folgenden CSS behebt außerdem einige Renderingfehler ( source ):

.scrollable {
    overflow: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
}
.scrollable * {
    -webkit-transform: translate3d(0,0,0);
}
23
Kuba Holuj

Verhindern Sie zunächst wie gewohnt Standardaktionen für Ihr gesamtes Dokument:

$(document).bind('touchmove', function(e){
  e.preventDefault();           
});

Stoppen Sie dann die Weitergabe Ihrer Elementklasse auf Dokumentebene. Dies verhindert, dass die obige Funktion erreicht wird, und somit wird e.preventDefault () nicht initiiert:

$('.scrollable').bind('touchmove', function(e){
  e.stopPropagation();
});

Dieses System scheint natürlicher und weniger intensiv zu sein als die Berechnung der Klasse bei allen Berührungsbewegungen. Verwenden Sie für dynamisch generierte Elemente .on () anstelle von .bind ().

Berücksichtigen Sie auch diese Meta-Tags, um zu verhindern, dass beim Verwenden Ihrer scrollbaren Div etwas Unglückliches passiert:

<meta content='True' name='HandheldFriendly' />
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0' name='viewport' />
<meta name="viewport" content="width=device-width" />
12
Jonathan Tonge

Können Sie Ihrem Code zum Deaktivieren von Overscrolls etwas mehr Logik hinzufügen, um sicherzustellen, dass es sich bei dem betreffenden Zielelement nicht um ein Element handelt, für das Sie einen Bildlauf durchführen möchten? Etwas wie das:

document.body.addEventListener('touchmove',function(e){
     if(!$(e.target).hasClass("scrollable")) {
       e.preventDefault();
     }
 });
7
DeveloperJoe

Die beste Lösung hierfür ist css/html: Erstellen Sie ein Div, in das Ihre Elemente eingewickelt werden sollen, falls Sie es noch nicht haben, und stellen Sie es auf eine feste Position und einen ausgeblendeten Überlauf ein. Optional können Sie Höhe und Breite auf 100% einstellen, wenn Sie möchten, dass der gesamte Bildschirm und nur der gesamte Bildschirm ausgefüllt wird

#wrapper{
  height: 100%;
  width: 100%;
  position: fixed;
  overflow: hidden;
}
<div id="wrapper">
  <p>All</p>
  <p>Your</p>
  <p>Elements</p>
</div>
6
Elias Fyksen

Überprüfen Sie, ob das bildlauffähige Element beim Versuch, einen Bildlauf nach oben oder nach unten durchzuführen, bereits nach oben gescrollt ist, und verhindern Sie dann durch die Standardaktion, dass sich die gesamte Seite nicht mehr bewegt.

var touchStartEvent;
$('.scrollable').on({
    touchstart: function(e) {
        touchStartEvent = e;
    },
    touchmove: function(e) {
        if ((e.originalEvent.pageY > touchStartEvent.originalEvent.pageY && this.scrollTop == 0) ||
            (e.originalEvent.pageY < touchStartEvent.originalEvent.pageY && this.scrollTop + this.offsetHeight >= this.scrollHeight))
            e.preventDefault();
    }
});
5
Johan

Ich habe nach einer Möglichkeit gesucht, das Scrollen des gesamten Körpers zu verhindern, wenn ein Popup mit einem scrollbaren Bereich vorhanden ist (ein "Einkaufswagen" -Popdown mit einer scrollbaren Ansicht Ihres Einkaufswagens).

Ich habe eine weitaus elegantere Lösung mit minimalem Javascript geschrieben, um nur die Klasse "noscroll" auf Ihrem Körper umzuschalten, wenn Sie ein Popup oder Div haben, das Sie scrollen möchten (und nicht den gesamten Seitenkörper "übersteuern").

während Desktop-Browser einen Überlauf beobachten: versteckt - iOS scheint dies zu ignorieren, es sei denn, Sie setzen die Position auf fest ..., was dazu führt, dass die gesamte Seite eine seltsame Breite hat. Daher müssen Sie Position und Breite auch manuell einstellen. benutze diese CSS:

.noscroll {
    overflow: hidden;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
}

und dieses jquery:

/* fade in/out cart popup, add/remove .noscroll from body */
$('a.cart').click(function() {
    $('nav > ul.cart').fadeToggle(100, 'linear');
    if ($('nav > ul.cart').is(":visible")) {
        $('body').toggleClass('noscroll');
    } else {
        $('body').removeClass('noscroll');
    }
});

/* close all popup menus when you click the page... */
$('body').click(function () {
    $('nav > ul').fadeOut(100, 'linear');
    $('body').removeClass('noscroll');
});

/* ... but prevent clicks in the popup from closing the popup */
$('nav > ul').click(function(event){
    event.stopPropagation();
});
4
nrutas

Ich arbeite ein wenig ohne jquery Workarround. Nicht perfekt, aber funktioniert gut (besonders wenn Sie ein scroll-x in einem scoll-y haben) https://github.com/pinadesign/overscroll/

Fühlen Sie sich frei, um daran teilzunehmen und es zu verbessern

3
PiñaDesign

Das Deaktivieren aller "touchmove" -Ereignisse scheint eine gute Idee zu sein. Sobald Sie andere scrollbare Elemente auf der Seite benötigen, kann dies zu Problemen führen. Wenn Sie darüber hinaus "touchmove" -Ereignisse nur für bestimmte Elemente deaktivieren (z. B. body, wenn die Seite nicht scrollbar sein soll), IOS wird eine unaufhaltsame Weitergabe in Chrome) verursachen, wenn die URL-Leiste umschaltet.

Obwohl ich dieses Verhalten nicht erklären kann, scheint die einzige Möglichkeit, dies zu verhindern, die Position des Körpers auf fixed zu setzen. Das einzige Problem dabei ist, dass Sie die Position des Dokuments verlieren - das ist zum Beispiel bei Modalen besonders ärgerlich. Eine Möglichkeit zur Lösung wäre die Verwendung dieser einfachen VanillaJS-Funktionen:

function disableDocumentScrolling() {
    if (document.documentElement.style.position != 'fixed') {
        // Get the top vertical offset.
        var topVerticalOffset = (typeof window.pageYOffset != 'undefined') ?
            window.pageYOffset : (document.documentElement.scrollTop ? 
            document.documentElement.scrollTop : 0);
        // Set the document to fixed position (this is the only way around IOS' overscroll "feature").
        document.documentElement.style.position = 'fixed';
        // Set back the offset position by user negative margin on the fixed document.
        document.documentElement.style.marginTop = '-' + topVerticalOffset + 'px';
    }
}

function enableDocumentScrolling() {
    if (document.documentElement.style.position == 'fixed') {
        // Remove the fixed position on the document.
        document.documentElement.style.position = null;
        // Calculate back the original position of the non-fixed document.
        var scrollPosition = -1 * parseFloat(document.documentElement.style.marginTop);
        // Remove fixed document negative margin.
        document.documentElement.style.marginTop = null;
        // Scroll to the original position of the non-fixed document.
        window.scrollTo(0, scrollPosition);
    }
}

Mit dieser Lösung können Sie ein festes Dokument haben und jedes andere Element auf Ihrer Seite kann mit einfachem CSS überlaufen (z. B. overflow: scroll;). Keine Notwendigkeit für spezielle Kurse oder irgendetwas anderes.

1

Diese Lösung erfordert nicht, dass Sie eine scrollbare Klasse für alle scrollbaren Divs erstellen. Das Scrollen ist für alle Elemente zulässig, die INPUT-Elemente contenteditables und overflow scroll oder autos sind oder untergeordnete Elemente von INPUT sind.

Ich verwende einen benutzerdefinierten Selektor und speichere auch das Ergebnis der Überprüfung im Element, um die Leistung zu verbessern. Sie müssen nicht jedes Mal dasselbe Element überprüfen. Dies kann ein paar Probleme haben, wie gerade geschrieben, aber ich dachte, ich würde teilen.

$.expr[':'].scrollable = function(obj) {
    var $el = $(obj);
    var tagName = $el.prop("tagName");
    return (tagName !== 'BODY' && tagName !== 'HTML') && (tagName === 'INPUT' || $el.is("[contentEditable='true']") || $el.css("overflow").match(/auto|scroll/));
};
function preventBodyScroll() {
    function isScrollAllowed($target) {
        if ($target.data("isScrollAllowed") !== undefined) {
            return $target.data("isScrollAllowed");
        }
        var scrollAllowed = $target.closest(":scrollable").length > 0;
        $target.data("isScrollAllowed",scrollAllowed);
        return scrollAllowed;
    }
    $('body').bind('touchmove', function (ev) {
        if (!isScrollAllowed($(ev.target))) {
            ev.preventDefault();
        }
    });
}
1
jcbdrn

dieses funktioniert bei mir (klares Javascript)

var fixScroll = function (className, border) {  // className = class of scrollElement(s), border: borderTop + borderBottom, due to offsetHeight
var reg = new RegExp(className,"i"); var off = +border + 1;
function _testClass(e) { var o = e.target; while (!reg.test(o.className)) if (!o || o==document) return false; else o = o.parentNode; return o;}
document.ontouchmove  = function(e) { var o = _testClass(e); if (o) { e.stopPropagation(); if (o.scrollTop == 0) { o.scrollTop += 1; e.preventDefault();}}}
document.ontouchstart = function(e) { var o = _testClass(e); if (o && o.scrollHeight >= o.scrollTop + o.offsetHeight - off) o.scrollTop -= off;}
}

fixScroll("fixscroll",2); // assuming I have a 1px border in my DIV

html:

<div class="fixscroll" style="border:1px gray solid">content</div>
0
RJS

Hier ist eine Zepto-kompatible Lösung

    if (!$(e.target).hasClass('scrollable') && !$(e.target).closest('.scrollable').length > 0) {
       console.log('prevented scroll');
       e.preventDefault();
       window.scroll(0,0);
       return false;
    }
0

Versuchen Sie dies. Es wird perfekt funktionieren.

$('body.overflow-hidden').delegate('#skrollr-body','touchmove',function(e){
    e.preventDefault();
    console.log('Stop skrollrbody');
}).delegate('.mfp-auto-cursor .mfp-content','touchmove',function(e){
    e.stopPropagation();
    console.log('Scroll scroll');
});

Ich hatte überraschendes Glück mit mit einfachen:

body {
    height: 100vh;
}

Es funktioniert großartig, das Überscrollen für Popups oder Menüs zu deaktivieren, und es erzwingt nicht, dass Browser-Leisten wie bei der Verwendung von position: fixed angezeigt werden. ABER - Sie müssen die Bildlaufposition speichern, bevor Sie eine feste Höhe festlegen und diese wiederherstellen, wenn Sie das Popup ausblenden. Andernfalls wird der Browser nach oben scrollen.

0