Atributos do DOM agora na cadeia do protótipo

A equipe do Chrome anunciou recentemente que estamos movendo propriedades DOM para a cadeia de protótipos. Com essa mudança, implementada no Chrome 43 (versão Beta a partir de meados de abril de 2015), o Chrome agora está mais de acordo com as especificações da Web IDL e as implementações de outros navegadores, como o IE e o Firefox. Edição: esclarecido que navegadores mais antigos baseados no WebKit não são compatíveis com a especificação, mas o Safari é agora.

O novo comportamento é positivo de várias maneiras. Ele:

  • Melhora a compatibilidade na Web (o IE e o Firefox já fazem isso) por meio da conformidade com a especificação.
  • Permite criar getters/setters de maneira consistente e eficiente em cada objeto DOM.
  • Aumenta a invasão da programação DOM. Por exemplo, ela permitirá que você implemente polyfills para emular de maneira eficiente a funcionalidade ausente em alguns navegadores e bibliotecas JavaScript que substituem os comportamentos padrão do atributo DOM.

Por exemplo, uma especificação hipotética do W3C inclui uma nova funcionalidade chamada isSuperContentEditable. Ela não é implementada pelo navegador Chrome, mas é possível "polyfill" ou emular o recurso com uma biblioteca. Como desenvolvedor da biblioteca, use prototype da seguinte maneira para criar um polyfill eficiente:

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

Antes dessa mudança, para fins de consistência com outras propriedades DOM no Chrome, era necessário criar a nova propriedade em todas as instâncias, o que seria muito ineficiente para cada HTMLDivElement na página.

Essas alterações são importantes para a consistência, o desempenho e a padronização da plataforma Web, mas podem causar alguns problemas para os desenvolvedores. Se você estava confiando nesse comportamento por causa da compatibilidade legada entre o Chrome e o WebKit, recomendamos verificar seu site e ver o resumo das mudanças abaixo.

Resumo das mudanças

O uso de hasOwnProperty em uma instância de objeto DOM agora retorna false.

Às vezes, os desenvolvedores usam hasOwnProperty para verificar a presença de uma propriedade em um objeto. Isso não vai mais funcionar de acordo com a especificação, porque os atributos DOM agora fazem parte da cadeia de protótipo, e o hasOwnProperty inspeciona apenas os objetos atuais para ver se estão definidos nele

Antes do Chrome 42, e incluindo, o seguinte retornava true.

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

true

No Chrome 43 em diante, ela retornará false.

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

false

Isso significa que, se você quiser conferir se o isContentEditable está disponível no elemento, será necessário verificar o protótipo no objeto HTMLElement. Por exemplo, HTMLDivElement herda de HTMLElement, que define a propriedade isContentEditable.

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

true

Você não é obrigado a usar o hasOwnProperty. Recomendamos usar o operando in, que é muito mais simples, porque vai verificar a propriedade em toda a cadeia de protótipos.

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

Object.getOwnPropertyDescriptor na instância de objeto DOM não retornará mais um descritor de propriedade para atributos.

Caso seu site precise do descritor de propriedade de um atributo em um objeto DOM, você precisa seguir a cadeia de protótipos.

Para conseguir a descrição da propriedade no Chrome 42 e versões anteriores, faça o seguinte:

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

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

O Chrome 43 em diante vai retornar undefined nessa situação.

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

undefined

Para receber o descritor de propriedade da propriedade isContentEditable, siga a cadeia de protótipos da seguinte maneira:

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

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

O JSON.stringify não vai mais serializar atributos DOM

O JSON.stringify não serializa propriedades do DOM que estão no protótipo. Por exemplo, isso poderá afetar seu site se você estiver tentando serializar um objeto como PushSubscription de notificações push.

No Chrome 42 e anteriores, o seguinte teria funcionado:

> JSON.stringify(subscription);

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

A partir do Chrome 43, as propriedades definidas no protótipo não serão serializadas, e você receberá um objeto vazio.

> JSON.stringify(subscription);

{}

Será necessário fornecer seu próprio método de serialização. Por exemplo, você pode fazer o seguinte:

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

Gravar em propriedades somente leitura no modo restrito vai gerar um erro

A gravação em propriedades somente leitura precisa gerar uma exceção quando você está usando o modo restrito. Por exemplo, veja o seguinte:

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

No Chrome 42 e nas versões anteriores, a função continuaria e continuaria silenciosamente executando a função, embora isContentEditable não tivesse sido modificado.

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

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

Agora, no Chrome 43 em diante, haverá uma exceção.

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

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

Estou com um problema. O que devo fazer?

Siga as orientações ou deixe um comentário abaixo e vamos conversar.

Encontrei um site com um problema. O que devo fazer?

Ótima pergunta. A maioria dos problemas com sites é baseada no fato de um site ter escolhido fazer a detecção de presença de atributo com o método getOwnProperty. Isso é feito principalmente quando o proprietário de um site segmenta apenas navegadores WebKit mais antigos. Existem algumas coisas que um desenvolvedor pode fazer:

  • Registre um problema sobre o site afetado no nosso Issue Tracker (Chrome)
  • Registre um problema no radar do WebKit e mencione https://bugs.webkit.org/show_bug.cgi?id=49739

Tenho interesse geral em acompanhar essa mudança