Event handler when changing the attribute of a label

4

Is there an event handler in JavaScript for when the value of an attribute is updated? And more concrete for the case of src of a img .

I do not mean load (which is thrown when the image has been loaded), but before, when the value of src is changed (it may take a while while the image is downloaded before it is release load ).

For example, if I have an image and through the code the src is changed, from that moment until the image is downloaded and displayed, I would like to show a message of "Loading".

Is it something that exists in JavaScript directly (and how)?

    
asked by Alvaro Montoro 10.01.2017 в 18:23
source

3 answers

4

You can use a MutationObserver (check the compatibility ).

Consists of creating an object:

var miMO = new MutationObserver(callback);

And call observe(nodo, opciones) with the node to be observed and options to determine when the callback is triggered. The options are a MutationObserverInit object that accepts:

  • childList - If true, note additions and deletions of child nodes (including textNodes).
  • attributes - If true, note the mutations in the attributes of the node.
  • characterData - If true, note the mutations in the data of the node.
  • subtree - If true, also observe the mutations in the descendants of the node.
  • attributeOldValue - If true (with attributes set to true), it triggers before the change in an attribute.
  • characterDataOldValue - If true (with characterData true), it triggers before the change in data.
  • attributeFilter - An array of attributes if you just want to see a specific list.


The benefit of using a MutationObserver is that it fires with any modification to the DOM. It can be triggered when modifying from code, with a user's script, or even when the same user edits the DOM manually. For this reason, it is important that the filters passed as options are as specific as possible, so that the use of resources is not as intensive.


For example, to observe mutations in the src of an image:

var miImg = document.getElementById('idDeImg'),
    miMO  = new MutationObserver(callback);

miMO.observe(miImg, {
    attributes: true,
    attributeFilter: [
        'src'
    ]
});


And then the callback will be called with a collection MutationRecord . In particular, for each element of the collection, we will be interested in verifying that .attributeName == "src" . That is, that specific property has been changed. This check is redundant with the filters that we just passed, but I'll leave it in case you want to add another mutation that triggers the callback.


Example :

var miImg = document.getElementById('idDeImg'),
    miMO  = new MutationObserver(callbackMO);

miMO.observe(miImg, {
    attributes: true,
    attributeOldValue: true,
    attributeFilter: ['src']
});

function callbackMO(mutations) {
    mutations.forEach((mutation) => {
        if (mutation.attributeName == "src") { // <- redundante
            console.log('Se cambió la imagen de', mutation.oldValue, 'a', mutation.target.src);
        }
    });
}


//Prueba cambiando el src
function cambiarSrc() {
    miImg.src = "https://i.stack.imgur.com/f4AWis.jpg";
}
<img id="idDeImg" src="https://i.stack.imgur.com/knEtbs.jpg">
<br>
<input type="button" onclick="cambiarSrc()" value="Probar cambiar el src">
    
answered by 10.01.2017 / 19:10
source
4

As far as there is no native event, but you can use a proxy ES6 (or use a polyfill if you need ES5) and launch a custom event when the property is modified.

The following example creates a proxy for set and get, and throws a custom event if the property src is modified.

// preparacion del proxy
function ImageProxy(element) {
  return new Proxy(element, {
    get(target, propKey, receiver) {
      return element[propKey];
    },
    set(target, propKey, value, receiver) {
      // aqui disparas el evento.
      if (propKey === 'src' && target.onsrcchanged) {
        target.onsrcchanged(target[propKey], value)
      }
      target[propKey] = value;
    }
  });
}

let el = document.createElement('img');
document.body.append(el);

// creas el proxy
let elProxy = ImageProxy(el);

// este es el manejador del evento
elProxy.onsrcchanged = function(oldVal, newVal) {
  console.log('se ha modificadoimg.src =', newVal);
}

// aqui cambias 
elProxy.src = 'http://lorempixel.com/400/200/sports';
console.log(elProxy.src);

setTimeout(function() {
  // vuelves a cambiar
  elProxy.src = 'http://lorempixel.com/400/200/nature';
  console.log(elProxy.src);
}, 5000);

It is also possible to do it with Object.definePropery , it is really quite similar and is compatible with ES5 without needing polyfils.

function ImageNonProxy(image) {
  Object.defineProperty(image, 'src', {
    set: function(srcAttr) {
      if (image.onsrcchanged) {
        image.onsrcchanged(image.getAttribute('src'), srcAttr)
      }
      image.setAttribute('src', srcAttr);
    },
    get: function() {
      return image.getAttribute('src');
    }
  });
  return image
}

var el = document.createElement('img');
document.body.append(el);

// creas el proxy
let elProxy = ImageNonProxy(el);

// este es el manejador del evento
elProxy.onsrcchanged = function(oldVal, newVal) {
  console.log('se ha modificadoimg.src =', newVal);
}

// aqui cambias 
elProxy.src = 'http://lorempixel.com/400/200/sports';
console.log(elProxy.src);

setTimeout(function() {
  // vuelves a cambiar
  elProxy.src = 'http://lorempixel.com/400/200/nature';
  console.log(elProxy.src);
}, 5000);
    
answered by 10.01.2017 в 18:58
3

There is the API MutationObserver ( IE11+ )

Using this API in combination with the event onload you can get to know exactly when the load started and when it ended (successfully)

var img = document.getElementById('image');
var loader = document.getElementById('loader');
var btn = document.getElementById('button');

// Creamos un observador y pasamos la función que sera invocada 
// cada vez que se realice alguna mutación
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    // Si la mutacion fue sobre un atributo y especificamente sobre el "src"
    if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
      console.log('Load Started!');
      loader.style.display = 'block';
    }
  });    
});
var config = {attributes: true};

// Suscribimos el observer a la img con la config deseada
observer.observe(img, config);

btn.addEventListener('click', function() {
  img.src = 'https://i.imgur.com/xkKw5r8.jpg';
});
img.addEventListener('load', function() {
  console.log('Load Finished!');
  loader.style.display = 'none';
});
#image {
  width: 300px;
}

#loader {
  display: none;
}
<button id="button">Cargar imagen</button>
<br/><br/>
<img id="image" />
<div id="loader">Cargando...</div>
    
answered by 10.01.2017 в 19:01