I'm using a div to format and display the text from a textarea of equal dimensions and I need them to be permanently in sync. However, I haven't been able to synchronize their respective scrollTops after the input text goes past the bottom of the textarea.
My process has been similar to the one described here, however I can't get his solution to work on my project.
Here's a demo and snippets of the minimum relevant code:
<section>
  <div class="input-text__container">
    <div id="input-text--mirror" class="input-text"></div>
      <textarea
        id="input-text--original"
        cols="30"
        rows="6"
        autofocus
        class="input-text"
        placeholder="Enter your text here"
        autocomplete="off"
        autocorrect="off"
        spellcheck="false"
      ></textarea>
  </div>
<section>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap');
html {
  font-size: 62.5%;
  box-sizing: border-box;
  scroll-behavior: smooth;
}
*,
*::after,
*::before {
  margin: 0;
  padding: 0;
  border: 0;
  box-sizing: inherit;
  vertical-align: baseline;
}
body {
  height: 100vh;
}
section {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  padding: 1rem;
}
.input-text__container {
  width: 100%;
  position: relative;
  flex: 1;
}
.input-text {
  width: 100%;
  height: 100%;
  position: absolute;
  font-size: 3.2rem;
  overflow-wrap: break-word;
  font-family: "Inter";
}
#input-text--mirror {
  background-color: #e9ecf8;
  color: #0a3871;
  overflow: hidden;
}
#input-text--original {
  background-color: transparent;
  -webkit-text-fill-color: transparent;
  resize: none;
  outline: none;
  -ms-overflow-style: none; /* IE and Edge */
  scrollbar-width: none; /* Firefox */
}
#input-text--original::placeholder {
  color: #e9ecf8;
  -webkit-text-fill-color: #052051;
}
#input-text--original::selection {
  -webkit-text-fill-color: #ffffff;
}
.invalid {
  font-weight: 400;
  color: #ff0000;
}
#input-text--original::-webkit-scrollbar {
  display: none;
}
let invalidInput = false;
const patterns = {
  valid: "a-z ",
  invalid: "[^a-z ]",
  linebreaks: "\r|\r\n|\n",
};
const textIn = document.querySelector("#input-text--original");
const mirror = document.querySelector("#input-text--mirror");
function validateInput(string, className) {
  let anyInvalidChar = false;
  // Generate regular expressions for validation
  const regExpInvalids = new RegExp(patterns.invalid, "g");
  const regExpLinebreaks = new RegExp(patterns.linebreaks);
  // Generate innerHTML for mirror
  const mirrorContent = string.replace(regExpInvalids, (match) => {
    if (regExpLinebreaks.test(match)) {
      return "<br/>";
    } else {
      anyInvalidChar = true;
      return `<span class=${className}>${match}</span>`;
    }
  });
  // Update mirror
  mirror.innerHTML = mirrorContent;
  return anyInvalidChar;
}
textIn.addEventListener("input", (e) => {
  const plain = textIn.value;
  const newInputValidity = validateInput(plain, "invalid");
  mirror.scrollTop = textIn.scrollTop;
});
textIn.addEventListener(
  "scroll",
  () => {
    mirror.scrollTop = textIn.scrollTop;
  },
  { passive: true }
);
On a desktop screen typing the first 8 natural numbers in a column should be enough to reproduce the issue.
The last thing I checked, but perhaps the most relevant so far was this. It seems to deal with the exact same issue on React, but I'm afraid I don't know how to adapt that solution to Vanilla JavaScript, since I'm just starting to learn React. Please, notice, I'm trying to find a solution that doesn't depend on libraries like jQuery or React.
Besides that, I tried the solution described in the aforementioned blog, by replacing return "<br/>"; with return "<br/> "; in my validateInput function but that didn't work. I also added a conditional to append a space to plain in const plain = textIn.value; in case the last char was a linebreak, but I had no luck.
I also included console.log commands before and after mirror.scrollTop = textIn.scrollTop; in the textIn scroll handler to track the values of each scrollTop and even when they were different, the mirror scrollTop wasn't updated. I read it might be because divs weren't scrollable by default, but adding "overflow: scroll" to its styles didn't fix the problem either.
I read about other properties related to scrollTop, like offsetTop and pageYOffset, but they're either read-only or not defined for divs.
I've reviewed the following posts/sites, too, but I've still haven't been able to fix this problem.
- https://codepen.io/Goweb/pen/rgrjWx
 - https://stackoverflow.com/questions/68092068/making-two-textareas-horizontally-scroll-in-sync
 - Scrolling 2 different elements in same time
 - React : setting scrollTop property of div doesn't work
 - sync scrolling of textarea input and highlighting container
 - .scrollTop(0) not working for getting a div to scroll to the top
 - How to attach a scroll event to a text input?
 
I no longer remember what else I've reviewed, but nothing has worked and I no longer know what else to do. Thank you for your attention and help.