Oliver
Oliver

Reputation: 19

input.scrollLeft behaves unexpectedly in Chome & Firefox

We are trying to build an input-field with syntax highlighting where the input is hidden but an overlaying div with the text / value is shown. This works perfectly so far. We've already tried the contenteditable approach mentioned here, but it ran into various issues (e.g. handling cursor position and IE 11) that seem solvable but rather messy.

Since we have to handle the overflow (and a htmlInput is scrolling automatically), we're trying to set the scrollLeft of the overlay-container the same as the scrollLeft of the input to have them perfectly matched.

However, we're experiencing various inconsistencies between browsers on how the scrollLeft is applied.

Curiously enough, IE11 works flawlessly. In Firefox (68.0.2), the overlay is consistently off by about a single character, where as in Chrome (76.0.3809.100) the difference between the offsets grows larger the more you type. See see the example code below or this CodePen for a demonstration of the Firefox and Chrome issues. As soon as the input begins to overflow, the two fields will become misaligned to various degrees.

var input = document.getElementById('input');
var container = document.getElementById('overlay');

input.addEventListener('input', (event) => {
  setTimeout(() => {
    container.innerHTML = event.target.value;
    container.scrollLeft = input.scrollLeft;
  })
});
#input {
  font: 400 1rem sans-serif;
  border: 1px solid;
  width: 200px;
  height: 20px;
  padding: 0;
}

#overlay {
  margin-top: 1rem;
  font: 400 1rem sans-serif;
  width: 200px;
  height: 20px;
  overflow: hidden;
  white-space: pre;
  border: 1px solid;
}
<input type="text" id="input">
<div id="overlay"></div>

How does the scrollLeft behaviour differ between browsers, and how can we ensure consistency between input and overlay position?

Update

The problem only seems to happen in Chrome on Windows10. We used textarea (styled like an input) instead of input and now it works perfectly.

Upvotes: 0

Views: 238

Answers (1)

Kaiido
Kaiido

Reputation: 137044

For Firefox, you apparently will need to wait more than 0ms. A 50ms delay seems to work fine for me.

var input = document.getElementById('input');
var container = document.getElementById('overlay');

input.addEventListener('input', (event) => {
  setTimeout(() => {
    container.innerHTML = event.target.value;
    container.scrollLeft = input.scrollLeft;
  }, 50)
});
#input {
  font: 400 1rem sans-serif;
  border: 1px solid;
  width: 200px;
  height: 20px;
  padding: 0;
}

#overlay {
  margin-top: 1rem;
  font: 400 1rem sans-serif;
  width: 200px;
  height: 20px;
  overflow: hidden;
  white-space: pre;
  border: 1px solid;
}
<input type="text" id="input">
<div id="overlay"></div>

For Chrome, I couldn't reproduce on v76 on macOS, so I can't be sure if the delay trick is enough there too.

Upvotes: 1

Related Questions