Android set scrollLeft when updating content in scroll event does not update scrollbar position

Setting scrollLeft resets the scrollbar position and updating the content works as expected , but when it does both at the same time , the scrollbar gets tangled and does not reset.

To see expected behavior and unexpected behavior, view each demo on the device using the touch pad and use the touch pad to scroll left or right inside the wrapper, and then try to do the same on the Android device.

Please note that on a laptop the item will scroll endlessly, on an Android device the item will scroll only until it reaches the originally set β€œmaximum scroll”

What is going to happen:

When the user scrolls left or right, drag the first child to the end of the list of nodes or move the last child to the beginning and reset the scroll position to half the first half of the first child.

The following are my attempts to fix the problem.

  • Set transform: translateX(0px) to .inner here , which had worse behavior than before.

  • the fix in the list is here , which was for the previous error in android, where setting scrollLeft didn't work at all. This did not help solve the problem.

  • wrap.appendChild(inner) in each scroll event, which slowed down scrolling but did not fix the problem as chrome remembers scroll positions. That would be a hack, even if I could make chrome forget the scroll position (it looks like it might be plausible, but it will be another hack)

I understand that I could smell the browser and just switch to jquery ui mobile swipe setup, but I think that if I could make it work, I would not have to use an external library to emulate my own behavior (and native is always better).

 var log = function(event) { var log = document.querySelector('.log'); log.innerHTML = event + "<br>" + log.innerHTML; }; var wrap = document.querySelector('.wrap'); var inner = document.querySelector('.inner'); var items = document.querySelectorAll('.item'); var controlLeft = document.createElement('a'); controlLeft.className = 'control control-left'; controlLeft.href = 'javascript:void(0)'; controlLeft.innerHTML = '&lt;'; controlLeft.onclick = function() { log('click left'); inner.scrollLeft++; }; wrap.appendChild(controlLeft); var controlRight = document.createElement('a'); controlRight.className = 'control control-right'; controlRight.href = 'javascript:void(0)'; controlRight.innerHTML = '&gt;'; controlRight.onclick = function() { log('click right'); inner.scrollLeft--; }; wrap.appendChild(controlRight); var darken1 = document.createElement('div'); var darken2 = document.createElement('div'); darken1.className = 'darken'; darken2.className = 'darken'; items[0].appendChild(darken1); items[2].appendChild(darken2); var getWidth = function(element) { return Number(window.getComputedStyle(element, null).getPropertyValue('width').replace('px', '')) + 1; }; wrap.style.overflow = 'hidden'; inner.style.overflowY = 'hidden'; inner.style.overflowX = 'auto'; wrap.style.height = inner.scrollHeight + 'px'; window.onresize = function() { wrap.style.height = inner.scrollHeight + 'px'; inner.scrollLeft = 0; inner.scrollLeft = getWidth(items[0]) / 2; }; inner.scrollLeft = getWidth(items[0]) / 2; oldScroll = inner.scrollLeft; inner.onscroll = function() { if (inner.scrollLeft < oldScroll) { log('scroll right'); inner.appendChild(inner.querySelector('.item:first-child')); inner.querySelector('.item:first-child').appendChild(darken1); inner.querySelector('.item:nth-child(3)').appendChild(darken2); } else if (inner.scrollLeft > oldScroll) { log('scroll left'); var first = inner.querySelector('.item:first-child'); var last = inner.querySelector('.item:last-child'); inner.insertBefore(last, first); inner.querySelector('.item:first-child').appendChild(darken1); inner.querySelector('.item:nth-child(3)').appendChild(darken2); } inner.scrollLeft = 0; inner.scrollLeft = getWidth(items[0]) / 2; oldScroll = inner.scrollLeft; }; 
 *, *::before, *::after { box-sizing: border-box; } html, body { padding: 0; margin: 0; max-height: 100%; overflow: hidden; } .wrap { position: relative; } .control { font-weight: bold; text-decoration: none; display: inline-block; position: absolute; padding: 10px; background: rgba(255, 255, 255, 0.5); top: 50%; transform: translateY(-50%); color: #FFF; font-size: 20pt; } .control-left { padding-right: 20px; border-top-right-radius: 50%; border-bottom-right-radius: 50%; left: 0; } .control-right { padding-left: 20px; border-top-left-radius: 50%; border-bottom-left-radius: 50%; right: 0; } .inner { font-size: 0; white-space: nowrap; overflow: auto; } .item { position: relative; display: inline-block; font-size: 1rem; white-space: initial; padding-bottom: 33.3333%; width: 50%; } .item .darken { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.8); } .item[data-n="2"] { background-image: url(http://www.lorempixel.com/400/300/animals); background-size: cover; } .item[data-n="3"] { background-image: url(http://www.lorempixel.com/400/300/business); background-size: cover; } .item[data-n="4"] { background-image: url(http://www.lorempixel.com/400/300/cats); background-size: cover; } .item[data-n="5"] { background-image: url(http://www.lorempixel.com/400/300/city); background-size: cover; } .item[data-n="6"] { background-image: url(http://www.lorempixel.com/400/300/food); background-size: cover; } .item[data-n="7"] { background-image: url(http://www.lorempixel.com/400/300/nightlife); background-size: cover; } .item[data-n="8"] { background-image: url(http://www.lorempixel.com/400/300/fashion); background-size: cover; } .item[data-n="9"] { background-image: url(http://www.lorempixel.com/400/300/people); background-size: cover; } .item[data-n="10"] { background-image: url(http://www.lorempixel.com/400/300/nature); background-size: cover; } .item[data-n="11"] { background-image: url(http://www.lorempixel.com/400/300/sports); background-size: cover; } .item[data-n="12"] { background-image: url(http://www.lorempixel.com/400/300/technics); background-size: cover; } .item[data-n="13"] { background-image: url(http://www.lorempixel.com/400/300/transport); background-size: cover; } 
 <div class="wrap"> <div class="inner"> <div class="item" data-n="2"></div> <div class="item" data-n="3"></div> <div class="item" data-n="4"></div> <div class="item" data-n="5"></div> <div class="item" data-n="6"></div> <div class="item" data-n="7"></div> <div class="item" data-n="8"></div> <div class="item" data-n="9"></div> <div class="item" data-n="10"></div> <div class="item" data-n="11"></div> <div class="item" data-n="12"></div> <div class="item" data-n="13"></div> </div> </div> <div class="log"> </div> 
+6
source share
2 answers

To fix this problem temporarily, I wrapped the function in a timeout. The timeout can be as little as 1 millisecond. I don't know why, but changing the contents and setting scrollLeft in the exact scroll event causes the browser to not reset the scroll bar.

( Demo )

 inner.onscroll = function() { window.clearTimeout(window.updateTimeout); window.updateTimeout = window.setTimeout(function() { if (inner.scrollLeft < oldScroll) { log('scroll right'); inner.appendChild(inner.querySelector('.item:first-child')); } else if (inner.scrollLeft > oldScroll) { log('scroll left') var first = inner.querySelector('.item:first-child'); var last = inner.querySelector('.item:last-child'); inner.insertBefore(last, first); } inner.querySelector('.item:first-child').appendChild(darken1); inner.querySelector('.item:nth-child(3)').appendChild(darken2); inner.scrollLeft = 0; inner.scrollLeft = getWidth(items[0]) / 2; oldScroll = inner.scrollLeft; }, 1); }; 

While this fixes the problem, I think it's a hack. I leave this question open to find out if I can get a real answer.

+1
source

Each time you want to change the property that you use to check whether you want to transfer the first or last element, you also change the same information again, so that you get unexpected behavior.

Your trick for adding a timeout gave you the idea of ​​being a solution, because it gives some time to the program, so it can serialize changes to the information.

I made some changes to my code and do not use a timeout. But due to the nature of the scroll event, depending on what configuration the user has in their scroll mode, one number of slides will scroll.

Therefore, I recommend that you add some checking, which may be a timer (hahahahaha ...), but only to switch the Boolean language to allow the scroll handler. Like snnipet below:

 inner.onscroll = function(e) { if (!canScroll) { return; } canScroll = false; setTimeout(function () { canScroll = true; }, 300); if (inner.scrollLeft < oldScrollLeft) { // ... 

Before you ask, the feeling of delay is inner.scrollLeft = 0 then inner.scrollLeft = getWidth(items[0]) / 2; provides is related to this excessive processing that the original program causes the browser to do.

 var log = function(event) { var log = document.querySelector('.log'); log.innerHTML = event + "<br>" + log.innerHTML; }; var wrap = document.querySelector('.wrap'); var inner = document.querySelector('.inner'); var items = document.querySelectorAll('.item'); var controlLeft = document.createElement('a'); controlLeft.className = 'control control-left'; controlLeft.href = 'javascript:void(0)'; controlLeft.innerHTML = '&lt;'; controlLeft.onclick = function() { log('click left'); inner.scrollLeft++; }; wrap.appendChild(controlLeft); var controlRight = document.createElement('a'); controlRight.className = 'control control-right'; controlRight.href = 'javascript:void(0)'; controlRight.innerHTML = '&gt;'; controlRight.onclick = function() { log('click right'); inner.scrollLeft--; }; wrap.appendChild(controlRight); var darken1 = document.createElement('div'); var darken2 = document.createElement('div'); darken1.className = 'darken'; darken2.className = 'darken'; items[0].appendChild(darken1); items[2].appendChild(darken2); var getWidth = function(element) { return Math.floor(Number(window.getComputedStyle(element, null).getPropertyValue('width').replace('px', ''))); }; wrap.style.overflow = 'hidden'; inner.style.overflowY = 'hidden'; inner.style.overflowX = 'auto'; wrap.style.height = inner.scrollHeight + 'px'; var oldScrollLeft = getWidth(items[0]) / 2; var oldScrollWidth = inner.scrollWidth; window.onresize = function() { wrap.style.height = inner.scrollHeight + 'px'; oldScrollWidth = inner.scrollWidth; oldScrollLeft = getWidth(items[0]) / 2; inner.scrollLeft = oldScrollLeft; }; inner.scrollLeft = getWidth(items[0]) / 2; inner.onscroll = function(e) { if (inner.scrollLeft < oldScrollLeft) { log('scroll right'); inner.appendChild(inner.querySelector('.item:first-child')); inner.querySelector('.item:first-child').appendChild(darken1); inner.querySelector('.item:nth-child(3)').appendChild(darken2); } else if (inner.scrollLeft > oldScrollLeft) { log('scroll left'); var first = inner.querySelector('.item:first-child'); var last = inner.querySelector('.item:last-child'); inner.insertBefore(last, first); inner.querySelector('.item:first-child').appendChild(darken1); inner.querySelector('.item:nth-child(3)').appendChild(darken2); inner.scrollLeft = 0; } inner.scrollLeft = oldScrollLeft; }; 
 *, *::before, *::after { box-sizing: border-box; } html, body { padding: 0; margin: 0; max-height: 100%; overflow: hidden; } .wrap { position: relative; } .control { font-weight: bold; text-decoration: none; display: inline-block; position: absolute; padding: 10px; background: rgba(255, 255, 255, 0.5); top: 50%; transform: translateY(-50%); color: #FFF; font-size: 20pt; } .control-left { padding-right: 20px; border-top-right-radius: 50%; border-bottom-right-radius: 50%; left: 0; } .control-right { padding-left: 20px; border-top-left-radius: 50%; border-bottom-left-radius: 50%; right: 0; } .inner { font-size: 0; white-space: nowrap; overflow: auto; } .item { position: relative; display: inline-block; font-size: 1rem; white-space: initial; padding-bottom: 33.3333%; width: 50%; } .item .darken { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.8); } .item[data-n="2"] { background-image: url(http://www.lorempixel.com/400/300/animals); background-size: cover; } .item[data-n="3"] { background-image: url(http://www.lorempixel.com/400/300/business); background-size: cover; } .item[data-n="4"] { background-image: url(http://www.lorempixel.com/400/300/cats); background-size: cover; } .item[data-n="5"] { background-image: url(http://www.lorempixel.com/400/300/city); background-size: cover; } .item[data-n="6"] { background-image: url(http://www.lorempixel.com/400/300/food); background-size: cover; } .item[data-n="7"] { background-image: url(http://www.lorempixel.com/400/300/nightlife); background-size: cover; } .item[data-n="8"] { background-image: url(http://www.lorempixel.com/400/300/fashion); background-size: cover; } .item[data-n="9"] { background-image: url(http://www.lorempixel.com/400/300/people); background-size: cover; } .item[data-n="10"] { background-image: url(http://www.lorempixel.com/400/300/nature); background-size: cover; } .item[data-n="11"] { background-image: url(http://www.lorempixel.com/400/300/sports); background-size: cover; } .item[data-n="12"] { background-image: url(http://www.lorempixel.com/400/300/technics); background-size: cover; } .item[data-n="13"] { background-image: url(http://www.lorempixel.com/400/300/transport); background-size: cover; } 
 <div class="wrap"> <div class="inner"> <div class="item" data-n="2"></div> <div class="item" data-n="3"></div> <div class="item" data-n="4"></div> <div class="item" data-n="5"></div> <div class="item" data-n="6"></div> <div class="item" data-n="7"></div> <div class="item" data-n="8"></div> <div class="item" data-n="9"></div> <div class="item" data-n="10"></div> <div class="item" data-n="11"></div> <div class="item" data-n="12"></div> <div class="item" data-n="13"></div> </div> </div> <div class="log"> </div> 
0
source

Source: https://habr.com/ru/post/986884/


All Articles