Fixing Dev.to's scrollbar bug with a single line of code
So many articles are written after the fact, and the author either forgets or takes for granted the jumps in logic they made. This article was written as I solved the problem, before I even knew I could solve the problem. I hope this gives you a better insight into the bug fixing process, from the very beginning to the very end. Without further ado:
=====================================
I was editing my article when I noticed an extremely annoying thing - each character I typed caused a scrollbar to appear and dissapear. My first instinct was to open up the dev console to inspect it, whereupon I was greeted by this:
-oooooooo/- .+ooooooooo: +ooo+ oooo/
+MMMMMMMMMMm+ -NMMMMMMMMMMs +MMMM: /MMMM/
+MMMNyyydMMMMy /MMMMyyyyyyy/ mMMMd mMMMd
+MMMm :MMMM. /MMMN /MMMM/ /MMMM:
+MMMm .MMMM- /MMMN dMMMm mMMMh
+MMMm .MMMM- /MMMMyyyy+ :MMMM/ +MMMM-
+MMMm .MMMM- /MMMMMMMMy hMMMm NMMMy
+MMMm .MMMM- /MMMMoooo: -MMMM+oMMMM-
+MMMm .MMMM- /MMMN yMMMmNMMMy
+MMMm +MMMM. /MMMN .MMMMMMMM.
+MMMMdddNMMMMo /MMMMddddddd+ sMMMMMMs
+MMMMMMMMMNh: .mMMMMMMMMMMs yMMMMs
.///////:- -/////////- .::.
Hey there! Interested in the code behind dev.to? Well you're in luck - we're open source! Come say hi, tell us what you're debugging, or even lend a hand in our repo - github.com/thepracticaldev/dev.to
Did you find a bug or vulnerability? Check out our bug bounty info here: dev.to/security
I was pleasantly surprised they had a security bug bounty. I proceeded to get sidetracked looking into the bounty program. Then I remembered I had a issue to solve.
I opened up the repo and searched "scrollbar" in their issues. I came across github.com/thepracticaldev/dev.to/issues/3330 which described the exact issue I experienced. Oddly enough it only had one user who reported it - it must be somewhat rare. Or people don't know to go to github to report the issue. My experience with creating AREPL has taught me that for every issue people bother to report, according to telemetry it's probably happened multiple times already.
After confirming that it's a reported issue (with a help wanted label!) I went back to the dev tools. I noticed that the textarea's height style was changing each time I typed.
Before: (no scrollbar)
<textarea style="height: 968px;" class="articleform__body" id="article_body_markdown" placeholder="Body Markdown" name="body_markdown" ></textarea>
After: (scrollbar)
<textarea style="height: 924px;" class="articleform__body" id="article_body_markdown" placeholder="Body Markdown" name="body_markdown"></textarea>
This makes sense - the scrollbar only appears when there's not enough height to display everything. But why was the height changing? I wasn't entering a new line, I was just adding a character to an existing line. Odd.
In the middle of this I had a sudden realization - I could write an article about this! It would serve as a good way to track my thoughts as I solved the issue. The downside is there's no turning back now - If I fail to solve this I'll have a ...
WAIT! Holy crap - I just got the exact same bug:
horrible yellow courtesy of flux
I'm forced to resort to writing this on notepad for the time being. I guess the silver lining is that I can reproduce the issue. I took the following paragraph and pasted it into a new draft. And ... huh. Problem didn't appear there. Then I remembered that according to the issue report it only happens when there is a certain amount of lines. So I added 21 lines before it and I got the problem again š!
The next question is if it happens after a certain number of lines why doesn't everyone run into this issue? Is there a unspoken agreement among dev.to writers not to go above 21 lines ala twitter's character limit? I highly doubt it. There must be something I'm missing. Maybe something related to the text I'm typing.
After playing around with the text for a bit, I found out that if I delete "I'll have a " on the line The downside is there's no turning back now - If I fail to solve this I'll have a
then the problem doesn't appear anymore. The problem only appears when the line goes over the width of the draft.
Now I should be able to create a minimal reproducible example. I opened up a new draft, typed out aaa.... untill it overflowed the line, then added 21 lines. No error š¤.
I fiddled around with the end a bit more and discovered that the error ONLY happens in the very specific scenario where you go over the draft width with space. Any normal character will simply go onto the next line, but you can add as many spaces to an existing line as you wish.
So now I can reproduce it, which is half the battle. Time for debugging!
But before that I want to check something - I noticed in the settings that I am using the V1 editor. Will the problem still appear in the V2 editor?
No. The problem does not appear there. And it's worse than I thought - new users default to the v2 editor, so this problem only affects old farts like me, and only a subset of grandpas at that.
Well, this was a waste of time. :|
On the bright side I can comment in the issue with the solution (switch to v2 editor), so that should help some folks out. And just for the sake of my pride, I'll spend 10 minutes trying to see how I could fix the issue. Starting now.
So it turns out the issue STILL happens with the v2 editor, so it's a good thing I checked again. It's just that in the v2 editor when your spaces go over the draft width your cursor stays at the same spot, so it looks like everything is working. But with some fiddling you can still reproduce the error.
So now let's get to debugging. Finally, you say!
I set a breakpoint for attribute modifications of the textarea element. I edited the element, which caused the height change, which caused the code to stop on the breakpoint. Unfortunately the file was blank, so I couldn't see the code it was stopped at. But I looked at the stacktrace and could see that in there was a file called TextareaAutosize.js
, in a folder called preact-textarea-autosize
, inside node_modules
. So dev.to is using a preact (variant of react) package for their textarea implementation. Googling it led me to github.com/DisplaySweet/preact-textarea-au… which 404's.
sigh
So I went to the next link - the npm package:
The repository link leads to github.com/evenius/react-textarea-autosize, which hasn't been updated in over two years and has no section for issues. Great. However, it is forked from a more popular repo that does have issues and a demo site. I searched the issues but didn't find my scrollbar problem reported there. I went to the demo site, and HOW ABOUT THAT, you can reproduce the problem in their demo site! So the issue might not be with dev.to code - it could be with how they are calling the library or an internal library issue.
At this point it's 1am so I went to bed. Sleep is healthy, y'all.
Me, ready to sleep:
The next day I finished a long day of work at 15Five, read about 100 pages of Scythe, and got back to work. I wrote up what I did yesterday, and in the process found another react textarea package called react-autosize-textarea
. (not to be confused with react-textarea-autosize
). HOW MANY OF THEM ARE THERE?? Even though the textarea script on dev.to was showing up blank, I noticed it was source-mapped from a bundled file, so I clicked on the tiny {} in the bottom left to pretty print it, searched for code matching a recent commit in the library, and confirmed that I was looking at the right one. Whew. It's been a hour, but now I can finally start debugging.
Weren't you going to do that already?
errr, yeah. Anyways:
I started off with trying to debug the bundled minified code, which was a absolute nightmare. I quickly did the sensible thing - gave up, cloned the repo, and made a reproducible example with code I could easily debug.
Or at least that's what I should have done. I went back to trying to debug the bundled code for the next hour.
Fun fact: see that line where the breakpoint is, m=1/0
? That corresponds to this line:
let maxHeight = Infinity;
You read that correctly, in Javascript 1/0 evaluates to infinity! And if you really want to be baffled, try executing this line in the console (press f12 to access):
"b" + "a" + +"a" + "a"
The output is baNaNa. I mean, duh. What else were you expectingāø®. But anyways, Javascript WTF's is a entire book of it's own[^1]. Let's get back on track.
I assumed the code was setting the height every other character, but it appears to be something else. When calculateNodeHeight
is executed the textarea already has a different height. I'm guessing it's something to do in combination with how the native textarea works and the constraining html. The mystery deepens...
I tried comparing the two textarea objects in Winmerge but didn't notice anything fishy.
If this is a html issue it's time to get educated. I read through developer.mozilla.org/en-US/docs/Web/API/E…. Then I had a realization: why even bother with scrollbars in the first place? The browser already has a scrollbar at the edge of the screen. react-textarea-autosize
already expands the textarea to a infinite length[^2]. The scrollbar is entirely unnecessary.
So I googled for "disable scrollbar" and came across this forumn post which reccomended the css style overflow:hidden
. hidden
disables scrollbars - you can read more about overflow
options here. Applying it to the textarea style got rid of the issue! š
Next I experimented with different page layouts - does it still work with half-window width? Quarter window? Mobile? Ipad? The answer was yes, yes, yes, and yes.
Thanks to this lovely feature of the chrome devtools I was able to test all the mobile layouts from my laptop without even needing a phone.
Now comes different browsers. I put it in Internet Explorer and to my horror the page didn't have any scrollbars, meaning the textarea had to have scrollbars! My plan was ruined! CURSE YOU IE![^3]
But then I noticed that the page didn't even have a "save changes" button - it was already totally broken and I didn't have to worry about supporting IE. Bullet dodged.
Then I tried edge. I entered in about 19 lines, but then the vertical scrollbar started flashing randomly when I typed in new lines. Woah. You could also type inside the footer on the bottom - you can even see the cursor in there. So edge is already pretty buggy (why am I not surprised). Adding overflow:hidden
fixed the vertical scrollbar problem and didn't cause any other problems to pop up elsewhere.
Finally I tried Firefox. The problem didn't even appear in firefox without the fix, way to go Firefox!
Now that I confirmed the fix worked I created a PR. No need to even open a editor for this - I just went to github.com/thepracticaldev/dev.to/blob/mas…
, clicked on the edit pencil, made my change, and commited it. Github automatically created the forked repo - from there I clicked Create Pull request, filled in a short template describing the change, and I was done!
Well, not quite. The reviewer had a comment (reviewers always do) requesting before/after images. With ShareX this was quite simple to provide. A couple days later, my PR was merged!
You can check it out yourself - create a new article, right click the textarea and click inspect elemnt, and under .articleform__body
there's a single line of css, overflow: hidden
that prevents the scrollbar from appearing š
So, what can we take from this?
- Bugs can be solved in surprising ways. You should keep your eyes open for workarounds, shortcuts, and other novel ways to entirely bypass the problem. This is NOT an excuse for dirty unreadable hacks. (lookin at you regex, love you honey but you got some problems) Remember that premature optimization is the root of all evil and that you will be reading your own code far more than writing it.
- You don't need any fancy knowledge of algorithms or expensive macbooks to get into open source. All you need is a web browser. With the advances of software like repl.it and others practically everything can be done in the cloud. You can use all your free hardware space for sexy pictures of cucumbers (no judgement)
- More companies should open-source their code. You get free contributions and free PR. In some cases contributors can become employees, saving you thousands in hiring costs. Finally open-sourcing gets rid of the false sense of security created by "security through obscurity"[^4] - by open-sourcing you HAVE to keep secure for fear of someone seeing your code. Even though in practice only a few people will bother looking for vulnerabilities and most of them will probably be security researchers or bounty hunters. You DO have a security bounty, right?
- Most of the work in fixing a bug comes from reproducing it, finding out why it's happening, finding a fix, and testing the fix. Coding is a suprisingly small part.
- When you run into a problem, try to find an associated github repo and report the problem to the maintainers. Just by including detailed reproduction steps you're doing them a massive favor[^5], and sometimes the mere act of researching the problem leads you to discover a workaround or solution (or that you're a idiot doing something stupid). Last of all, if the code is open source, fixing it is a possibility! It just takes some hard work šØ.
[^1]: see github.com/denysdovhan/wtfjs
[^2]: If you wana get technical, you'll "only" be able to go down a couple billion lines before you run out of memory. With utf-8 encoding each ASCII character is a byte so 4 gigabytes of ram is enough to hold 4 billion characters. The more you know! ~~~ ā
[^3]: I was trying to find a picture for this and came across this glorious vintage meme. It was so wonderfully terrible I almost had to include it.
[^4]: Note that obscurity is fine as a defense in depth measure, you just shouldn't rely on it.
[^5]: A good bug report has 5 parts: A. summary B. reproduction steps C. expected result D. actual result E. current settings (ex: windows,chrome) and if you're really going for gold F. pictures/video of the problem. A report like this will make developers sing your praises to the high heavens š. Most issues only have a badly written part A. and it can be frustrating to even understand what's going on.