Чуть более полутыщи человек добавили первую часть в избранное и совсем не все так плохо, как казалось мне и некоторым комментаторам. За внимание к теме всем спасибо!
В этом выпуске мы продолжаем возиться с кодом последней на данный момент версии — 1.8.3.
В прошлом выпуске мы упомянули, что при передаче в jQuery вместо селектора html-строки, на основе нее функция parseHTML создаст соответствующие элементы и вернет их в привычном jQuery-объекте. Сейчас мы рассмотрим все это более тщательно и затронем кроме core.js еще manipulation.js и attributes.js.
Начнем с простого
jQuery определяет, что вместо селектора передана html-строка по первому и последнему символу (знак «меньше» и «больше», открывающие и закрывающие тег) или, если первая проверка не удалась, по специальной регулярке /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/
.
ВНИМАНИЕ: начиная с версии 1.9 проверка будет состоять только из в подсматривании первого символа строки, он должен быть знаком «меньше». По крайней мере так писали в блоге. В этом случае строка "test<div>лалала
" уже не будет воспринята как html, который jQuery потом будет пытаться распарсить, будьте внимательны.
Перво-наперво будет проверено, передан ли в качестве строки простой одиночный тег без какого-нибудь содержимого, в этом случае он будет создан просто через context.createElement( [tagName] )
, выполнение каждой из этой строки приведет довольно быстро к одному и тому же результату:
$('<div>') $('<div />') $('<div></div>') $(document.createElement('div'))
В этом же случае с одиночным тегом, если вместо контекста мы передали объект, к полученному результату будет применен jQuery.attr, который постарается сооруженному тегу добавить атрибуты, указанные в этом объекте. Об этом, надеюсь, мы поговорим в какой-нибудь из следующих частей.
Перейдем к более сложному
В остальных случаях jQuery произведет настолько больше всякой работы, что мы заранее посочувствуем старым медленным браузерам, скажем core.js «давай, до свидания!» и посмотрим на buildFragment из manipulation.js, именно там начнет вершиться магия. Вкратце — будет создаваться фрагмент, в него по-тихоньку помещаться полученные DOM-элементы, которые из него потом и придут в результат.
Кеширование
Первым делом для указанного html-кода будет определено, можно и нужно ли его кешировать. Кешировать результат построения можно, если он строится в контексте document, длина исходного html не превышает 512 символов, не содержит тегов script, object, embed, option или style и проходит несколько браузеро-специфических тестов (к примеру, в относительно несвежем Webkit’е клонировать фрагмент с нодой, у которой задан атрибут checked, не получится с сохранением его значения). Результаты, которые можно кешировать, сначала отмечаются в объект fragments, а на вторую попытку создать что-то по тому же самому html, туда уйдет и сам результат.
Тут мы встречаемся с проблемой — объект jQuery.fragments
никогда не чистится! Для динамических приложений, в которых на одной странице приходится создавать много элементов по каким-то пришедшим данным, это важно. Подумайте несколько раз, прежде чем создавать тоннами какие-то простые элементы именно таким способом.
Реальный пример с созданием каких-то воображаемых плашек для хеша с тремя воображаемыми пользователями:
var users = { 5: 'Ольга', 6: 'Вася', 10: 'Юля' }; $.each(users, function(id, name) { $('<span id="user' + id + '" title="Пользователь ' + name + '">' + name + '</span>') .appendTo(document.body); } );
Код некрасив и так писать не стоит в любом случае. Тем не менее я этот код у разработчиков периодически вижу, а на волне популярности javascript-шаблонизаторов его становится все больше и больше. Что в итоге получится в jQuery.fragments:
> jQuery.fragments Object { <span id="5" title="Пользователь Петя">Петя</span>: false <span id="6" title="Пользователь Вася">Вася</span>: false <span id="10" title="Пользователь Юля">Юля</span>: false }
Три span’а, три элемента в объекте jQuery.fragments
. false
в значении — как раз то, о чем я говорил, в первый раз они только попадают в объект, на второй раз — вместо false
там будет сам результат. Несколько записей — фигня вопрос, конечно. А вот тонны записей — пустой расход памяти и никуда не годится. Можно попробовать понабирать что-нибудь в саджесте поиска на Хабре и потом в консоли глянуть на мусор в jQuery.fragments
.
А вот немного другой код, но более красивый и с тем же результатом:
$.each(users, function(id, name) { $('<span>', { 'id': 'user' + id, 'title': 'Пользователь ' + name, 'text': name } ).appendTo(document.body); } );
jQuery.fragments
в этом случае будет просто пустой, потому что для простого тега будет вызван document.createElement('span')
, на него будет повешен идентификатор, а внутрь — заброшен текст.
И так, кешированный фрагмент вернется сразу же. Если же в кеше не нашлось результата, будет создан легковесный DocumentFragment и наше внимание будет переключено на функцию clean, в которую будут прокинуты свежесозданный фрагмент и наш html-код.
safeFragment
Все временные действия с созданием элементов производятся в специальном фрагменте-отстойнике, safeFragment
, который создается при инициализации. Причем в IE он еще и дополнительно обрабатывается для поддержки html5-тегов (см. баг, очень интересный).
Создание элементов
В safeFragment
создается пустой <div></div>
, в который jQuery с помощью стандартного метода innerHTML
записывает наш html-код
Но предварительно jQuery пытается найти, не нужно ли обрамить наш код как-то дополнительно. Берется самый первый найденный в коде тег и ищется в служебном объекте wrapMap. Зачем вообще что-то обрамлять? Затем, что нельзя просто взять и вставить в innerHTML у <div>
, к примеру, <td>Привет!</td>
:
var k = document.createElement('div'); k.innerHTML = '<td>Привет!</td>' > k <div>Привет!</div>
Для случая с <td>Привет!</td>
, код превратится в <table><tbody><tr><td>Привет!</td></tr></tbory></table>
, а указатель на контейнер с нужным нам результатом будет смещен на глубину в 3 тега, тоесть в <tr>
вместо <div>
, который был создан внутри safeFrag
в самом начале.
Дальше идет постобработка результата для некоторых случаев в IE — удаление вставленных автоматически tbody в таблицы и добавление удаленных автоматически в пробелов вначале нашего года.
Все, ура, мы можем получить наш результат из контейнера с помощью стандартной функции childNodes
и удалить его из safeFrag
.
Пробегаемся по полученному набору нод и добавляем их в наш собственный фрагмент, который попадет в кеш (если он кешируется, см. выше) и отдается нам назад в parseHTML
, йуху! Там в наш результат мержится полученный клонированный фрагмент (если получен из кеша), либо он сам.
Неужели все?
Да, все. Тем не менее метод clean
, который мы разбирали, на самом деле несколько сложнее (метод пользуется в нескольких местах в jQuery внутри) и я намеренно просто не рассматривал тот его функционал, который не используется именно в нашем случае, для создания элементов из обычной строки, а предназначен для совсем других целей.
Заключение
Думали, все просто? К сожалению, нет. Но мы осилили! Кстати, пока писал статью и ковырялся в исходниках, тоже нашел что-то новое 🙂
Пишите красивый код и получайте от этого удовольствие, мальчики и девочки.
ссылка на оригинал статьи http://habrahabr.ru/post/164533/
Добавить комментарий