Inconsistent Behavior with Delete Key events in Firefox 68+

This page illustrates a bug in Mozilla Firefox v68+.

The following input has 2 event listeners, one for keydown and one for input. In browsers, when pressing keys, the keydown event will fire, then the input event will fire. This is the case for the delete key as well. Under normal circumstances, if the value of the input is modified in the event listener for keydown, the cursor selection will be put at the end of the input and the input event will not fire. You can mitigiate this by setting the selection of the cursor after the value is modified. For regular keys, this works just fine in all browsers. However, in the latest version of Firefox, when the delete key (to remove the character after the cursor, fn + delete on a Mac, just delete on a PC) is used, the input event never fires, even when adjusting the selection.

For an example of the expected behavior, do the following in Chrome.

For an example of the buggy behavior, do the following in Firefox 68+.

Admittedly, this is a bit of a contrived example. Where my team is seeing this behavior is in our input formatting library to automatically add spaces to credit and debit card numbers. Our integration tests started failing in Firefox 68 because the 5 in 1234 56 code no longer be deleted with the delete key, because the input event would not fire as transforms were applied to the input's value.

Input with Event Listeners

Event Log


    

JavaScript Code Runnning on this Page


var input = document.getElementById('input');
var log = document.getElementById('log');

input.addEventListener('keydown', function () {
  writeLog('keydown event fired');
  
  applyTransform(function (val) {
    return val.toUpperCase();
  });
});

input.addEventListener('input', function () {
  writeLog('input event fired');
  
  applyTransform(function (val) {
    return val.toLowerCase();
  });
});

function applyTransform(transform) {
  var start = input.selectionStart;
  var end = input.selectionEnd;
  
  input.value = transform(input.value);
  
  input.setSelectionRange(start, end);
}

function writeLog(message) {
  log.innerText = log.innerText + message + '\n';
}