Android 在滚动事件更新内容时设置 scrollLeft 不更新滚动条位置

Android set scrollLeft while updating content on scroll event does not update scrollbar position

Setting scrollLeft does reset the scroll bar position and updating the content works as expected but when doing both at the same time 滚动条变得混乱并且没有重置。

要查看预期行为与意外行为,请在带有触摸板的设备上查看每个演示,并使用触摸板在包装器内向左或向右滚动,然后尝试在 android 设备上做同样的事情.

请注意,在笔记本电脑上,该元素会无限滚动,在 android 设备上,该元素只会滚动,直到达到初始设置 "max scroll"

应该发生什么:

当用户向左或向右滚动时,将第一个子元素移动到nodeList的末尾或将最后一个子元素移动到开头并将滚动位置重置为第一个子元素的一半。

以下是我解决问题的尝试

  1. .inner see here 上设置 transform: translateX(0px),其行为比以前更糟。

  2. 列出的修复 here 是针对 android 中的先前错误,其中设置 scrollLeft 根本不起作用。这对问题根本没有帮助。

  3. wrap.appendChild(inner) 在每个滚动事件上,这会减慢向下滚动但没有解决问题,因为 chrome 会记住滚动位置。即使我可以让 chrome 忘记滚动位置,这也将是一个 hack(这看起来似乎是合理的,但会是另一个 hack)

我意识到我可以嗅探浏览器并恢复到 jquery ui 移动滑动设置,但我认为如果我能让它工作,我就不必使用模拟本机行为的外部库(本机总是更好)。

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>

为了暂时解决这个问题,我将函数包装在超时中。超时可以短至 1 毫秒。我不知道为什么,但是在滚动的确切事件中更改内容和设置 scrollLeft 会导致浏览器不重置滚动条。

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

虽然这确实 "fix" 问题,但我认为这是一个 hack。我让这个问题悬而未决,看看我是否能得到真正的答案。

每次你想修改你用来检查是否需要移动第一个或最后一个元素的 属性 时,你也会再次修改相同的信息,所以你会得到一个意想不到的结果行为。

你添加超时的技巧给人的印象是解决方案,因为它给了程序一些时间,所以它可以序列化信息更改。

我对您的代码进行了一些更改,它没有使用超时。但是,由于滚动事件的性质,根据用户在其滚动行为中的配置,将滚动不同数量的幻灯片。

所以我建议你添加一些验证,它可以是一个计时器(哈哈哈哈......),但只是切换一个布尔值让滚动处理程序去。喜欢下面的截图:

inner.onscroll = function(e) {
    if (!canScroll) {
        return;
    }

    canScroll = false;

    setTimeout(function () {
        canScroll = true;
    }, 300);

    if (inner.scrollLeft < oldScrollLeft) {
    // ...

在你问之前,inner.scrollLeft = 0 然后 inner.scrollLeft = getWidth(items[0]) / 2; 提供的延迟感是因为原始程序强制浏览器进行的这种过度处理。

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>