javascript - style - title css
Acción de disparo en el cambio programático a un valor de entrada (6)
Mi objetivo es observar un valor de entrada y desencadenar un controlador cuando su valor se modifica mediante programación . Solo lo necesito para navegadores modernos.
He probado muchas combinaciones usando defineProperty
y esta es mi última iteración:
var myInput=document.getElementById("myInput");
Object.defineProperty(myInput,"value",{
get:function(){
return this.getAttribute("value");
},
set:function(val){
console.log("set");
// handle value change here
this.setAttribute("value",val);
}
});
myInput.value="new value"; // should trigger console.log and handler
Esto parece hacer lo que espero, pero se siente como un truco ya que estoy anulando la propiedad de valor existente y jugando con el estado dual del value
(atributo y propiedad). También rompe el evento de change
que no parece gustar la propiedad modificada.
Mis otros intentos:
- un bucle setTimeout / setInterval, pero esto tampoco está limpio
- varios
watch
yobserve
polifills, pero se rompen para una propiedad de valor de entrada
¿Cuál sería una forma adecuada de lograr el mismo resultado?
Demostración en vivo: http://jsfiddle.net/L7Emx/4/
[Editar] Para aclarar: Mi código está viendo un elemento de entrada donde otras aplicaciones pueden enviar actualizaciones (como resultado de llamadas ajax, por ejemplo, o como resultado de cambios en otros campos). No tengo control sobre cómo las otras aplicaciones envían actualizaciones, solo soy un observador.
[Editar 2] Para aclarar lo que quiero decir con "navegador moderno", me encantaría con una solución que funciona en IE 11 y Chrome 30.
[Actualización] Demo actualizada basada en la respuesta aceptada: http://jsfiddle.net/L7Emx/10/
El truco sugerido por @ mohit-jain es agregar una segunda entrada para la interacción del usuario.
Solo lo necesito para navegadores modernos.
¿Qué tan moderno te gustaría ir? Ecma Script 7 (6 se hará definitivo en diciembre) puede contener Object.observe . Esto le permitiría crear observables nativos. Y sí, puedes correrlo! ¿Cómo?
Para experimentar con esta función, debe habilitar el indicador Habilitar JavaScript experimental en Chrome Canary y reiniciar el navegador. La bandera se puede encontrar en
''about:flags''
Más información: lee esto .
Así que sí, esto es altamente experimental y no está listo en el conjunto actual de navegadores. Además, aún no está completamente listo y no está al 100% si llega a ES7, y la fecha final de ES7 aún no está establecida. Aún así, quería hacerle saber para uso futuro.
Escribí el siguiente Gist un rato, que permite escuchar eventos personalizados en todos los navegadores (incluido IE8 +).
Eche un vistazo a cómo escucho onpropertychange
en IE8.
util.listenToCustomEvents = function (event_name, callback) {
if (document.addEventListener) {
document.addEventListener(event_name, callback, false);
} else {
document.documentElement.attachEvent(''onpropertychange'', function (e) {
if(e.propertyName == event_name) {
callback();
}
}
};
No estoy seguro de que la solución IE8 funcione en todos los navegadores, pero podría establecer un eventlistener
de eventlistener
falso en el value
de propiedad de su entrada y ejecutar una devolución de llamada una vez que el valor de los cambios de value
desencadenados por onpropertychange
.
Lo siguiente funciona en todos los lugares en los que lo he probado, incluido IE11 (incluso en el modo de emulación IE9).
Lleva su idea defineProperty
un poco más allá al encontrar el objeto en la cadena de prototipos del elemento de entrada que define el establecedor de .value
y al modificar este configurador para desencadenar un evento (lo he llamado modified
en el ejemplo), mientras se mantiene el comportamiento anterior .
Cuando ejecute el fragmento de código a continuación, puede escribir / pegar / lo que sea en el cuadro de entrada de texto, o puede hacer clic en el botón que agrega " more"
al valor .value
del elemento de .value
. En cualquier caso, el contenido de <span>
se actualiza sincrónicamente.
Lo único que no se maneja aquí es una actualización causada por la configuración del atributo. Si lo desea, puede manejar eso con un MutationObserver
, pero tenga en cuenta que no existe una relación de uno a uno entre .value
y el atributo de value
(este último es solo el valor predeterminado para el primero).
// make all input elements trigger an event when programmatically setting .value
monkeyPatchAllTheThings();
var input = document.querySelector("input");
var span = document.querySelector("span");
function updateSpan() {
span.textContent = input.value;
}
// handle user-initiated changes to the value
input.addEventListener("input", updateSpan);
// handle programmatic changes to the value
input.addEventListener("modified", updateSpan);
// handle initial content
updateSpan();
document.querySelector("button").addEventListener("click", function () {
input.value += " more";
});
function monkeyPatchAllTheThings() {
// create an input element
var inp = document.createElement("input");
// walk up its prototype chain until we find the object on which .value is defined
var valuePropObj = Object.getPrototypeOf(inp);
var descriptor;
while (valuePropObj && !descriptor) {
descriptor = Object.getOwnPropertyDescriptor(valuePropObj, "value");
if (!descriptor)
valuePropObj = Object.getPrototypeOf(valuePropObj);
}
if (!descriptor) {
console.log("couldn''t find .value anywhere in the prototype chain :(");
} else {
console.log(".value descriptor found on", "" + valuePropObj);
}
// remember the original .value setter ...
var oldSetter = descriptor.set;
// ... and replace it with a new one that a) calls the original,
// and b) triggers a custom event
descriptor.set = function () {
oldSetter.apply(this, arguments);
// for simplicity I''m using the old IE-compatible way of creating events
var evt = document.createEvent("Event");
evt.initEvent("modified", true, true);
this.dispatchEvent(evt);
};
// re-apply the modified descriptor
Object.defineProperty(valuePropObj, "value", descriptor);
}
<input><br><br>
The input contains "<span></span>"<br><br>
<button>update input programmatically</button>
Puesto que ya está utilizando polyfills para ver / observar, etc., permítame aprovechar la oportunidad para sugerirle Angularjs .
Ofrece exactamente esta funcionalidad en forma de sus modelos ng. Puede poner a los observadores en el valor del modelo, y cuando cambie, puede llamar a otras funciones.
Aquí está una solución muy simple, pero de trabajo para lo que quieres:
http://jsfiddle.net/RedDevil/jv8pK/
Básicamente, haz una entrada de texto y vincúlala a un modelo:
<input type="text" data-ng-model="variable">
luego coloque un observador en el modelo angularjs en esta entrada en el controlador.
$scope.$watch(function() {
return $scope.variable
}, function(newVal, oldVal) {
if(newVal !== null) {
window.alert(''programmatically changed'');
}
});
hay una forma de hacer esto. No hay un evento DOM para esto, sin embargo, hay un evento javascript que se activa en un cambio de propiedad del objeto.
document.form1.textfield.watch("value", function(object, oldval, newval){})
^ Object watched ^ ^ ^
|_ property watched | |
|________|____ old and new value
En el callback puedes hacer lo que sea.
En este ejemplo, podemos ver este efecto (verifique el jsFiddle ):
var obj = { prop: 123 };
obj.watch(''prop'', function(propertyName, oldValue, newValue){
console.log(''Old value is ''+oldValue); // 123
console.log(''New value is ''+newValue); // 456
});
obj.prop = 456;
Cuando se cambia de obj
, se activa la escucha del watch
.
Tiene más información en este enlace: http://james.padolsey.com/javascript/monitoring-dom-properties/
si el único problema con su solución es romper el evento de cambio en el valor establecido. entonces puedes disparar ese evento manualmente en el set. (Pero este monitor no se configura en caso de que un usuario realice un cambio en la entrada a través del navegador; consulte la edición a continuación)
<html>
<body>
<input type=''hidden'' id=''myInput'' />
<input type=''text'' id=''myInputVisible'' />
<input type=''button'' value=''Test'' onclick=''return testSet();''/>
<script>
//hidden input which your API will be changing
var myInput=document.getElementById("myInput");
//visible input for the users
var myInputVisible=document.getElementById("myInputVisible");
//property mutation for hidden input
Object.defineProperty(myInput,"value",{
get:function(){
return this.getAttribute("value");
},
set:function(val){
console.log("set");
//update value of myInputVisible on myInput set
myInputVisible.value = val;
// handle value change here
this.setAttribute("value",val);
//fire the event
if ("createEvent" in document) { // Modern browsers
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", true, false);
myInput.dispatchEvent(evt);
}
else { // IE 8 and below
var evt = document.createEventObject();
myInput.fireEvent("onchange", evt);
}
}
});
//listen for visible input changes and update hidden
myInputVisible.onchange = function(e){
myInput.value = myInputVisible.value;
};
//this is whatever custom event handler you wish to use
//it will catch both the programmatic changes (done on myInput directly)
//and user''s changes (done on myInputVisible)
myInput.onchange = function(e){
console.log(myInput.value);
};
//test method to demonstrate programmatic changes
function testSet(){
myInput.value=Math.floor((Math.random()*100000)+1);
}
</script>
</body>
</html>
más sobre disparar eventos manualmente
EDITAR :
El problema con la activación manual de eventos y el enfoque del mutador es que la propiedad de valor no cambiará cuando el usuario cambie el valor de campo desde el navegador. el trabajo alrededor es utilizar dos campos. Una oculta con la que podemos tener interacción programática. Otra es visible con la que el usuario puede interactuar. Después de este enfoque de consideración es bastante simple.
- mutar la propiedad de valor en el campo de entrada oculto para observar los cambios y disparar el evento de cambio manual. en el valor establecido, cambie el valor del campo visible para dar retroalimentación al usuario.
- en el cambio de valor del campo visible actualice el valor de oculto para el observador.