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ść.

Moje inne próby:
  • pętla setTimeout/setInterval, ale to również nie jest czyste
  • różne watch i observe 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.

Author: Christophe, 2013-10-11

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.

  1. 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.
  2. przy zmianie wartości pola widzialnego zaktualizuj wartość hidden for observer.
 16
Author: Mohit,
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>
 2
Author: balpha,
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.

 1
Author: MarcoK,
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');
  }
});
 0
Author: kumar_harsh,
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/

 0
Author: Donovan Charpin,
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.

 0
Author: frequent,
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