Mise à jour des attributs DOM dans la chaîne de prototypes

L'équipe Chrome a récemment annoncé que nous allions transférer les propriétés DOM vers la chaîne de prototypes. Ce changement, mis en œuvre dans Chrome 43 (bêta à la mi-avril 2015), permet à Chrome d'être davantage conforme aux spécifications Web IDL et aux implémentations d'autres navigateurs, tels qu'IE et Firefox. Modification: clarification Les anciens navigateurs basés sur WebKit ne sont actuellement pas compatibles avec ces spécifications, mais Safari l'est désormais.

Le nouveau comportement est positif à bien des égards.

  • Améliore la compatibilité sur le Web (IE et Firefox le font déjà) par le biais de la conformité aux spécifications.
  • Permet de créer de manière cohérente et efficace des getters/setters sur chaque objet DOM.
  • Il favorise le piratage lors de la programmation DOM. Par exemple, vous pourrez implémenter des polyfills qui vous permettront d'émuler efficacement les fonctionnalités manquantes dans certains navigateurs et dans certaines bibliothèques JavaScript qui ignorent les comportements par défaut des attributs DOM.

Par exemple, une spécification W3C fictive inclut une nouvelle fonctionnalité appelée isSuperContentEditable, qui n'est pas implémentée par le navigateur Chrome, mais il est possible de "polyfill" ou d'émulation avec une bibliothèque. En tant que développeur de la bibliothèque, vous pouvez naturellement utiliser prototype comme suit pour créer un polyfill efficace:

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

Avant cette modification, pour plus de cohérence avec les autres propriétés DOM dans Chrome, vous deviez créer la propriété sur chaque instance, ce qui s'avère très inefficace pour chaque HTMLDivElement de la page.

Ces modifications sont importantes pour la cohérence, les performances et la normalisation de la plate-forme Web, mais elles peuvent causer des problèmes pour les développeurs. Si vous vous appuyiez sur ce comportement en raison de la compatibilité ancienne entre Chrome et WebKit, nous vous encourageons à vérifier votre site et à consulter le résumé des modifications ci-dessous.

Résumé des modifications

L'utilisation de hasOwnProperty sur une instance d'objet DOM renvoie désormais false.

Parfois, les développeurs utilisent hasOwnProperty pour vérifier la présence d'une propriété sur un objet. Cela ne fonctionnera plus conformément aux spécifications, car les attributs DOM font désormais partie de la chaîne du prototype et hasOwnProperty inspecte uniquement les objets actuels pour voir s'ils y sont définis.

Avant cette version, les éléments suivants renvoyaient true.

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

true

À partir de la version Chrome 43, elle renvoie false.

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

false

Cela signifie maintenant que si vous souhaitez vérifier que isContentEditable est disponible sur l'élément, vous devez vérifier le prototype sur l'objet HTMLElement. Par exemple, HTMLDivElement hérite de HTMLElement, qui définit la propriété isContentEditable.

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

true

Vous n'êtes pas obligé d'utiliser hasOwnProperty. Nous vous recommandons d'utiliser l'opérande in, qui est beaucoup plus simple, car il vérifiera la propriété sur l'ensemble de la chaîne de prototype.

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

Object.getOwnPropertyDescriptor sur une instance d'objet DOM ne renvoie plus de descripteur de propriété pour les attributs.

Si votre site doit obtenir le descripteur de propriété d'un attribut d'un objet DOM, vous devez maintenant suivre la chaîne de prototypes.

Si vous souhaitiez obtenir la description des propriétés dans Chrome 42 et versions antérieures, vous auriez dû procéder comme suit:

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

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

Dans ce scénario, Chrome 43 et les versions ultérieures renverront undefined.

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

undefined

Pour obtenir le descripteur de la propriété isContentEditable, vous devez donc suivre la chaîne de prototype comme suit:

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

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

JSON.stringify ne sérialise plus les attributs DOM

JSON.stringify ne sérialise pas les propriétés DOM qui se trouvent sur le prototype. Cela peut, par exemple, affecter votre site si vous essayez de sérialiser un objet tel que PushSubscription de la notification push.

À partir de la version 42 de Chrome, les opérations suivantes auraient fonctionné:

> JSON.stringify(subscription);

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

À partir de la version 43 de Chrome, les propriétés définies sur le prototype ne seront pas sérialisées, et vous recevrez un objet vide.

> JSON.stringify(subscription);

{}

Vous devrez fournir votre propre méthode de sérialisation, par exemple:

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);

L'écriture dans des propriétés en lecture seule en mode strict génère une erreur

L'écriture dans des propriétés en lecture seule est censée générer une exception lorsque vous utilisez le mode strict. Prenons l'exemple suivant:

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

Dans Chrome 42 et versions antérieures, la fonction aurait continué à l'exécuter en mode silencieux, bien que isContentEditable n'ait pas été modifié.

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

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

Une exception est désormais générée à partir de la version 43 de Chrome.

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

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

Je rencontre un problème. Que dois-je faire ?

Suivez les conseils ou laissez un commentaire ci-dessous pour nous en parler.

J'ai vu un site présentant un problème. Que dois-je faire ?

Excellente question. La plupart des problèmes liés aux sites découlent du fait qu'un site a choisi d'effectuer une détection de présence d'attribut avec la méthode getOwnProperty. Cela se produit généralement lorsque le propriétaire d'un site n'a ciblé que des navigateurs WebKit plus anciens. Un développeur peut effectuer plusieurs actions:

  • Signaler un problème concernant le site concerné dans notre outil de suivi des problèmes (Chrome)
  • Signaler un problème sur le radar WebKit et mentionner https://bugs.webkit.org/show_bug.cgi?id=49739

Ce changement m'intéresse d'une manière générale