簡體   English   中英

Javascript:是否可以確定用戶在到達頁面末尾后滾動了多少?

[英]Javascript: is it possible to determine how much user scrolls after reaching the end of a page?

在移動設備上,在可拖動元素中包含可滾動元素是一種常見的 UI 模式。 當您到達可滾動元素的末尾時,您開始拖動外部元素。 例如在這個 GIF ( https://media.giphy.com/media/9MJgBkoZfqA7jRdQop/giphy.gif ) 中,滾動到頂部后,如果您繼續滾動,它將拖動 subreddits 菜單。

我想使用 JS/CSS 實現類似的模式。 為此,我需要檢測用戶是否在到達結尾后繼續滾動。 這可能嗎? 如果是這樣,是否有可能確定他們到達終點后滾動多少?

 window.onscroll = function(element) {
    if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
       alert("you're at the bottom of the page");
    }
 };

使用元素參數來了解鼠標現在所在的當前確切 xy 以計算更多以及滾動了多少

Javascript:如何檢測瀏覽器窗口是否滾動到底部?

JavaScript:

 // get the button var theBtn = document.getElementById('theBtn'); // get the box var theBox = document.getElementById('theBox'); // add event to the button on click show/hide(toggle) the box theBtn.addEventListener('click', () => { theBox.classList.toggle('active'); }); // when scrolling on the box theBox.onscroll = function(){ // get the top of the div var theBoxTop = theBox.scrollTop; if(theBoxTop <= 0){ // when it reaches 0 or less, hide the box. It'll toggle the class, since it's "show" will "hide" theBox.classList.toggle('active'); } };
 * { margin: 0; padding: 0; box-sizing: border-box; } body { font-size: 10px; font-family: 'Arial', sans-serif; height: 1500px; } html { scroll-behavior: smooth; } ul { list-style-type: none; } #theBox ul li { border: 1px solid; height: 100px; } #navbar-bottom { height: 100px; width: 100%; background: rgb(90, 111, 143); position: fixed; bottom: 0; left: 0; right: 0; box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562); display: flex; justify-content: space-around; align-items: center; } #theBox { background-color: red; height: 350px; width: 100%; position: fixed; bottom: 0; transform: translateY(100%); transition: all 0.3s; overflow-y: scroll; } #theBox.active{ transform: translateY(0); } .myBtns { width: 50px; height: 50px; border-radius: 50%; border: none; position: relative; display: flex; justify-content: center; align-items: center; flex-direction: column; cursor: pointer; } .myBtns span { height: 3px; width: 30px; background-color: black; margin: 3px 0; }
 <main role="main"> <div id="theBox"> <ul> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> </ul> </div> <div id="navbar-bottom"> <button class="myBtns"></button> <button class="myBtns" id="theBtn"> <span></span> <span></span> <span></span> </button> <button class="myBtns"></button> </div> </main>

jQuery:

 // add event to the button on click show/hide(toggle) the box $('#theBtn').click(function(){ $('#theBox').toggleClass('active'); }); // when scrolling on the box $('#theBox').scroll(function () { // get the top of the div var theBoxTop = $('#theBox').scrollTop(); // when it reaches 0 or less, hide the box. It'll toggle the class, since it's "show" will "hide" if(theBoxTop <= 0){ $('#theBox').toggleClass('active'); } });
 * { margin: 0; padding: 0; box-sizing: border-box; } body { font-size: 10px; font-family: 'Arial', sans-serif; height: 1500px; } html { scroll-behavior: smooth; } ul { list-style-type: none; } #theBox ul li { border: 1px solid; height: 100px; } #navbar-bottom { height: 100px; width: 100%; background: rgb(90, 111, 143); position: fixed; bottom: 0; left: 0; right: 0; box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562); display: flex; justify-content: space-around; align-items: center; } #theBox { background-color: red; height: 350px; width: 100%; position: fixed; bottom: 0; transform: translateY(100%); transition: all 0.3s; overflow-y: scroll; } #theBox.active{ transform: translateY(0); } .myBtns { width: 50px; height: 50px; border-radius: 50%; border: none; position: relative; display: flex; justify-content: center; align-items: center; flex-direction: column; cursor: pointer; } .myBtns span { height: 3px; width: 30px; background-color: black; margin: 3px 0; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <main role="main"> <div id="theBox"> <ul> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> <li><p>Text</p></li> </ul> </div> <div id="navbar-bottom"> <button class="myBtns"></button> <button class="myBtns" id="theBtn"> <span></span> <span></span> <span></span> </button> <button class="myBtns"></button> </div> </main>

window.onscroll = function(ev) {
    if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
       alert("you are at the bottom of the page");
    }
};

演示鏈接: http : //jsfiddle.net/5xpoe4yg/

如果您需要在到達頁面底部(或頂部)后跟蹤用戶活動,除了滾動事件之外,您還需要跟蹤滾輪事件。 此外,在移動設備上,您還需要跟蹤touchstarttouchmove事件。

並非所有這些事件都跨瀏覽器標准化,所以我做了自己的標准化函數,或多或少是這樣的: var compulsivity = Math.log2(Math.max(scrollAmount, 0.01) * wheelAmount);

下面是一個完整的游樂場。 您可以使用開發者工具的移動視圖在 Chrome 中進行測試,或者使用 TouchEmulator 在其他瀏覽器中進行測試。

 function Tracker(page) { this.page = page; this.moveUp = 0; this.moveDown = 0; this.startTouches = {}; this.moveTouches = {}; this.lastScrollY = 0; this.monitor = {}; this.startThreshold = 160; this.moveThreshold = 10; this.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; this.pullToRefresh = window.chrome || navigator.userAgent.match('CriOS'); this.amplitude = 16 / Math.log(2); this.page.ownerDocument.addEventListener( 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll', this, { passive: true } ); /* The basic scroll event cannot be canceled, so it does not need to be set passive.*/ this.page.ownerDocument.addEventListener('scroll', this); this.page.addEventListener('touchstart', this, { passive: true }); /* Maybe we need to cancel pullToRefresh */ this.page.addEventListener('touchmove', this, { passive: false }); return this; } Tracker.prototype.handleEvent = function (e) { /* handleEvent is built-in */ var winHeight = (this.iOS ? document.documentElement.clientHeight : window.innerHeight) | 0, currScrollY = window.pageYOffset | 0, amountScrollY = (this.lastScrollY - currScrollY) | 0, elHeight = this.page.offsetHeight | 0, elTop = -currScrollY, elBottom = winHeight - elHeight + currScrollY, isTop = elTop >= 0, isBottom = elBottom >= 0; switch (e.type) { case 'wheel': case 'onmousewheel': case 'mousewheel': case 'DOMMouseScroll': var wheelDelta = e.wheelDelta ? e.wheelDelta : e.deltaY ? -e.deltaY : -e.detail, wheelDir = (wheelDelta > 0) - (wheelDelta < 0), wheelUp = wheelDir < 0, wheelDown = wheelDir > 0, wheelAmount = 100 * wheelDir; if (isTop && wheelDown) { this.moveUp++; this.moveDown = 0; } else if (isBottom && wheelUp) { this.moveUp = 0; this.moveDown++; } else { this.moveUp = 0; this.moveDown = 0; } var compulsivity = this.amplitude * Math.log(Math.max(this.moveUp, this.moveDown, 0.01) * wheelAmount* wheelDir); this.monitor[e.type].track(wheelAmount, compulsivity); break; case 'scroll': /* end of scroll event for iOS, start/end of scroll event for other browsers */ this.lastScrollY = currScrollY; this.monitor[e.type].track(amountScrollY, 0); break; case 'touchstart': var touches = [].slice.call(e.touches), i = touches.length; while (i--) { var touch = touches[i], id = touch.identifier; this.startTouches[id] = touch; this.moveTouches[id] = touch; } break; case 'touchmove': var touches = [].slice.call(e.touches), i = touches.length, currTouches = {}, swipeUp = false, swipeDown = false, currMoveY = 0, totalMoveY = 0; while (i--) { var touch = touches[i], id = touch.identifier; currTouches[id] = touch; if (id in this.moveTouches) { currMoveY = this.moveTouches[id].screenY - touch.screenY; } if (id in this.startTouches) { totalMoveY = this.startTouches[id].screenY - touch.screenY; } swipeUp = currMoveY > 0 || totalMoveY > 0; swipeDown = currMoveY < 0 || totalMoveY < 0; if (this.pullToRefresh && isTop && swipeDown && e.cancelable) { e.preventDefault(); console.log('Reload prevented'); } } this.moveTouches = currTouches; var moveDir = (totalMoveY > 0) - (totalMoveY < 0), longSwipe = moveDir * totalMoveY > this.startThreshold, shortSwipe = moveDir * totalMoveY > this.moveThreshold, realSwipe = longSwipe || shortSwipe; if (isTop && swipeDown) { if (realSwipe) this.moveUp++; this.moveDown = 0; } else if (isBottom && swipeUp) { this.moveUp = 0; if (realSwipe) this.moveDown++; } else { this.moveUp = 0; this.moveDown = 0; } var compulsivity = this.amplitude * Math.log(Math.max(this.moveUp, this.moveDown, 0.01) * moveDir * totalMoveY); this.monitor[e.type].track(currMoveY, compulsivity); break; } }; function Monitor(events) { this.ctx = null; this.cont = null; this.events = events; this.values = []; this.average = 0; this.lastDrawTime = 0; this.inertiaDuration = 200; return this; } Monitor.prototype.showOn = function (container) { var cv = document.createElement('canvas'); this.ctx = cv.getContext('2d'); this.cont = document.getElementById(container); cv.width = this.cont.offsetWidth; cv.height = this.cont.offsetHeight; cv.style.top = 0; cv.style.left = 0; cv.style.zIndex = -1; cv.style.position = 'absolute'; cv.style.backgroundColor = '#000'; this.cont.appendChild(cv); var self = this; window.addEventListener('resize', function () { var cv = self.ctx.canvas, cont = self.cont; cv.width = cont.offsetWidth; cv.height = cont.offsetHeight; }); return this; }; Monitor.prototype.track = function (value, average) { this.average = average; if (this.values.push(value) > this.ctx.canvas.width) this.values.shift(); if (value) this.lastDrawTime = new Date().getTime(); }; Monitor.prototype.draw = function () { if (this.ctx) { var cv = this.ctx.canvas, w = cv.width, h = cv.height; var i = this.values.length, x = w | 0, y = (0.5 * h) | 0; cv.style.backgroundColor = 'rgb(' + this.average + ', 0, 0)'; this.ctx.clearRect(0, 0, w, h); this.ctx.strokeStyle = '#00ffff'; this.ctx.lineWidth = 1; this.ctx.beginPath(); while (i--) { x -= 4; if (x < 0) break; this.ctx.moveTo(x, y); this.ctx.lineTo(x + 1, y); this.ctx.lineTo(x + 1, y - this.values[i]); } this.ctx.stroke(); var elapsed = new Date().getTime() - this.lastDrawTime; /* cool down */ this.average = this.average > 0 ? (this.average * 0.9) | 0 : 0; if (elapsed > this.inertiaDuration) { this.track(0, this.average); } } var self = this; setTimeout(function () { self.draw(); }, 100); }; Monitor.prototype.connectTo = function (tracker) { var events = this.events.split(' '), i = events.length; while (i--) { tracker.monitor[events[i]] = this; } this.draw(); return this; }; function loadSomeData(target) { $.ajax({ url: 'https://jsonplaceholder.typicode.com/users', method: 'GET', crossDomain: true, dataType: 'json', success: function (users) { var html = '', $ul = $(target).find('ul'); $.each(users, function (i, user) { var item = '<li><a class="ui-alt-icon ui-nodisc-icon">'; item += '<h2>' + user.name + '</h2>'; item += '<p><strong>' + user.company.name + '</strong></p>'; item += '<p>' + user.address.zipcode + ', ' + user.address.city + '</p>'; item += '<p>' + user.phone + '</p>'; item += '<p>' + user.email + '</p>'; item += '<p class="ui-body-inherit ui-li-aside ui-li-count"><strong>' + user.id + '</strong></p>'; item += '</a></li>'; html += item; }); $ul.append(html).listview('refresh'); }, }); } $(document) .on('pagecreate', '#page-list', function (e) { $("[data-role='header'], [data-role='footer']").toolbar({ theme: 'a', position: 'fixed', tapToggle: false }); loadSomeData(e.target); }) .on('pageshow', '#page-list', function (e, ui) { var tracker = $.data(this, 'mobile-page', new Tracker(this)); new Monitor('touchstart touchmove').connectTo(tracker).showOn('header'); new Monitor('scroll wheel mousewheel DOMMouseScroll').connectTo(tracker).showOn('footer'); });
 .ui-page { touch-action: none; } h1, h2, h3, h4, h5, h6, p { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } /* JQM no frills */ .ui-btn, .ui-title, .ui-btn:hover, .ui-btn:focus, .ui-btn:active, .ui-btn:visited { text-shadow: none !important; } * { -webkit-box-shadow: none !important; -moz-box-shadow: none !important; box-shadow: none !important; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Compulsivity</title> <meta name="description" content="Compulsivity" /> <meta name="HandheldFriendly" content="True" /> <meta name="MobileOptimized" content="320" /> <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, width=device-width, minimal-ui shrink-to-fit=no" /> <meta http-equiv="cleartype" content="on" /> <!-- Add to homescreen for Chrome on Android --> <meta name="mobile-web-app-capable" content="yes" /> <!-- For iOS web apps. Delete if not needed. --> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta name="apple-mobile-web-app-title" content="Compulsivity" /> <link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" /> <!-- <script type="application/javascript" src="lib/touch-emulator.js"></script> <script> TouchEmulator(); </script> --> <script type="application/javascript" src="https://cdn.jsdelivr.net/npm/jquery@2.2.4/dist/jquery.min.js"></script> <script type="application/javascript" src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script> </head> <body> <div id="header" data-role="header"><h4 style="color: #fff">Compulsivity</h4></div> <div id="page-list" data-role="page"> <div data-role="content" role="main"> <ul data-role="listview" data-filter="true" data-inset="true"></ul> </div> </div> <div id="footer" data-role="footer"><h4 style="color: #fff">Scroll</h4></div> </body> </html>

其中,您還需要注意平滑滾動行為的下拉刷新慣性(或動量)。

請嘗試滾動或滑動並查看事件是如何被跟蹤的:在分別到達頁面底部或頂部后,頂部欄或底部欄將更改顏色以顯示用戶活動。

對此有兩種解決方案。 一種用於觸摸設備,第二種用於使用鼠標的設備。

使用輪事件

如果目標是鼠標設備,那么我們將使用以下方法:

document.onwheel = event => ScrollAction(event);

有關車輪事件的更多信息,請訪問此鏈接

觸控設備

如果目標是觸摸設備,則以下方法將很有用:

document.ontouchcancel = event => TouchInterrupt(event);
document.ontouchend = event => FingerRemoved(event);
document.ontouchmove = event => FingerDragged(event);
document.ontouchstart = event => FingerPlaced(event);

有關觸摸事件的更多信息,請訪問此鏈接

我認為這個解決方案完全解決了你的問題。

您的具體問題可以通過收聽wheel事件來解決,盡管結果不是非常精確。 滾輪事件通常在滾動事件之前觸發,因此此示例有時會在從頁面底部向上滾動時記錄負滾動值:

 const content = document.querySelector('.content'); for (let i = 0; i < 50; i++) { const p = document.createElement('p'); p.textContent = 'Content'; content.append(p); }; content.addEventListener('wheel', e => { const atBottom = content.scrollHeight - content.scrollTop === content.clientHeight; if (atBottom) console.log(e.deltaY); });
 * { padding: 0; margin: 0; box-sizing: border-box; } body { height: 100vh; width: 100%; } .content { overflow-y: scroll; height: 100%; }
 <div class="content"></div>

正如其他人所建議的那樣,對於您的用例來說,更好的方法可能是使用疊加層,您可以在單擊/觸摸時觸發該疊加層,然后滾動到視圖中。 您可能會遇到的一個問題是,Web 瀏覽器上的深度嵌套滾動會變得非常難看,而無需訴諸純 JS 解決方案,這些解決方案也有其自身的性能問題。

這是一個彈出窗口,單擊時會打開並允許您滾動。 當它到達頁面頂部時,它的標題會固定。

 var navbar = document.querySelector('.navbar'), navheader = document.querySelector('.navheader'); // Toggle navbar navheader.addEventListener('click', e => { navbar.classList.toggle('open'); if (!navbar.classList.contains('open')) { navbar.style.overflow = 'hidden'; document.body.style.overflow = ''; navbar.scrollTop = 0; stickTop = false; navbar.classList.remove('sticky'); navbar.style.top = ''; navbar.style.transition = '.2s'; setTimeout(() => { navbar.style.transition = ''; }, 200); } else { navbar.style.overflow = 'overlay'; navbar.style.transition = '.2s'; setTimeout(() => { navbar.style.transition = ''; }, 200); } }) var prevtop = 0; var stickTop = false; // Add scroll listener navbar.addEventListener('scroll', e => { // If navbar is open if (navbar.classList.contains('open')) { if (!stickTop) { navbar.style.top = navbar.getBoundingClientRect().top - navbar.scrollTop + 'px'; } if ((window.innerHeight - navbar.getBoundingClientRect().bottom) >= 0) { document.body.style.overflow = 'hidden'; navbar.style.overflow = 'auto'; navbar.style.top = 0; navbar.classList.add('sticky'); stickTop = true; } if (navbar.scrollTop == 0) { navbar.classList.remove('open'); navbar.style.overflow = 'hidden'; document.body.style.overflow = ''; stickTop = false; navbar.classList.remove('sticky'); navbar.style.top = ''; navbar.style.transition = '.2s'; setTimeout(() => { navbar.style.transition = ''; }, 200); } } })
 body { font-family: sans-serif; } .navbar { position: fixed; top: calc(100vh - 50px); height: 100vh; left: 0; width: 100%; overflow: hidden; } .navbar.open { top: 50vh; } .navcontent { background: black; width: 100%; color: white; } .navcontent p { margin: 0; } .navheader { height: 50px; width: 100%; background: lightblue; cursor: pointer; top: 0; position: sticky; display: flex; justify-content: center; z-index: 1; } .navheader::before { width: 50px; height: 3px; margin-top: 10px; background: white; border-radius: 3px; content: ''; }
 <div class="navbar"> <div class="navheader"></div> <div class="navcontent"><p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p></div> </div> <div class="content"> <p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p> </div>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM