Атрибуты DOM теперь в цепочке прототипов

Команда Chrome недавно объявила, что переносит свойства DOM в цепочку прототипов . Это изменение, реализованное в Chrome 43 (бета-версия по состоянию на середину апреля 2015 г.), приводит Chrome в большее соответствие со спецификацией Web IDL и реализациями других браузеров, таких как IE и Firefox. Изменить: уточнено. Старые браузеры на основе WebKit в настоящее время несовместимы со спецификацией, однако Safari теперь совместим.

Новое поведение является положительным во многих отношениях. Это:

  • Улучшает совместимость через Интернет (IE и Firefox уже это делают) за счет соответствия спецификации.
  • Позволяет последовательно и эффективно создавать геттеры/сеттеры для каждого объекта DOM.
  • Повышает возможность взлома программирования DOM. Например, это позволит вам реализовать полифилы, позволяющие эффективно эмулировать функциональность, отсутствующую в некоторых браузерах и библиотеках JavaScript, которые переопределяют поведение атрибутов DOM по умолчанию.

Например, гипотетическая спецификация W3C включает в себя некоторую новую функциональность под названием isSuperContentEditable , и браузер Chrome ее не реализует, но можно «полифилить» или эмулировать эту функцию с помощью библиотеки. Как разработчик библиотеки, вы, естественно, захотите использовать prototype для создания эффективного полифилла следующим образом:

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

До этого изменения (для обеспечения согласованности с другими свойствами DOM в Chrome) вам приходилось создавать новое свойство для каждого экземпляра, что для каждого HTMLDivElement на странице было бы очень неэффективно.

Эти изменения важны для согласованности, производительности и стандартизации веб-платформы, однако они могут вызвать некоторые проблемы для разработчиков. Если вы полагались на такое поведение из-за устаревшей совместимости Chrome и WebKit, мы рекомендуем вам проверить свой сайт и просмотреть сводку изменений ниже.

Краткое описание изменений

Использование hasOwnProperty в экземпляре объекта DOM теперь будет возвращать false

Иногда разработчики используют hasOwnProperty для проверки наличия свойства у объекта. Это больше не будет работать согласно спецификации , поскольку атрибуты DOM теперь являются частью цепочки прототипов, а hasOwnProperty проверяет только текущие объекты, чтобы определить, определены ли они в них.

До версии Chrome 42 включительно следующее возвращало true .

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

true

В Chrome 43 и более поздних версиях он вернет false .

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

false

Теперь это означает, что если вы хотите проверить, доступен ли isContentEditable для элемента, вам нужно будет проверить прототип объекта HTMLElement. Например HTMLDivElement наследуется от HTMLElement , который определяет свойство isContentEditable .

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

true

Вы не привязаны к использованию hasOwnProperty . Мы рекомендуем использовать гораздо более простой in , поскольку при этом свойство будет проверяться во всей цепочке прототипов.

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

Object.getOwnPropertyDescriptor в экземпляре объекта DOM больше не будет возвращать дескриптор свойства для атрибутов.

Если вашему сайту необходимо получить дескриптор свойства для атрибута объекта DOM, теперь вам нужно будет следовать цепочке прототипов.

Если бы вы хотели получить описание свойства в Chrome 42 и более ранних версиях, вы бы сделали:

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

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

В этом сценарии Chrome 43 и более поздних версий вернет undefined .

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

undefined

Это означает, что теперь, чтобы получить дескриптор свойства isContentEditable , вам нужно будет следовать цепочке прототипов следующим образом:

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

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

JSON.stringify больше не будет сериализовать атрибуты DOM.

JSON.stringify не сериализует свойства DOM, находящиеся в прототипе. Например, это может повлиять на ваш сайт, если вы пытаетесь сериализовать такой объект, как PushSubscription из Push-уведомлений.

Chrome 42 и более ранние версии работали бы следующим образом:

> JSON.stringify(subscription);

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

Начиная с версии Chrome 43, свойства, определенные в прототипе, не будут сериализоваться, и вам будет возвращен пустой объект.

> JSON.stringify(subscription);

{}

Вам нужно будет предоставить свой собственный метод сериализации, например, вы можете сделать следующее:

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

Запись в свойства только для чтения в строгом режиме приведет к ошибке.

Запись в свойства, доступные только для чтения, должна вызывать исключение, когда вы используете строгий режим. Например, возьмем следующее:

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

В Chrome 42 и более ранних версиях функция продолжала бы выполняться автоматически, хотя isContentEditable не была бы изменена.

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

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

Теперь в Chrome 43 и более поздних версиях будет выдано исключение.

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

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

У меня проблема, что мне делать?

Следуйте инструкциям или оставьте комментарий ниже, и давайте поговорим.

Я увидел сайт с проблемой, что мне делать?

Отличный вопрос. Большинство проблем с сайтами будет связано с тем, что сайт решил выполнять обнаружение присутствия атрибута с помощью метода getOwnProperty . Обычно это происходит, когда владелец сайта ориентируется только на старые браузеры WebKit. Есть несколько вещей, которые может сделать разработчик:

  • Сообщите о проблеме, связанной с затронутым сайтом, в нашей системе отслеживания проблем (Chrome).
  • Сообщите о проблеме на радаре WebKit и укажите https://bugs.webkit.org/show_bug.cgi?id=49739.

Мне вообще интересно следить за этим изменением