HTML-атрибуты data-* для хранения параметров и получения их в js

В HTML 5 были введены такие атрибуты тегов, как data-*.
Про них вы наверняка слышали или видели в разных проектах.
Например, их используют такие модные товарищи, как Twitter Bootstrap и jQuery Mobile.

Раньше использовали классы, ради сохранения информации в HTML, с целью последующего использования в js.

Например, для сохранения уникального номера блока часто пишут так:

<div class="items">
  <div class="item1">...</div>
  <div class="item3">...</div>
  <div class="item6">...</div>
  <div class="item1">...</div>
  ...
</div>


А если нам нужно добавить еще один класс для каждого элемента? Или модификатор для отдельных? Да, конечно, можно обрезать регуляркой или другим костыликом на ваш вкус.

Как может показаться, тут можно задействовать id, но у нас могут быть блоки с одинаковым номером.

Иногда используют атрибут ‘rel’, но его можно использовать только для ссылок, хотя я видел и у других элементов. И опять же недостаток — мы можем записать в него только одно значение.

И вот нам на помощь спешат Чип и Дейл атрибуты data-*.

Плюшки


Можно присобачить к любому тегу и старые браузеры ничего не скажут против.
Можно в названии писать словосочетания: data-email-id=”190”.
Можно использовать любую строку в значении.
Можно использовать любой количество таких параметров для одного тега.

HTML тогда превратится в это:

<div class="items">
  <div class="item" data-item="1">...</div>
  <div class="item" data-item="3">...</div>
  <div class="item" data-item="6">...</div>
  <div class="item" data-item="1">...</div>
  ...
</div>


Теперь самое интересное, а именно — работа в jQuery.

Находим: $(‘[data-email-id]’) или $(‘[data-action=close]’) или даже $(‘[data-date^=2010]’)
Получаем значение: var email = $(selector).attr(‘data-email-id’)

Самое интересное — это использование функции .data().
В версии 1.4.3 data() научилось получать наши атрибуты data-* вот таким образом:

   var action = $(selector).data(‘action’); // close

Если же мы использовали словосочетание через дефис, то мы сможем получить его в camelCase:


  <div id="superid" data-foo-bar="123"></div>

   $('#superid').data('fooBar'); // вернет 123


Один минус (а может и не минус) — это то, что в data() сохранится только изначальное значение (кешируется), и если мы изменим значение атрибута (например, через .attr(‘data-foo-bar’, 456)), то получая .data('fooBar') увидим наше старое значение.
Но никто не мешает нам обновлять значение в 2х местах, если мы так захотим:

    var baz = 150;
    $(selector).data('fooBar', baz).attr('data-foo-bar', baz);


Хотя, если вам не нужно отслеживать код в, например, фаербаге, то можно и не обновлять .attr(), так как он влияет только на “визуальное” отображение.

В общем, как только вам понабиться сохранить дополнительные параметры в теге, то используйте data-атрибуты.

ЗЫ:


Интересно, кто нибудь пробовал хранить в атрибутах json? :)
Хотя это, пожалуй, в ненормальное программирование.

ЗЫЫ:


Многие говорят про функцию jQuery.data(elem, key, [value])
Кто не знает, эта функция отличается от $(selector).data(key, [value])
Она позволяет привязывать данные к DOM-элементам любым объектам, а не к jQuery объектам. Да, она работает на 60% быстрей, но вот data-* атрибуты она не получает.

ЗЫЫЫ:


trijin: Не упомянуто что $(selector).data() — вернет объект со всеми data-* свойствами элемента.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 82

    +2
    А на счет JSON это вы зря, вполне удобно, и jQuery автоматом это преобразует из строки в JSON
      0
      Попробую, как наберусь смелости)
        0
        Этот подход использует KnockoutJS. Есть свои противники среди сторонников unobstructive подхода
        +2
        Ну кто классы раньше использовал, а кто писал myattr=«N», что вообщем-то также работало.
          –5
          Если я не ошибаюсь, $.data не имеет ничего общего с тегами data-*
            +2
            Ошибаетесь. api.jquery.com/data/#data-html5
              +1
              Хм, я почему то думал что этот метод аттачит данные к дом элементу не при помощи data-* аттрибутов.
                +1
                Так и есть, но с 1.4.3 jQuery умеет их считывать из атрибутов. В общем то, я писал об этом в статье.
                  0
                  Тогда извиняюсь :) Что-то напутал
                    +3
                    Теперь пришла моя очередь извиняться. Как обычно я был невнимателен.

                    Да, действительно функция jQuery.data(obj, key, [value]) не знает об атрибутах.

                    Но вот другая функция, та, о которой я писал в статье, $(selector).data(key, [value]) — прекрасно их запоминает.
            +1
            >>и если мы изменим значение атрибута, например, через .attr(‘email-id’, 90)
            — .attr('data-email-id', 90)
              +1
              Спасибо, пофиксил.
              +1
              Забыли упомянуть один очень важный момент!
              В местах, критичных к скорости, ни в коем случае нельзя использовать $(selector).data(...);. Такая конструкция крайне тормознутая. Лучше всегда использовать data через функцию jQuery.data().

              Пример:
              jQuery.data(document.body, 'foo', 52);

              Пруф (jsperf)
                0
                Это как раз ньюансы. Если их перечислить в статье, то вся её суть утонет.
                  +1
                  Мне трудно представить задачу когда работа с data станет критичной, ну разве что сортировка таблицы, но и там использовать надо будет лишь get

                  P.S. Ссылайтесь на последнюю версию документа — jsperf.com/jquery-data-vs-jqueryselection-data/22
                  0
                  Внес в топик.
                    +1
                    Тогда уж надо добавить, что функции jQuery.data нет никакого дела до атрибутов data-*, про которые, собственно, в статье и речь. Она выдаёт значения атрибутов, только если selector.data была вызвана раньше.
                      +1
                      Можете уточнить?
                      $(selector).data('name') берет значение «явного» атрибута data-name либо присвоенного в чере $(selector).data('name','value') значения… что не так?
                        +1
                        $.data() и $(selector).data() — разные функции. Первая — низкоуровневая, прописывает свойства; вторая — высокоуровневая, к чтению-записи свойств добавляет взаимодействие с атрибутами data-* (только на чтение, кэшируются при первом обращении). Вторая, соответственно, значительно тормознее первой. Причём, что интересно, по результатам тестов — даже при повторном чтении свойств после кэширования.
                          0
                          Почему $(selector).data() тормознее, чем $.data(), если $(selector).data() вызывает ту же самую функцию jQuery.data внутри себя для нахождения данных?
                            0
                            Если только, что $(selector).data() при отсутствии кешированного атрибута парсит значение этого тега и засовывает его в кеш, а $.data() просто пошлет нафиг при отстутствии элемента в кеше.

                            Логично, что парсинг добавляет время исполнения, но это то что нам и нужно, я хочу получить значение атрибута в первый раз, чтобы положить его в кеш.

                            При повторном вызове $(selector).data() мы вызываем $.data(), тот находит нужный элемент в кеше и никакого парсинга. Откуда «значительно тормознее первой»?
                              0
                              Откуда я знаю? Я сорцы не читал, бенчмарки не запускал. Вон сверху ссылку дали, там смотрите. А я почём купил — потом продал.
                          0
                          Видимо я не очень понятно написал, хотя старался.
                          .data(name) получит только начальное значение из html атрибута.
                          Но если мы его изменим через .attr или .prop, то значение .data(name) останется старым.
                            0
                            Не совсем так.

                            <a href="" id="foo" data-foo="buzz">click</a>


                            var foo = document.getElementById('foo');
                            
                            console.log($.data(foo, 'foo'));
                            


                            > undefined
                              0
                              jQuery.data работает абсолютно с любыми объектами, а не с DOM-элементами.
                              Внутри jQuery есть некое хранилище, которое хранит сам элемент и все его ключи-значения. Оно никак не связано с data-атрибутами, $().data-функция сама проверяет элемент на data-атрибуты.
                              Вот для справки аналог:
                              var store = [];
                              function getData(obj, key){
                               for(var i = 0, l = store.length; i < l; i++){
                                if(store[i].obj === obj) return store[i].values[key];
                               }
                              }
                              function setData(obj, key, val){
                               var str = { obj:obj, values:{} }, is = 0;
                               for(var i = 0, l = store.length; i < l; i++){
                                if(store[i].obj === obj) is = str = store[i].values;
                               }
                               if(!is) store.push(str);
                               store.values[key] = val;
                              }
                                0
                                Да, я сегодня уже почитал исходники, поэтому и написал такой пример, что никакое начальное значение атрибутов data мы не получаем :) И при загрузке страницы хранилище пусто, пока мы сами туда не запишем что-то с помощью $.data или неявно скопируем атрибут через $().data при чтении.
                          0
                          Какую цель вы преследуете используя два типа записи: «data-email-id, emailId» одновременно? Не исключено (никогда не приходило в голову так делать и не тестировал соответственно) что именно из-за этого вы сталкиваетесь с проблемой: "… Один минус (а может и не минус) — это то, что в data() сохранится только изначальное значение, и если мы изменим значение атрибута (например, через .attr(‘data-email-id’, 90)), то получая .data(‘emailId’) увидим наше старое значение..."

                          Далее: одно из ключевых преимуществ data атрибутов это нативное: хранение типизированных данных(массивов, объектов и тд) и соответственно json
                            +1
                            dev.w3.org/html5/spec/global-attributes.html#embedding-custom-non-visible-data-with-the-data-attributes
                            «Hyphenated names become camel-cased. For example, data-foo-bar= „“ becomes element.dataset.fooBar.»

                            «Написанные через дефис имена превращаются в camel-cased. Например, data-foo-bar=»" станет element.dataset.fooBar."

                            Это для нативного js. Но это же правило работает и в jQuery.

                            Пример:

                            console.log($('#superid').data('fooBar')); // выведет 123. До этого разумеется мы ничего не записывали в data('fooBar')
                              +1
                              Слушалась строка:

                              <div id="superid" data-foo-bar="123"></div>
                                0
                                Тут набор тестов, как должна преобразовываться строка в lowerCamelCase
                                github.com/Raynos/DOM-shim/blob/master/test/test-suites/Element.js#L332
                                Там есть нюансы, например: как преобразовать «data--foo»? Ответ: «Foo»
                              0
                              Я храню в обычных аттрибутах.
                              Например
                              <div class=«item» item_id=«15»>..
                              Чем это чревато?
                                0
                                Вроде ничем, кроме ошибки валидации.
                                  0
                                  Которая при необходимости вроде как обходится своей схемой.
                                    0
                                    Я тоже так временами делаю, ибо офигенно удобно. Но это это чревато тем что никто не гарантирует что всегда и везде будет стабильно работать, ибо не совсем по стандарту. К примеру разработчик какого-нибудь браузера может взять и придумать свой собственный атрибут, который совпадет по имени.
                                      0
                                      Для этого существуют пространства имён.
                                        +2
                                        для этого существуют data- атрибуты
                                          +1
                                          … и пространства имён
                                          0
                                          Что вы имеете ввиду? Пространства имен которые решили взять себе разработчики браузеров? Ну так ничего не мешает кому-нибудь создать свой браузер со своим пространством имен которое совпадет с именем моих атрибутов. Никто не помешает кому-нибудь просто вырезать мои невалидные атрибуты. Я это все как гипотетический пример привел, чем может грозить несоблюдение стандартов.
                                            0
                                            Я имею ввиду пространства имён XML:
                                            <tag prefix:name="value" />

                                            Это стандарт. Они и были придуманы для того, что бы не возникало конфликтов ни с существующими тегами/атрибутами, ни с любыми другими, которые могут появится в будущем.

                                            При использовании атрибутов data-* небольшая вероятность конфликтов всё-таки есть. Когда несколько независимых модулей используют одинаковые имена — 2 одинаковых атрибута тег не может иметь.
                                            С пространством имён этого в принципе не может быть, если за каждым модулем будет закреплено своё пространство имён, которое так же может быть изменено в любом месте разметки (xmlns). И с валидацией не будет проблем, нужно только составить DTD.

                                            Собственно, ничего нового нет в атрибутах data-* нет.

                                            Что касается «новых браузеров». Ни один браузер не использует пространства имён, кроме старых версий IE. Они используют префикс xml (это зарезервированный префикс в xhtml), но такие теги вырезаются движком и в DOM их уже нет.
                                              0
                                              Оно только для xhtml или в html тоже работает?
                                                +1
                                                Работает в каком плане?
                                                Конечно, есть различия между text/html и application/xml+xhtml, но только на уровне DOM.
                                                К примеру, каждый узел тега и атрибута имеет свойства prefix и localName.
                                                В режиме text/html свойство prefix равено null, а localName равен nodeName, т.е. 'prefix:name'.
                                                В режиме application/xml+xhtml, соответственно, всё на своих местах.
                                                Так же в любом режиме доступны методы для работы с такими узлами, например getElementsByTagNameNS.

                                                Старые ишаки официально не поддерживают xhtml (включая mime-type application/xml+xhtml), но поддерживают пространства имён.

                                                Если говорить только о атрибутах, то с ними проблем нет вообще. С тегами не так всё гладко, но решаемо без костылей и ограничений для всех браузеров.
                                                  0
                                                  Я просто не разбирался так глубоко в DTD и подобном, практической пользы не видел.
                                                  Надо будет почитать, спасибо.
                                                  • UFO just landed and posted this here
                                                      0
                                                      Не совсем. В text/html работает как надо — не распознаются префиксы и всё воспринимается как одно имя тега (или локальное имя, в терминах xhtml).
                                                      Соответственно и метод, например, getElementsByTagName возвращает разный набор узлов. Всё в порядке.
                                                      В осле, конечно, всё веселее…
                                                      • UFO just landed and posted this here
                                                          0
                                                          Ослиные приколы касаются практически всего, вы ещё не привыкли?
                                                          • UFO just landed and posted this here
                                                  0
                                                  Действительно интересно. Правда ни разу в живую не видел.

                                                  Но это XML фитча, вот что думает w3.org по поводу namespaces in html5

                                                  я бы опасался этого способа с не XHTML.
                                                    0
                                                      0
                                                      Ну да, всё верно, в html не распознаётся отдельно префикс и локальное имя на уровне DOM, но нам этого и не надо.
                                            0
                                            Если используется DTD XHTML. Проблем нигде не возникает.
                                          0
                                          Чревато тем, что в дальнейшем в стандартах могут появиться такие атрибуты, которые вы уже используете для своих целей.
                                          +1
                                          Кроме Twitter Bootstrap и jQuery Mobile, уже давно используется в ASP.Net MVC3 для Unobtrusive Client Validation.
                                            0
                                            Это даже не столько MVC3 сколько jquery.validate и плагин написанный для него MS. То есть использовать можно везде, главное аттрибуты прописать
                                          • UFO just landed and posted this here
                                              0
                                              Да никто и не предлагает использовать для отображения. Почему у вас возникло такое ощущение? Где нужно перефразировать статью?
                                              • UFO just landed and posted this here
                                                  0
                                                  Необходимость именно в получении номера. Просто это самый простой вариант.
                                              0
                                              Для начала. Для блоков кода используйте конструкцию <source lang=«javascript»>… код...> (для jQuery) и с lang=«html» для html.
                                              Укажу на несколько неточностей.

                                              >> А если нам нужно добавить еще один класс для каждого элемента?
                                              Можно писать несколько классов через пробел: <div class=«first second third»>

                                              >> Иногда используют атрибут ‘rel’, но его можно использовать только для ссылок, хотя я видел и у других элементов.
                                              Атрибут rel ещё чаще используют у тегов link. А вообще браузеры поддерживают абсолютно кастомные атрибуты, включая работу с ними через getAttribute и setAttribute (именно поэтому старые браузеры поддерживают data-*), просто rel валидный.

                                              >> Она позволяет привязывать данные к DOM-элементам, а не к jQuery объектам.
                                              Функция jQuery.data работает не с DOM-элементами, а с абсолютно любыми объектами.
                                              var obj = {};
                                              $.data(obj, 'key', 'val');
                                              $.data(obj, 'key'); // 'val'

                                              И ещё. Данное замечание в корне неверно, т.к. внутри jQuery-объекта лежит как раз DOM-элемент (или несколько). Его легко можно получить так: $('#element')[0] (соответственно, получит первый элемент). И $().data работает, если не ошибаюсь, именно с $.data.
                                                0
                                                * Парсер съел начало. Для блоков кода с jQuery юзайте конструкцию:
                                                <source lang="javascript">...код...</source>

                                                Для html поменяйте здесь javascript на html соответственно.
                                                  0
                                                  Спасибо за подсказку. Поправил в статье.

                                                  1. Да, через пробел, но тогда это значение очень сложно выдрать.

                                                  2. В a и link атрибут rel разрешен. Но я видел его и у дивов.
                                                  Поддерживают, но это невалидно. И что вы будете делать если браузер вдруг добавит параметр, такой же как у вас, но со своим функционалом?

                                                  3. Поправил в статье. Но только $.data() все равно не получает data-* атрибутов.
                                                    0
                                                    Про валидность. Для этого и введены префиксы (и не только css, например в хроме у форм есть атрибут -webkit-text-speech). Просто придумываем свой префикс и делаем свой атрибут.
                                                      0
                                                      И от этого оно станет валидным? И -webkit-text-speech тоже валидный? Пруф пожалуйста.
                                                        0
                                                        Нет, но вряд ли браузеры украдут у вас префикс. Так что бояться аналогичных атрибутов в браузере уже можно не бояться.

                                                        P.S. Про классы. В jQuery для проверки существования класса есть hasClass.
                                                  0
                                                  Я раньше не парился и писал
                                                  <tagname unexistent_attrname="value"></tagname>
                                                  

                                                  и получал значение
                                                  $('tagname').attr('unexistent_attrname');
                                                  

                                                  не валидно, но
                                                  Лучший валидатор — это браузер

                                                  Самизнаетекто
                                                  :)
                                                    0
                                                    Писал выше.
                                                    Будет беда, если браузер вдруг добавит параметр, такой же как у вас, но со своим функционалом.
                                                    Отсюда и вся необходимость в валидности. И из-за этого в HTML5 внесли эти атрибуты.
                                                      0
                                                      В старых браузерах аттрибуты data-blablabla тоже невалидные, но jQuery с ними работает, браузеру пофиг, можно юзать:)
                                                        0
                                                        В старых то ничего и не добавится, а вот в новых — может.
                                                        • UFO just landed and posted this here
                                                      +2
                                                      Не понимаю. Статья про `data-*`, а про `element.dataset` ни слова.

                                                      element.dataset.foo;

                                                      Для совместимости IE6+ эмулируется достаточно тривиально
                                                      0
                                                      Не упомянуто что $(selector).data() — вернет объект со всеми data-* свойствами элемента.
                                                        0
                                                        Уже в посте.
                                                        0
                                                        Круто, но я не стал бы использовать такой подход, если понадобится массовая агрегация данных. Все же чтение/запись DOM медленнее простого объекта в памяти.
                                                          0
                                                          Чтение происходит 1 раз (через $().data), потом сбрасывается в кеш, последующее чтение только из него

                                                          Запись в DOM через data в любой вариации вообще не происходит.
                                                          0
                                                          Осторожно, не все data-методы работают в Zepto.js.
                                                            0
                                                            Вот я тормоз-то, а! Вот буквально на днях надо было передать id объекта, его порядковый номер и порядковый номер родительского элемента. И получается в результате
                                                            <div class="printGroup" printgroup="1">
                                                            <div class="printItem" printitem="1"></div>
                                                            </div>
                                                            

                                                            И ведь мелькала где-то на краю сознания мысль «все уже придумано» — не обратил внимания. Теперь переписывать буду.
                                                              0
                                                              >Интересно, кто нибудь пробовал хранить в атрибутах json? :)
                                                              В рельсах видел такое использовали.
                                                                0
                                                                data это круто, но причина использования «А если нам нужно добавить еще один класс для каждого элемента?» не вполне корректна. Классов у элемента может быть несколько через пробел и в том же jQuery есть удобные средства добавления-удаления-проверки классов.
                                                                  0
                                                                  А как вы получите значение name из классов если будет так: div class=«red big name-vasya»
                                                                  (кроме регулярки, разумеется).
                                                                  Просто, это сложнее чем из одного единственного класса вытаскивать.
                                                                    0
                                                                    А, да. Окей. Я в аналогичной ситуации ставил id с номером, но это тоже не очень-то удобно.
                                                                  0
                                                                  В FF $(selector).data() лажает как-то. Часть атрибутов остаются строками, а часть приводит насильно к number.

                                                                  Only users with full accounts can post comments. Log in, please.