iOS Safari - ओवरस्क्रोल को कैसे अक्षम करें लेकिन स्क्रॉल करने योग्य divs को सामान्य रूप से स्क्रॉल करने की अनुमति दें?


100

मैं एक iPad-आधारित वेब ऐप पर काम कर रहा हूं, और ओवरस्क्रोलिंग को रोकने की आवश्यकता है ताकि यह एक वेब पेज की तरह कम लगे। मैं वर्तमान में व्यूपोर्ट को फ्रीज करने और ओवरक्रोल को अक्षम करने के लिए इसका उपयोग कर रहा हूं:

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

यह ओवरस्क्रॉल को अक्षम करने के लिए बहुत अच्छा काम करता है, लेकिन मेरे ऐप में कई स्क्रॉल करने योग्य div हैं, और उपरोक्त कोड उन्हें स्क्रॉल करने से रोकता है

मैं iOS 5 और इसके बाद के संस्करण को लक्षित कर रहा हूं, इसलिए मैंने iScroll जैसे हैकी समाधान से परहेज किया है। इसके बजाय मैं अपने स्क्रॉल करने योग्य div के लिए इस CSS का उपयोग कर रहा हूं:

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

यह दस्तावेज़ के बिना स्क्रिप्ट को ओवरकॉल करता है, लेकिन div स्क्रॉलिंग समस्या को हल नहीं करता है।

एक jQuery प्लगइन के बिना, क्या ओवरस्क्रॉल फिक्स का उपयोग करने का कोई तरीका है लेकिन मेरे $ ('स्क्रॉल योग्य') डिव को छूट देता है?

संपादित करें:

मैंने पाया कि एक अच्छा समाधान है:

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

व्यूपोर्ट तब भी चलता है जब आप div की शुरुआत या अंत को स्क्रॉल करते हैं। मैं इसे अक्षम करने का एक तरीका खोजना चाहता हूं।


अपने अंतिम एक की भी कोशिश की, लेकिन या तो काम नहीं किया
सैंटियागो रेबेला

जब आप स्क्रॉल किए गए div के माता-पिता पर स्क्रॉल ईवेंट को स्पष्ट रूप से कैप्चर करके और वास्तव में स्क्रॉल करने की अनुमति नहीं देते हैं तो मैं व्यूपोर्ट को आगे बढ़ने से रोक सकता था। यदि आप jquery मोबाइल का उपयोग कर रहे हैं, तो यह पेज स्तर पर ऐसा करने के लिए समझ में आता है: $ ('div [data-role = "page"]])। (' स्क्रॉल ', फंक्शन (e) {e.preventDefault। ();});
क्रिस्टोफर जॉनसन

github.com/lazd/iNoBounce अद्भुत काम करता है
डेविड अंडरहिल

मुझे यह स्क्रिप्ट मिली है जो इस समस्या को ठीक करती है! :) github.com/lazd/iNoBounce
Jan ánafránek

यदि आप अपने पोस्ट से ऊपर किसी को 7 महीने पहले पोस्ट किया है, तो आप फिर से लिंक क्यों पोस्ट करेंगे?
डेनी

जवाबों:


84

यह समस्या को हल करता है जब आप div की शुरुआत या अंत को स्क्रॉल करते हैं

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();
});

ध्यान दें कि यह काम नहीं करेगा यदि आप पूरे पृष्ठ को ब्लॉक करना चाहते हैं जब एक div अतिप्रवाह नहीं है। इसे ब्लॉक करने के लिए, ऊपर दिए गए एक के बजाय निम्नलिखित घटना हैंडलर का उपयोग करें ( इस प्रश्न से अनुकूलित ):

$('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();
    }
});

यह काम नहीं करेगा अगर स्क्रॉल करने योग्य क्षेत्र के अंदर एक iframe है और उपयोगकर्ता उस iframe पर स्क्रॉल करना शुरू कर देता है। क्या इसके आसपास कोई कार्य है?
तिमो

2
महान काम किया - यह निश्चित रूप से .scrollableसीधे लक्षित करने से बेहतर है (जो कि मैंने मूल रूप से इस समस्या को हल करने की कोशिश की थी)। अगर आप एक जावास्क्रिप्ट नोब हैं और लाइन के नीचे इन हैंडलर को हटाने के लिए आसान कोड चाहते हैं, तो ये दो लाइनें मेरे लिए बहुत अच्छा काम करती हैं! $(document).off('touchmove'); और $('body').off('touchmove touchstart', '.scrollable');
डेविन

इसने मेरे लिए पूरी तरह से काम किया। बहुत बहुत धन्यवाद, आपने मुझे घंटों बचाया!
marcgg

1
अगर स्क्रॉल करने के लिए div में पर्याप्त सामग्री नहीं है तो यह काम नहीं करता है। किसी ने एक अलग प्रश्न किया जिसने उत्तर दिया कि यहां: stackoverflow.com/q/16437182/40352
क्रिस

मैं एक से अधिक ".scrollable" वर्ग की अनुमति कैसे दे सकता हूं? यह एक के साथ ठीक काम करता है, लेकिन मुझे स्क्रॉल करने योग्य एक और div बनाने की आवश्यकता है। धन्यवाद!
मेव

23

टायलर डॉज के उत्कृष्ट उत्तर का उपयोग करना मेरे iPad पर पिछड़ता रहा, इसलिए मैंने कुछ थ्रॉटलिंग कोड जोड़े, अब यह काफी सुचारू है। स्क्रॉल करते समय कभी-कभी कुछ न्यूनतम लंघन होता है।

// 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();
});

इसके अलावा, निम्नलिखित सीएसएस जोड़ने से कुछ रेंडरिंग ग्लिच ( स्रोत ) ठीक हो जाते हैं:

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

यह काम नहीं करेगा अगर स्क्रॉल करने योग्य क्षेत्र के अंदर एक iframe है और उपयोगकर्ता उस iframe पर स्क्रॉल करना शुरू कर देता है। क्या इसके आसपास कोई कार्य है?
तिमो

1
पीछे की ओर खींचने के लिए एकदम सही काम करने लगता है, लेकिन नीचे की ओर खींचना अभी भी सफारी को आगे बढ़ाएगा।
अबादबा

1
एक भयानक समाधान ... बहुत बहुत धन्यवाद :)
आमिर शाह

इसने मेरे लिए काम किया। धन्यवाद! मैं इस मुद्दे को हल करने के लिए 1.5 दिन से अधिक समय बिताता हूं।
अचिन्त्य सांधिका

यह बहुत बढ़िया है, बहुत अच्छा काम किया है और मुझे आगे के तनाव को हल करने की कोशिश में बचाया है। शुक्रिया कुबा!
लियोनार्ड

12

पहले अपने संपूर्ण दस्तावेज़ पर हमेशा की तरह डिफ़ॉल्ट क्रियाओं को रोकें:

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

फिर अपने स्तर के तत्वों को दस्तावेज़ स्तर पर प्रचारित करने से रोकें। यह इसे ऊपर दिए गए फ़ंक्शन तक पहुंचने से रोकता है और इस प्रकार e.preventDefault () आरंभ नहीं होता है:

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

यह प्रणाली सभी स्पर्श चालों पर वर्ग की गणना करने की तुलना में अधिक प्राकृतिक और कम गहन प्रतीत होती है। गतिशील रूप से उत्पन्न तत्वों के लिए .bind () के बजाय .on () का उपयोग करें।

अपने स्क्रॉल करने योग्य div का उपयोग करते समय दुर्भाग्यपूर्ण चीजों को रोकने के लिए इन मेटा टैग पर भी विचार करें:

<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" />

7

क्या आप यह सुनिश्चित करने के लिए अपने ओवरस्क्रॉल डिसेबल कोड में थोड़ा और तर्क जोड़ सकते हैं कि यह सुनिश्चित करने के लिए कि प्रश्न में लक्षित तत्व ऐसा नहीं है जिसे आप स्क्रॉल करना चाहते हैं? कुछ इस तरह:

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

3
धन्यवाद ... ऐसा लगता है कि यह काम करना चाहिए , लेकिन यह नहीं है। इसके अलावा, क्या यह "स्क्रॉल करने योग्य" नहीं होना चाहिए और न ही ".सीरोबल" (डॉट के साथ)?
जेफ

1
ऐसा लगता है कि यह सबसे गहरा नेस्टेड तत्व है जो स्पर्श घटना को प्राप्त करता है इसलिए आपको अपने सभी माता-पिता को यह देखने के लिए जांचने की आवश्यकता हो सकती है कि क्या आप स्क्रॉल करने योग्य div में हैं।
क्रिस्टोफर जॉनसन

3
यदि jQuery का उपयोग किया जाता है, तो कोई दस्तावेज़ का उपयोग क्यों करेगा ।body.addEventListener? क्या यह एक कारण है?
fnagel

7

इसका सबसे अच्छा समाधान है css / html: अपने तत्वों को लपेटने के लिए एक div बनाएं, यदि आपके पास पहले से ही नहीं है और इसे स्थिर और अतिप्रवाहित स्थिति में सेट करें। वैकल्पिक, सेट ऊँचाई और चौड़ाई 100% यदि आप चाहते हैं कि यह पूरी स्क्रीन को भरने के लिए हो और पूरी स्क्रीन के अलावा कुछ भी न हो

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


5

स्क्रॉल करने योग्य तत्व पहले से ही स्क्रॉल करने के लिए ऊपर या नीचे जब स्क्रॉल करने की कोशिश कर रहा है और फिर पूरे पृष्ठ को चलने से रोकने के लिए डिफ़ॉल्ट कार्रवाई को रोकने के लिए ऊपर की ओर स्क्रॉल किया जाता है।

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();
    }
});

मुझे e.originalEvent.touches [0] के लिए जांच करनी थी। यह काम किया है, लेकिन केवल अगर आप स्क्रॉलिंग div के अंत में पहले से ही हैं। जब स्क्रॉल प्रगति पर होता है (उदाहरण के लिए, आपने बहुत तेज़ी से स्क्रॉल किया है) स्क्रॉल किए गए div के अंत तक पहुँच जाने के बाद यह बंद नहीं होता है।
कीन सेज

4

मैं एक स्क्रॉल करने योग्य क्षेत्र ("शॉपिंग कार्ट" पॉपडाउन जिसमें आपकी कार्ट का स्क्रॉल दृश्य है) के साथ पॉपअप होने पर सभी बॉडी स्क्रॉलिंग को रोकने का एक तरीका खोज रहा था।

जब आप एक पॉपअप या div है कि आप स्क्रॉल करना चाहते हैं (और पूरे पृष्ठ शरीर "नहीं" ओवरस्क्रॉल) जब आप अपने शरीर पर वर्ग "noscroll" टॉगल करने के लिए न्यूनतम जावास्क्रिप्ट का उपयोग कर एक और अधिक सुंदर समाधान लिखा था।

जब डेस्कटॉप ब्राउज़र ओवरफ्लो का निरीक्षण करते हैं: छिपा हुआ - iOS तब तक अनदेखा करता है जब तक कि आप स्थिति को निर्धारित नहीं करते ... जो पूरे पृष्ठ को एक अजीब चौड़ाई का कारण बनता है, इसलिए आपको स्थिति और चौड़ाई को मैन्युअल रूप से भी सेट करना होगा। इस सीएसएस का उपयोग करें:

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

और यह 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();
});

यह बहुत उपयोगी है, और एक न्यूनतम दृष्टिकोण है, बस मुझे क्या चाहिए। स्थिति को निर्धारित करने के लिए, शीर्ष के साथ: 0; छोड़ दिया: 0; चौड़ाई: 100%; मैं गायब था तत्वों। यह फ्लाई-आउट मेनू के लिए भी उपयोगी है।
bdanin

3

मैं jquery के बिना एक छोटे से काम घेर काम किया है। Perfert नहीं, लेकिन ठीक काम करता है (विशेषकर यदि आपके पास एक sc-y में एक स्क्रॉल-x है) https://github.com/pinadesign/overscroll/

इसमें भाग लेने और इसमें सुधार करने के लिए स्वतंत्र रहें


1
जेफ के रूप में एक ही मुद्दा था, हर जवाब की कोशिश की, तुम्हारा काम किया। धन्यवाद!
डॉमिनिक श्रेयेर

मेरे द्वारा स्वीकार किए गए उत्तर ने मेरे लिए तभी काम किया जब div .scrollable के पास इसे ओवरफ्लो करने के लिए पर्याप्त सामग्री थी। यदि यह अतिप्रवाह नहीं हुआ, तो 'उछाल' प्रभाव अभी भी मौजूद है। हालाँकि यह पूरी तरह से काम करता है, धन्यवाद!
एडम मार्शल

1

इस समाधान के लिए आपको अपने सभी स्क्रॉल करने योग्य div पर स्क्रॉल करने योग्य क्लास लगाने की आवश्यकता नहीं है, इसलिए यह अधिक सामान्य है। स्क्रॉलिंग को उन सभी तत्वों पर अनुमति दी जाती है जो हैं, या INPUT तत्व संतोषी और अतिप्रवाह स्क्रॉल या ऑटो के बच्चे हैं।

मैं एक कस्टम चयनकर्ता का उपयोग करता हूं और प्रदर्शन को बेहतर बनाने के लिए तत्व में चेक के परिणाम को कैश भी करता हूं। हर बार एक ही तत्व की जांच करने की आवश्यकता नहीं है। यह कुछ मुद्दों के रूप में सिर्फ लिखा जा सकता है, लेकिन सोचा था कि मैं साझा करूँगा।

$.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

सभी "टचमॉव" घटनाओं को अक्षम करते समय, यह एक अच्छा विचार हो सकता है, जैसे ही आपको पृष्ठ पर अन्य स्क्रॉल करने योग्य तत्वों की आवश्यकता होगी, यह समस्या पैदा करेगा। उसके शीर्ष पर, यदि आप केवल कुछ तत्वों पर "टचमॉव" ईवेंट को अक्षम करते हैं (जैसे कि यदि आप चाहते हैं कि पृष्ठ गैर-स्क्रॉल करने योग्य हो), तो जैसे ही इसे कहीं और सक्षम किया जाएगा, आईओएस क्रोम में URL के बिना अप्राप्य प्रसार का कारण होगा जब URL बार टॉगल।

जबकि मैं इस व्यवहार की व्याख्या नहीं कर सकता, यह शरीर की स्थिति सेट करने के लिए लगता है को रोकने के लिए एकमात्र तरीका है fixed। केवल समस्या यह है कि आप दस्तावेज़ की स्थिति खो देंगे - यह उदाहरण के लिए मोडल में विशेष रूप से कष्टप्रद है। इसे हल करने का एक तरीका इन सरल वनिलाज कार्यों का उपयोग करना होगा:

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);
    }
}

इस समाधान का उपयोग करके आपके पास एक निश्चित दस्तावेज़ हो सकता है और आपके पृष्ठ का कोई अन्य तत्व सरल सीएसएस (जैसे, overflow: scroll;) का उपयोग करके ओवरफ्लो कर सकता है । इसके लिए किसी विशेष वर्ग या किसी अन्य चीज की जरूरत नहीं है।


0

यहाँ एक zepto संगत समाधान है

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

0

यह मेरे लिए काम करता है (सादा जावास्क्रिप्ट)

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

एचटीएमएल:

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

0

यह कोशिश करो यह सही काम करेंगे।

$('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');
});

0

मैं साधारण के साथ आश्चर्यजनक भाग्य था:

body {
    height: 100vh;
}

यह पॉप-अप या मेनू के लिए ओवरस्क्रोल को अक्षम करने के लिए बहुत अच्छा काम करता है और यह स्थिति का उपयोग करते समय ब्राउज़र बार को प्रकट होने के लिए मजबूर नहीं करता है: निश्चित। लेकिन - आपको निश्चित ऊंचाई निर्धारित करने से पहले स्क्रॉल की स्थिति को बचाने और पॉप-अप को छिपाने के दौरान इसे पुनर्स्थापित करने की आवश्यकता है, अन्यथा, ब्राउज़र शीर्ष पर स्क्रॉल करेगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.