Stephen Gilmore

Auto expanding html textarea via AI... sort of

Programming September 3rd, 2024 2 minute read.

I've been getting a bit tired of editing content on this website and dragging the textarea boxes bigger every time I write more than a few quick markdown sentences. I figured this was probably an easy task for AI. Like most things with AI, it was close, but fell a little short.

The prompt:

Can you write the js to make an auto-expanding textarea?

Answer:

function createAutoExpandingTextarea() {
  const textarea = document.createElement('textarea');
  textarea.style.overflow = 'hidden';
  textarea.style.resize = 'none';

  function adjustHeight() {
    textarea.style.height = 'auto';
    textarea.style.height = textarea.scrollHeight + 'px';
  }

  textarea.addEventListener('input', adjustHeight);

  // Initial adjustment
  setTimeout(adjustHeight, 0);

  return textarea;
}

// Usage
const container = document.getElementById('textarea-container');
const autoExpandingTextarea = createAutoExpandingTextarea();
container.appendChild(autoExpandingTextarea);

The result:

It mostly works. The text box does expand, but the screen was jumped or jittered everytime a newline was added.

A better solution:

Stackoverflow.com had a better solution. Solving the jump issue required some magic with the window size that I don't 100% understand.

After a bit of fiddling, I came up with this reusable bit of javascript that can simply be called with resizeTextarea("id_content").

function resizeTextarea(textareaId) {
  var observe;
  if (window.attachEvent) {
    observe = function (element, event, handler) {
      element.attachEvent('on' + event, handler);
    };
  } else {
    observe = function (element, event, handler) {
      element.addEventListener(event, handler, false);
    };
  }

  var text = document.getElementById(textareaId);

  if (!text) {
    console.error(`Textarea with id "${textareaId}" not found.`);
    return;
  }

  function resize() {
    var scrollLeft = window.pageXOffset ||
      (document.documentElement || document.body.parentNode || document.body).scrollLeft;
    var scrollTop = window.pageYOffset ||
      (document.documentElement || document.body.parentNode || document.body).scrollTop;
    text.style.height = "auto";
    text.style.height = text.scrollHeight + 'px';
    window.scrollTo(scrollLeft, scrollTop);
  }

  /* 0-timeout to get the already changed text */
  function delayedResize() {
    window.setTimeout(resize, 0);
  }

  observe(text, 'change', resize);
  observe(text, 'cut', delayedResize);
  observe(text, 'paste', delayedResize);
  observe(text, 'drop', delayedResize);
  observe(text, 'keydown', delayedResize);

  text.focus();
  text.select();
  resize();
}