Is this change committed?

It always starts with messy code (part 2)

Hello! welcome back to the series where I'll discuss things I learned while writing Knitlify.

Previouse post focused on change event on file input, we are going to keep looking at change event but this time, on <input type="number">


Slow action on the number input

My app has 3 number inputs to specify size and proportion of knit stitch. Every time these numbers are changed, I re-render the image on canvas.

The messy job I did was that I just choose to listen to change event and did not even think about other options.

Here is the thing, at this point, every user-selectable parts had the event listener for change event. File input? change event(see previouse post). Checkbox? change event. Select element? change event. I thought I would just use change event for number input as well.

$pps.addEventListener('change', function () {
  settings.setData = {pps: parseInt(this.value)}
}, false)
$sts.addEventListener('change', function () {
  settings.setData = {sts: parseInt(this.value)}
}, false)

$row.addEventListener('change', function () {
  settings.setData = {row: parseInt(this.value)}
}, false)

$direction.addEventListener('change', function () {
  settings.setData = {direction: this.value}
}, false)

$horizontal.addEventListener('change', function () {
  settings.setData = {horizontal: this.checked}
}, false) 

$vertical.addEventListener('change', function () {
  settings.setData = {vertical: this.checked}
}, false)

Issue: "Sometimes" change event takes a long time to fire

My application seemed to work fine. I was switching between Chrome, Firefox, and Safari to make sure my app worked cross-browser.

Sometimes, I notice a small lag between my click till the new image is drawn, but I'm dealing with large size 2d canvas, drawing many paths and filling colors. That's maybe expected, right? None of those shiny fast WebGL business here.

The app went on live with change event listener. A few hours later, I got a message from Paul Irish.

Paul: "heyyyyyyyyya. have you noticed some weird delay on knitlify's change events when you change the number inputs?"

I mean, what do you do when an engineer who works on Chrome DevTools and frequently talks about web performance messages you that your app has "delay"???

I quickly replied with not-really-answer thinking I could maybe save myself from being shamed for writing messy code, but the conversation goes unexpected direction.

Me: "oh that code is a weekend project and I'm sure it's not performant...(please don't judge me)"

Paul: "haha I read your source already, it's not your perf bug. I looked at trace, I'm gonna file a bug on Chrome."

Turns out, in Chrome, change event on Number input is not fired after the click, if user's mouse didn't move... to explain Paul has created smal app to demonstrate this.

In the video, you can see change event being fired only when I move the mouse cursor.

Is browser variance "a bug"?

Description of change event from MDN says

The change event is fired for `<input>`, `<select>`, and `<textarea>` elements when a change to the element's value is committed by the user. Unlike the input event, the change event is not necessarily fired for each change to an element's value.

The interpretation of "value is committed" seems to differ depending on the browser. When you hold down on ↑↓ buttons to change the value on number input, Safari fires change event on every tick (= 1 click event, many change events). In Firefox, the change event is only fired when you release the click (= 1 click event, 1 change event).

None the less Chrome's way of waiting until "mouse move" seems not intuitive. But the question is... is this a bug or just "each browser is different"?

Me: "I was gonna ask... is this just annoying browser variance or actual 'bug'?"

Paul: "IMO any annoying browser variance should be considered a bug."

I have to admit, I spent many moons writing piles of code just to handle different browser behaviors. I would've never thought to file a bug if Paul didn't mention it.

The bug is now tracked here. Turns out, someone raised the issue before us.

Solution : Switch to input or click events

Since we identified the issue with change event for Chrome, I switched to using input event. Across Chrome/FireFox/Safari, input event seems to be fired on every tick.

This works okay for what I'm doing (for now) If I want to reduce events (= reduce rerendering of canvas) I could switch to click event but then I'd also have to write another event handler for keyboard input...I wasn't gonna do that at the time.

This has been part 2 of "It always starts with messy code". Next post, I'm going to step back from browser quarks and talk about how my code evolved from event-driven to state-driven over the course of prototyping phase.