Los atributos del DOM ahora están en la cadena de prototipos

Recientemente, el equipo de Chrome anunció que trasladaremos las propiedades del DOM a la cadena de prototipos. Este cambio, implementado en Chrome 43 (versión beta a partir de mediados de abril de 2015), alinea más Chrome con la especificación de IDL web y las implementaciones de otros navegadores, como IE y Firefox. Edit: Aclaración   Actualmente, los navegadores más antiguos basados en WebKit no son compatibles con la especificación, pero Safari sí lo es.

El nuevo comportamiento es positivo de muchas maneras. El reglamento se caracteriza por los siguientes aspectos:

  • Mejora la compatibilidad en la Web (IE y Firefox ya lo hacen) mediante el cumplimiento de las especificaciones.
  • Te permite crear getters y setters de forma coherente y eficiente en cada objeto DOM.
  • Aumenta la hackabilidad de la programación del DOM. Por ejemplo, te permitirá implementar polyfills que te permitan emular de manera eficiente la funcionalidad que falta en algunos navegadores y bibliotecas de JavaScript que anulan los comportamientos predeterminados de los atributos DOM.

Por ejemplo, una especificación hipotética del W3C incluye una funcionalidad nueva llamada isSuperContentEditable y el navegador Chrome no la implementa, pero es posible "polyfill" o emular la función con una biblioteca. Como desarrollador de la biblioteca, te recomendamos que uses prototype de la siguiente manera para crear un polyfill eficiente:

Object.defineProperty(HTMLDivElement.prototype, "isSuperContentEditable", {
    get: function() { return true; },
    set: function() { /* some logic to set it up */ },
});

Antes de este cambio, para mantener la coherencia con otras propiedades del DOM en Chrome, habrías tenido que crear la propiedad nueva en cada instancia, lo que sería muy ineficiente para cada HTMLDivElement en la página.

Estos cambios son importantes para la coherencia, el rendimiento y la estandarización de la plataforma web, pero pueden causar algunos problemas para los desarrolladores. Si dependías de este comportamiento debido a la compatibilidad heredada entre Chrome y WebKit, te recomendamos que revises tu sitio y veas el resumen de los cambios a continuación.

Resumen de cambios

Usar hasOwnProperty en una instancia de objeto DOM ahora mostrará false.

A veces, los desarrolladores usan hasOwnProperty para verificar la presencia de una propiedad en un objeto. Esto ya no funcionará según las especificaciones porque los atributos DOM ahora forman parte de la cadena de prototipos y hasOwnProperty solo inspecciona los objetos actuales para ver si se definen en ellos.

Antes de Chrome 42, inclusive, se mostraba true.

> div = document.createElement("div");
> div.hasOwnProperty("isContentEditable");

true

A partir de Chrome 43, mostrará false.

> div = document.createElement("div");
> div.hasOwnProperty("isContentEditable");

false

Esto significa que, si quieres comprobar que isContentEditable está disponible en el elemento, deberás verificar el prototipo en el objeto HTMLElement. Por ejemplo, HTMLDivElement hereda de HTMLElement, que define la propiedad isContentEditable.

> HTMLElement.prototype.hasOwnProperty("isContentEditable");

true

No estás obligado a usar hasOwnProperty. Te recomendamos que uses el operando in mucho más simple, ya que este verificará la propiedad en toda la cadena de prototipos.

if("isContentEditable" in div) {
    // We have support!!
}

Object.getOwnPropertyDescriptor en la instancia de objeto DOM ya no mostrará un descriptor de propiedad para los atributos.

Si tu sitio necesita obtener el descriptor de propiedad de un atributo en un objeto DOM, ahora deberás seguir la cadena de prototipos.

Si quisieras obtener la descripción de la propiedad en Chrome 42 y versiones anteriores, deberías hacer lo siguiente:

> Object.getOwnPropertyDescriptor(div, "isContentEditable");

Object {value: "", writable: true, enumerable: true, configurable: true}

A partir de Chrome 43, se mostrará undefined en esta situación.

> Object.getOwnPropertyDescriptor(div, "isContentEditable");

undefined

Esto significa que, para obtener el descriptor de la propiedad isContentEditable, deberás seguir la cadena de prototipos de la siguiente manera:

> Object.getOwnPropertyDescriptor(HTMLElement.prototype, "isContentEditable");

Object {get: function, set: function, enumerable: false, configurable: false}

JSON.stringify ya no serializará los atributos DOM.

JSON.stringify no serializa las propiedades del DOM que están en el prototipo. Por ejemplo, esto puede afectar tu sitio si intentas serializar un objeto, como PushSubscription de notificaciones push.

En Chrome 42 y versiones anteriores, habría funcionado lo siguiente:

> JSON.stringify(subscription);

{
    "endpoint": "https://something",
    "subscriptionId": "SomeID"
}

A partir de Chrome 43, no se serializarán las propiedades definidas en el prototipo y se te mostrará un objeto vacío.

> JSON.stringify(subscription);

{}

Deberás proporcionar tu propio método de serialización. Por ejemplo, puedes hacer lo siguiente:

function stringifyDOMObject(object)
{
    function deepCopy(src) {
        if (typeof src != "object")
            return src;
        var dst = Array.isArray(src) ? [] : {};
        for (var property in src) {
            dst[property] = deepCopy(src[property]);
        }
        return dst;
    }
    return JSON.stringify(deepCopy(object));
}
var s = stringifyDOMObject(domObject);

Si escribes en propiedades de solo lectura en modo estricto, se mostrará un error.

Se supone que escribir en propiedades de solo lectura arroja una excepción cuando usas el modo estricto. Por ejemplo, considera lo siguiente:

function foo() {
    "use strict";
    var d = document.createElement("div");
    console.log(d.isContentEditable);
    d.isContentEditable = 1;
    console.log(d.isContentEditable);
}

En Chrome 42 y versiones anteriores, la función habría continuado y ejecutado la función en silencio, aunque no se habría cambiado isContentEditable.

// Chrome 42 and earlier behavior
> foo();

false // isContentEditable
false // isContentEditable (after writing to read-only property)

Ahora, en Chrome 43 y versiones posteriores, se arrojará una excepción.

// Chrome 43 and onwards behavior
> foo();

false
Uncaught TypeError: Cannot set property isContentEditable of #<HTMLElement> which has only a getter

Tengo un problema, ¿qué debo hacer?

Sigue las instrucciones o deja un comentario a continuación para que hablemos.

Vi un sitio con un problema. ¿Qué debo hacer?

Muy buena pregunta. La mayoría de los problemas con los sitios se deben a que un sitio eligió realizar la detección de presencia de atributos con el método getOwnProperty. Esto se hace principalmente cuando el propietario de un sitio solo segmenta anuncios para navegadores WebKit más antiguos. Un desarrollador puede hacer lo siguiente:

  • Informa un problema sobre el sitio afectado en nuestro servicio de seguimiento de problemas (de Chrome).
  • Informa un problema en el radar de WebKit y consulta https://bugs.webkit.org/show_bug.cgi?id=49739.

En general, me interesa seguir este cambio