loading...

The comment box resize bug

yujiri8 profile image Ryan Westlund ・3 min read

Today I'll be telling the story of an interesting bug I'd given up on (I identified the problem but didn't see a way around it), then today I just happened to think of the solution, and fixed it.

So a long time ago, I used a couple lines of Javascript to make my site's comment entry box autoexpand when it would gain a vertical scrollbar. The solution was to set textarea.style.height = textarea.scrollHeight + 2 + 'px'; on an event handler for input. It was a well-liked improvement.

Later I realized that it didn't auto*shrink* - it would expand if you entered a lot, but if you deleted it, it would stay big. If Reddit could do better than that, so could I. Problem was, scrollHeight didn't return the height of the contents but the maximum of the height of the contents and of the textarea. There didn't seem to be an alterative that did the former.

Not to worry. Just give it textarea.style.height = 'inherit' first. (I don't remember my reasoning behind that over 0, but it seems to work well.) That made it always contract first, so scrollHeight would reveal the height of the contents. It was only one more line and I was happy. The contracting step was completely invisible.

Then here's the bug: a user reported this:

A comment box 'bug' (odd interaction) I had writing this comment, if you have a long comment, scroll to the bottom, and type something, the view resets so that the line of where you last typed is at the bottom of the screen, even if it was already on-screen and didn't need any scrolling to. It's a bit jarring. And also Ctrl+Z resets your view back to the top of the comment.

I wasn't able to reproduce this at first, but trying under other circumstances later, I was. It happened if you were at the bottom of the page (as opposed to just the bottom of the comment box). I didn't realize at the time what change had introduced this bug.

Turns out, it was caused by the line textarea.style.height = 'inherit'. When the textarea contracted, layout was recalculated, reducing the document height, forcing the viewport to scroll up to still be on the page, before the textarea regained its size.

I couldn't find a way to solve it for a couple weeks, so I pretty much gave up. There was just no way to get the content height without messing it up. I also didn't think it was quite worth reverting the change that made it shrink and losing the benefits.

It was today I was thinking about it a bit more and got an idea out of the ether of my mind: what if, before contracting it, I added a margin-bottom equal to its height so it would never contract the document height? I would reset the margin-bottom at the end of the event handler. I tried it and as far as we can tell, the bug is gone.

So that's the story of the comment box resize bug. As an epilogue, the full function as of this writing is:

// A global utility to make a textarea grow when necessary.
window.autogrow = e => {
    const textarea = e.target;
    // Temporarily add a bottom margin equal to the height of the textarea.
    // This prevents a glitch that scrolls the viewport upward when the textarea contracts.
    const prevMarginBottom = textarea.style.marginBottom;
    textarea.style.marginBottom = textarea.scrollHeight + 'px';
    // We have to clear the height first so it can also shrink when text is deleted.
    textarea.style.height = 'inherit';
    textarea.style.height = textarea.scrollHeight + 2 + 'px';
    textarea.style.marginBottom = prevMarginBottom;
}

Discussion

pic
Editor guide