Well, the bad news is that I suspect this is a Firefox bug. The good news is, I figured out what's causing it and how to work around it.
Basically, if you remove an <input>
element from the DOM while it has the focus, then no focusout
event will be sent for that element, and I suspect that some internal focus-related code also fails to run. In particular, if you then immediately call .focus()
on a <textarea>
(as the Markdown editor code does) it seems as if Firefox gets confused about what kind of an element has the focus and still thinks we're editing a single-line <input>
where the up and down arrow keys don't work.
Or I might be wrong, and it might be some weird keypress event handler in SE code that gets confused. But I suspect Firefox here, since 1) this bug does not occur on Chrome, and 2) I've looked very hard for that keypress handler, and I just can't find one.
In any case, explicitly calling .blur()
on the URL input box before removing the dialog from the DOM fixes this bug.
Here's a single-line patch against Markdown.Editor.js that should solve this:
--- Markdown.Editor.js 2015-09-19 00:02:09.762614897 +0300
+++ Markdown.Editor.new.js 2015-09-19 00:01:19.362615469 +0300
@@ -1102,6 +1102,7 @@
text = 'http://' + text;
}
+ input.blur(); // work around Firefox bug, see https://meta.stackexchange.com/q/264307
dialog.parentNode.removeChild(dialog);
callback(text);
I'd submit this directly to the Pagedown project, but the Google Code project is frozen, and AFAIK there isn't an official GitHub repo yet. Hopefully balpha or someone will see this here and can apply it.
Ps. The way I ended up fixing this in SOUP is a bit more thorough:
var proto = document.body;
while ( proto && proto.removeChild && !proto.hasOwnProperty('removeChild') ) {
proto = Object.getPrototypeOf( proto );
}
if ( !proto || !proto.removeChild ) return;
var oldRemoveChild = proto.removeChild;
proto.removeChild = function ( removed ) {
var active = document.activeElement, node = active;
while ( node && node !== removed ) node = node.parentNode;
if ( node ) active.blur();
return oldRemoveChild.apply( this, arguments );
};
Basically, this patches Node.removeChild()
so that, if the currently focused element is a child of the element being removed, it will be properly defocused before it gets removed from the DOM. This seems to fix the problem, and hopefully may also proactively fix similar bugs lurking in other parts of the SE interface.