guy
7 years ago
12 changed files with 1476 additions and 194 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,145 @@
|
||||
/** |
||||
* jQuery "splendid textchange" plugin |
||||
* http://benalpert.com/2013/06/18/a-near-perfect-oninput-shim-for-ie-8-and-9.html
|
||||
* |
||||
* (c) 2013 Ben Alpert, released under the MIT license |
||||
*/ |
||||
|
||||
(function($) { |
||||
|
||||
var testNode = document.createElement("input"); |
||||
var isInputSupported = "oninput" in testNode && |
||||
(!("documentMode" in document) || document.documentMode > 9); |
||||
|
||||
var hasInputCapabilities = function(elem) { |
||||
// The HTML5 spec lists many more types than `text` and `password` on
|
||||
// which the input event is triggered but none of them exist in IE 8 or
|
||||
// 9, so we don't check them here.
|
||||
// TODO: <textarea> should be supported too but IE seems to reset the
|
||||
// selection when changing textarea contents during a selectionchange
|
||||
// event so it's not listed here for now.
|
||||
return elem.nodeName === "INPUT" && |
||||
(elem.type === "text" || elem.type === "password"); |
||||
}; |
||||
|
||||
var activeElement = null; |
||||
var activeElementValue = null; |
||||
var activeElementValueProp = null; |
||||
|
||||
/** |
||||
* (For old IE.) Replacement getter/setter for the `value` property that |
||||
* gets set on the active element. |
||||
*/ |
||||
var newValueProp = { |
||||
get: function() { |
||||
return activeElementValueProp.get.call(this); |
||||
}, |
||||
set: function(val) { |
||||
activeElementValue = val; |
||||
activeElementValueProp.set.call(this, val); |
||||
} |
||||
}; |
||||
|
||||
/** |
||||
* (For old IE.) Starts tracking propertychange events on the passed-in element |
||||
* and override the value property so that we can distinguish user events from |
||||
* value changes in JS. |
||||
*/ |
||||
var startWatching = function(target) { |
||||
activeElement = target; |
||||
activeElementValue = target.value; |
||||
activeElementValueProp = Object.getOwnPropertyDescriptor( |
||||
target.constructor.prototype, "value"); |
||||
|
||||
Object.defineProperty(activeElement, "value", newValueProp); |
||||
activeElement.attachEvent("onpropertychange", handlePropertyChange); |
||||
}; |
||||
|
||||
/** |
||||
* (For old IE.) Removes the event listeners from the currently-tracked |
||||
* element, if any exists. |
||||
*/ |
||||
var stopWatching = function() { |
||||
if (!activeElement) return; |
||||
|
||||
// delete restores the original property definition
|
||||
delete activeElement.value; |
||||
activeElement.detachEvent("onpropertychange", handlePropertyChange); |
||||
|
||||
activeElement = null; |
||||
activeElementValue = null; |
||||
activeElementValueProp = null; |
||||
}; |
||||
|
||||
/** |
||||
* (For old IE.) Handles a propertychange event, sending a textChange event if |
||||
* the value of the active element has changed. |
||||
*/ |
||||
var handlePropertyChange = function(nativeEvent) { |
||||
if (nativeEvent.propertyName !== "value") return; |
||||
|
||||
var value = nativeEvent.srcElement.value; |
||||
if (value === activeElementValue) return; |
||||
activeElementValue = value; |
||||
|
||||
$(activeElement).trigger("textchange"); |
||||
}; |
||||
|
||||
if (isInputSupported) { |
||||
$(document) |
||||
.on("input", function(e) { |
||||
// In modern browsers (i.e., not IE 8 or 9), the input event is
|
||||
// exactly what we want so fall through here and trigger the
|
||||
// event...
|
||||
if (e.target.nodeName !== "TEXTAREA") { |
||||
// ...unless it's a textarea, in which case we don't fire an
|
||||
// event (so that we have consistency with our old-IE shim).
|
||||
$(e.target).trigger("textchange"); |
||||
} |
||||
}); |
||||
} else { |
||||
$(document) |
||||
.on("focusin", function(e) { |
||||
// In IE 8, we can capture almost all .value changes by adding a
|
||||
// propertychange handler and looking for events with propertyName
|
||||
// equal to 'value'.
|
||||
// In IE 9, propertychange fires for most input events but is buggy
|
||||
// and doesn't fire when text is deleted, but conveniently,
|
||||
// selectionchange appears to fire in all of the remaining cases so
|
||||
// we catch those and forward the event if the value has changed.
|
||||
// In either case, we don't want to call the event handler if the
|
||||
// value is changed from JS so we redefine a setter for `.value`
|
||||
// that updates our activeElementValue variable, allowing us to
|
||||
// ignore those changes.
|
||||
if (hasInputCapabilities(e.target)) { |
||||
// stopWatching() should be a noop here but we call it just in
|
||||
// case we missed a blur event somehow.
|
||||
stopWatching(); |
||||
startWatching(e.target); |
||||
} |
||||
}) |
||||
|
||||
.on("focusout", function() { |
||||
stopWatching(); |
||||
}) |
||||
|
||||
.on("selectionchange keyup keydown", function() { |
||||
// On the selectionchange event, e.target is just document which
|
||||
// isn't helpful for us so just check activeElement instead.
|
||||
//
|
||||
// 90% of the time, keydown and keyup aren't necessary. IE 8 fails
|
||||
// to fire propertychange on the first input event after setting
|
||||
// `value` from a script and fires only keydown, keypress, keyup.
|
||||
// Catching keyup usually gets it and catching keydown lets us fire
|
||||
// an event for the first keystroke if user does a key repeat
|
||||
// (it'll be a little delayed: right before the second keystroke).
|
||||
// Other input methods (e.g., paste) seem to fire selectionchange
|
||||
// normally.
|
||||
if (activeElement && activeElement.value !== activeElementValue) { |
||||
activeElementValue = activeElement.value; |
||||
$(activeElement).trigger("textchange"); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
})(jQuery); |
Loading…
Reference in new issue