Consistent scrolling behavior across browsers

I made this pen to simulate the problem.

function createBox() {
  var box = document.createElement("div");
  box.className = "box";
  box.style.backgroundColor = "#"+((1<<24)*Math.random()|0).toString(16);
  
  return box;
}

function prependInnerHTML() {
  console.log('prependInnerHTML'); 
  var element = document.getElementById('scroll');
  var box = createBox();
  element.innerHTML = box.outerHTML + element.innerHTML;
  element.prepend(box);
}

function prependPrepend() {
  console.log('prependPrepend'); 
  var element = document.getElementById('scroll');
  var box = createBox();
  element.prepend(box);
}

function prependPrepend() {
  console.log('prependPrepend'); 
  var element = document.getElementById('scroll');
  var box = createBox();
  element.prepend(box);
}

function prependInsertBefore() {
  console.log('prependInsertBefore'); 
  var element = document.getElementById('scroll');
  var box = createBox();
  element.insertBefore(box, element.firstChild);
}

function scroll() {
  console.log('scroll');
  var detailElement = document.getElementById('details');
  var scrollElement = document.getElementById('scroll');
  
  detailElement.innerHTML = "ScrollTop: " + scrollElement.scrollTop;
}

function clearScrollElement() {
  console.log('clear');
  var element = document.getElementById('scroll');
    while(element.firstChild){
      element.removeChild(element.firstChild);
  }
}
      

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%
}
#details {
  position: fixed;
  top: 10px;
  left: 20px;
  background-color: white;
}
#options {
  position: fixed;
  top: 10px;
  right: 20px;
  background-color: white;
}
#scroll {
  overflow-x: hidden;
  overflow-y: scroll;
  height: 100%;
  width: 100%;
}
.box {
  height: 100vh;
  width: 100%
}
      

<div id="options">
  <span onclick="prependInnerHTML()">InnerHTML</span>
  <span onclick="prependInsertBefore()">InsertBefore</span>
  <span onclick="prependPrepend()">Prepend</span>
  <span onclick="clearScrollElement()">Clear</span>
</div>

<div id="details">
  ScrollTop: 0
</div>

<div id="scroll" onscroll="scroll()"></div>
      

Run codeHide result


The problem is that when an element is added to a scrollable element, it will have different types of behavior in different browsers.

innerHTML:

The first prepend method changes the innerHTML of the scrollable element. This seems to be cross-browser compatible.

The problem is that frameworks like Vue don't use this method internally, it might be using one of the other methods.

PasteBefore with Chrome:

If scrollTop is 0 and we add scrollTop, it will remain zero. If scrollTop is greater than zero, it will adjust the scrollTop to include the height of the added element.

PasteBefore with IE / Edge / Firefox:

Same behavior as innerHTML.

Prepend:

Prepend doesn't seem to be supported in IE / Edge, so I'll skip this one.

Question:

How do I get InsertBefore to behave the same across all browsers without introducing all browser checks?

+3


source to share


1 answer


Try to do this

/**
 * Adds a new element to the top of the container, If after is a valid element then the new element is added after that element
 * @param {HTMLElement} newItem - newItem to be added
 * @param {HTMLElement} container - container
 * @param {HTMLElement} [after=] - optional parameter, states that the new item will be added after this element
 * @returns {HTMLElement}
 */
function addElementToTop(newItem, container, after) {
    if (after instanceof HTMLElement === false || after.parentElement !== container) {
        var first = container.firstElementChild;
    } else {
        first = after.nextElementSibling;
    }
    if (first !== null) {
        container.insertBefore(newItem, first);
    } else {
        container.appendChild(newItem);
    }
    return newItem;
}

      

Here is a simple version

function addElementToTop(newItem, container) {
    var first = container.firstElementChild;
    if (first) container.insertBefore(newItem, first);
    else container.appendChild(newItem);
    return newItem;
}

      



The version below maintains the same viewport but scroll changes

function addElementToTopSteadyScroll(newItem, container) {
    var bot = container.scrollHeight - container.scrollTop;
    var first = container.firstElementChild;
    if (first) container.insertBefore(newItem, first);
    else container.appendChild(newItem);
    container.scrollTop = container.scrollHeight - bot;
    return newItem;
}

      

Whereas the below version supports the same scrolling but change view-port

function addElementToTopFlowScroll(newItem, container) {
    var top = container.scrollTop;
    var first = container.firstElementChild;
    if (first) container.insertBefore(newItem, first);
    else container.appendChild(newItem);
    container.scrollTop = top;
    return newItem;
}

      

0


source







All Articles