Wyzwalanie działania przy programowej zmianie na wartość wejściową
Moim celem jest obserwacja wartości wejściowej i uruchomienie obsługi, gdy jej wartość zostanie zmieniona programowo . Potrzebuję go tylko dla nowoczesnych przeglądarek.
Próbowałem wielu kombinacji używając defineProperty
i to jest moja najnowsza iteracja:
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
Wygląda na to, że robi to, czego oczekuję, ale czuję się jak włamanie, ponieważ nadpisuję istniejącą właściwość value i bawię się podwójnym statusem value
(atrybut i właściwość). Łamie również zdarzenie change
, które nie wydaje się lubić zmodyfikowana własność.
- pętla setTimeout/setInterval, ale to również nie jest czyste
- różne
watch
iobserve
polifiltry, ale pękają dla właściwości wartości wejściowej
Jaki byłby właściwy sposób na osiągnięcie tego samego wyniku?
Live demo: http://jsfiddle.net/L7Emx/4/
[Edytuj] dla wyjaśnienia: mój kod obserwuje element wejściowy, w którym inne aplikacje mogą wysyłać aktualizacje (w wyniku wywołań ajax dla przykład, lub w wyniku zmian w innych polach). Nie mam kontroli nad tym, jak inne aplikacje wypychają aktualizacje, jestem tylko obserwatorem.
[Edit 2] aby wyjaśnić, co mam na myśli przez "nowoczesną przeglądarkę", byłbym bardzo zadowolony z rozwiązania, które działa na IE 11 i Chrome 30.
[Update] Zaktualizowano demo na podstawie zaakceptowanej odpowiedzi: http://jsfiddle.net/L7Emx/10/
Sztuczka sugerowana przez @ mohit-jain polega na dodaniu drugiego wejścia do interakcji użytkownika.
6 answers
Jeśli jedynym problemem z Twoim rozwiązaniem jest przerwanie zdarzenia change na ustawionej wartości. thn możesz odpalić to zdarzenie ręcznie na ustawieniu. (Ale ten monitor nie będzie ustawiony w przypadku, gdy użytkownik zmieni wejście za pomocą przeglądarki -- zobacz edit poniżej)
<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>
Więcej o odpalaniu zdarzeń ręcznie
EDIT :
Problem z ręcznym uruchamianiem zdarzeń i podejściem mutatora polega na tym, że właściwość value nie zmieni się, gdy użytkownik zmieni wartość pola z przeglądarki. praca polega na wykorzystaniu dwóch pól. taki Ukryty, z którym możemy mieć programową interakcję. Inny jest widoczny, z którym użytkownik może wchodzić w interakcje. Po tym rozważeniu podejście jest wystarczająco proste.
- mutuje właściwość value na ukrytym polu wejściowym, aby obserwować Zdarzenie changes I fire manual onchange. po ustawieniu wartości zmień wartość widocznego pola, aby dać opinię użytkownika.
- przy zmianie wartości pola widzialnego zaktualizuj wartość hidden for observer.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-08 03:54:23
Poniższe działania działają wszędzie, gdzie próbowałem, w tym IE11 (nawet w trybie emulacji IE9).
Zajmuje to twój pomysł defineProperty
nieco dalej, znajdując obiekt w łańcuchu prototypów elementu wejściowego, który definiuje seter .value
i modyfikując go w celu wywołania zdarzenia (nazwałem go modified
w przykładzie), zachowując stare zachowanie.
Po uruchomieniu poniższego fragmentu możesz wpisać / wkleić / whatnot w polu wprowadzania tekstu lub kliknąć przycisk dołącza {[5] } do elementu wejściowego .value
. W obu przypadkach zawartość <span>
jest synchronicznie aktualizowana.
Jedyną rzeczą, która nie jest tutaj obsługiwana, jest aktualizacja spowodowana ustawieniem atrybutu. Możesz sobie z tym poradzić za pomocą MutationObserver
, jeśli chcesz, ale zauważ, że nie ma relacji jeden do jednego między .value
a value
atrybutem (ten ostatni jest tylko domyślną wartością dla pierwszego).
// 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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-05 10:42:36
potrzebuję go tylko dla nowoczesnych przeglądarek.
Jak nowoczesny chciałbyś iść? Skrypt Ecma 7 (6 zostanie ukończony w grudniu) Może zawierać obiekt.obserwuj. Pozwoli to na tworzenie natywnych obserwabli. I tak, możesz go uruchomić! Jak?
Aby eksperymentować z tą funkcją, musisz włączyć Enable Eksperymentalna flaga JavaScript w Chrome i ponowne uruchomienie przeglądarki. Flaga znajduje się pod
'about:flags’
Więcej informacji: przeczytaj to .
Więc tak, jest to wysoce eksperymentalne i nie jest gotowe w obecnym zestawie przeglądarek. Ponadto, nadal nie jest w pełni gotowy i nie w 100%, jeśli zbliża się do ES7, a ostateczna data dla ES7 nie jest jeszcze ustalona. Mimo to, chciałem dać Ci znać do wykorzystania w przyszłości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-14 17:04:46
Ponieważ używasz już polyfills do oglądania / obserwowania itp., pozwól, że skorzystam z okazji, aby zasugerować Ci Angularjs .
Oferuje dokładnie tę funkcjonalność w postaci swoich Modeli ng. Możesz umieścić obserwatorów na wartości modelu, a gdy się zmieni, możesz wywołać inne funkcje.
Oto bardzo proste, ale działające rozwiązanie tego, co chcesz:
Http://jsfiddle.net/RedDevil/jv8pK/
Zasadniczo należy wprowadzić tekst i powiązać go do modelu:
<input type="text" data-ng-model="variable">
Następnie umieść kontroler na modelu angularjs na tym wejściu w kontrolerze.
$scope.$watch(function() {
return $scope.variable
}, function(newVal, oldVal) {
if(newVal !== null) {
window.alert('programmatically changed');
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-14 19:48:36
Jest na to sposób. Nie ma dla tego zdarzenia DOM, jednak istnieje Zdarzenie javascript, które uruchamia się przy zmianie Właściwości obiektu.
document.form1.textfield.watch("value", function(object, oldval, newval){})
^ Object watched ^ ^ ^
|_ property watched | |
|________|____ old and new value
W oddzwanianiu możesz zrobić wszystko.
W tym przykładzie możemy zobaczyć ten efekt (Sprawdź 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;
Kiedy obj
zmieni się, aktywuje watch
słuchacza.
Więcej informacji znajdziesz pod tym linkiem : http://james.padolsey.com/javascript/monitoring-dom-properties/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-15 10:26:37
Jakiś czas temu napisałem następujący Gist, który pozwala słuchać niestandardowych zdarzeń w przeglądarce (w tym IE8+).
Zobacz, jak słucham onpropertychange
na 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();
}
}
};
Nie jestem pewien, czy rozwiązanie IE8 działa między przeglądarkami, ale możesz ustawić fałszywą eventlistener
na właściwości value
twojego wejścia i uruchomić callback po zmianie wartości value
wywołanej przez onpropertychange
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-20 19:51:31