ie9-oninput-polyfill
ie9-oninput-polyfill copied to clipboard
Infinite event loop
Because the polyfill listen to any selectionChange
event to trigger a synthetic input
event, if one is to call setSelectionRange()
from within a input
event handler, then an infinite event loop occurs (setSelectionRange()
does trigger a selectionChange
).
My take, cause I'm aware that this project is not really alive, and also too lazy to do a proper PR
(function ($nav, $doc) {
"use strict";
var $targetInput;
function isTextField($elem) {
return $elem.tagName === "TEXTAREA" || ($elem.tagName === "INPUT" && $elem.type === "text");
}
// using bitwise NOT to compare against -1 as ~(-1) returns 0
// (~indexOf) is similar to (indexOf != -1)
if (~$nav.userAgent.indexOf("MSIE 9")) {
$doc.addEventListener("keydown", function (e) {
// 8 backspace
// 46 delete
if (isTextField(e.target) && ~[8, 46].indexOf(e.which)) {
$targetInput = e.target;
}
});
$doc.addEventListener("cut", function (e) {
// Note: cut is triggered before the action
if (isTextField(e.target)) {
$targetInput = e.target;
}
});
$doc.addEventListener("selectionchange", function () {
var event;
if ($targetInput && $targetInput === $doc.activeElement) {
event = $doc.createEvent("CustomEvent");
event.initCustomEvent("input", true, false, {});
$targetInput.dispatchEvent(event);
$targetInput = null;
}
});
}
})(navigator, document);
It should be simple to just check if (!e.isTrusted) return
before anything else.
Something like:
(function (d) {
if (navigator.userAgent.indexOf('MSIE 9') === -1) return;
d.addEventListener('selectionchange', function(e) {
if (!e.isTrusted) return;
var el = d.activeElement;
if (el.tagName === 'TEXTAREA' || (el.tagName === 'INPUT' && el.type === 'text')) {
var ev = d.createEvent('CustomEvent');
ev.initCustomEvent('input', true, true, {});
el.dispatchEvent(ev);
}
});
})(document);
Can you try it? @agka
Thanks for the quick response.
Alas, that does not the trick. But for other reasons.
If you were to change the value of an input, and then update the caret position within the same field, IE9 can't do it properly during the same "frame"
In the input
event handler I had something like
input.value = strVal;
input.setSelectionRange(caretPos, caretPos);
My guess : IE9 resets the input value to "" first, then somewhat schedules a value change later. Because the input value is empty input.setSelectionRange()
moves the caret to the end of the empty string. When IE9 applies the value, it also remembers to move the caret, but it is moved to the end of the string value.
The behaviour has been fixed in later IE versions
A workaround is to use setTimeout()
to delay the call to setSelectionRange()
input.value = strVal;
setTimeout(function () {
input.setSelectionRange(caretPos, caretPos);
}, 0);
But calling setSelectionRange()
on an input also force the focus back to the input field.
And the last straw, switching focus between inputs does trigger selectionChange
events, before any focus
or blur
events (I checked)
So, with the suggested change, whenever the focus is switched away from an text input, a selectionchange
is fired, which triggers a synthetic input
(ok)
But because my input
handler delays a setSelectionRange()
, the focus is put right back to the text input, thus the user is "trapped".