Javascript.RU

querySelectorAll на практике

Метод element.querySelectorAll("css query") был введен в браузеры уже достаточно давно, но, честно говоря, я не встречал особо статей про него.
Что ж, исправим эту ситуацию.

Наверняка, Вы знакомы с jQuery. И наверняка знаете про функцию $, возвращающую элементы, соответствующие css-селектору (в ранних версиях можно было еще использовать XPath, но потом от него отказались).

Так вот, querySelectorAll делает то же самое, но нативными средствами браузера.

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

Тектовые ноды, комментарии, CDATA-секции выбрать этим методом нельзя, только элементы.

Поддерживается всеми современными браузерами, Internet Explorer — начиная с версии 8.

Так вот, querySelector почти то же самое, что и querySelectorAll, но возвращает только один (первый попавшийся) элемент, либо Null.

qSA — родной метод браузера, и работает быстрее. По крайней мере в Опере на тестовом образце document.querySelectorAll() показал в среднем 9 мс против 43 мс $(). Причем, скорость очень мало уменьшается от ширины и глубины DOM-дерева.

Как ни парадоксально, в той же Опере .querySelectorAll("div") и .querySelectorAll(".my") работает примерно вдвое быстрее, чем .getElementsByTagName("div") и .getElementsByClassName("my") соответственно.

qSA имеет доступ к таким псевдоклассам, как :focus и :hover

Итак, а с чем же его можно есть?

Если Вы осуществляете траверс по селектору без использования :animated и других специфичных для jQ псевдоклассов, можно использовать

$(document.querySelectorAll("#myDiv p"))

вместо

$("#myDiv p")

На обертывание staticNodeList в объект jQuery, конечно, уйдет какое-то время, но, в целом, такой код выполнится быстрее.

При помощи qSA можно достаточно просто определить, наведен ли курсор на элемент.
Пример (Opera, Safari, Chrome):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
	<head>
		<title></title>
		<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
		<style type="text/css">
			.samplediv {
				border: dashed red 1px;
				margin: 30px;
			}
			.samplediv #hovered {display: none}
			.samplediv:hover #hovered {display: inline}
			.samplediv:hover #nothovered {display: none}
		</style>
	</head>
	<body>
		<div class="samplediv" id="test">
			<p>Javascript: <span id="status">I am not hovered</span></p>
			<p>Css: <span id="nothovered">I am not hovered</span><span id="hovered">I am hovered</span></p>
		</div>
		<script type="text/javascript">
			function isHovered(o){return (o.parentNode && o.parentNode.querySelector(":hover") == o);}
			
			document.getElementById("test").addEventListener('mouseover', function(){
				if (isHovered(this)) document.getElementById("status").innerHTML = "I am hovered";
			}, false);
			document.getElementById("test").addEventListener('mouseout', function(){
				if (!isHovered(this)) document.getElementById("status").innerHTML = "I am not hovered";
			}, false);
		</script>
	</body>
</html>

В Firefox 3.6 статус так и останется залипшим на «hovered». Это связано с тем, что в этом замечательном браузере на момент mouseout css-стиль еще не обновился. При этом на mouseover работает отлично. Т.е, выходит, что Firefox обновляет css между mouseout и mouseover. По-моему, это нелогично и неправильно. Разработчики Firefox, поправьте это, пожалуйста!

Но вернемся к функции isHovered(). Работает она на одном достаточно простом предположении: если у родителя элемента есть элементы, на которые наведен курсор, и один из этих элементов (нулевой в массиве, траверс начинается с непсредственных детей) — сам исходный элемент, то на исходный элемент наведен курсор.

У documentElement родитель — document, соответственно, код тоже работает. У document же родителя нет, но и hover для него бессмысленнен.

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

document.querySelector(":focus")

Как видите, метод достаточно мощный и быстрый. Не без острых краев и пока не универсальный, но все-таки

Само собой, это не единственные применения, просто think portals CSS.

И, просто прошу, если что-то можно сделать на «голом» CSS, не стоит вообще делать это яваскриптом.

P.S: Давненько я уже ничего не писал. В следующий раз, скорее всего, напишу про CSS3 animation, и когда ее следует использовать вместо $(…).animate(…)

+13

Автор: ixth, дата: 21 сентября, 2010 - 10:27
#permalink

Почему Sizzle не использует querySelectorAll для ускорения части своих выборок? Или проверка на валидность селектора слишком трудозатратна и сведет выгоду на нет?


Автор: subzey, дата: 21 сентября, 2010 - 16:23
#permalink

Оказывается, использует. Но с кучей проверок. Универсальность же!
http://code.jquery.com/jquery-1.4.2.js — вот тут можно поискать по тексту «querySelector»


Автор: ixth, дата: 22 сентября, 2010 - 15:17
#permalink

Я догадывался еще с тех пор, как впервые услышал про qsa. Резиг не мог упустить такой шанс. Не сказал бы, что проверок много: фильтруется quirks mode Safari и еще какая-то фигня с XML/non-XML.
Правда, непонятно как фильтруются чисто jQuery-вские селекторы, которых qsa не понимает. Думаю, они отфильтровываются еще до Сиззла.


Автор: B@rmaley.e><e, дата: 28 ноября, 2010 - 00:04
#permalink
if ( !seed && context.nodeType === 9 && !isXML(context) ) {
	try {
		return makeArray( context.querySelectorAll(query), extra );
	} catch(e){}
}

return oldSizzle(query, context, extra, seed);

Если в querySelector передать неподдерживаемый селектор, то он выбросит exception. Вот тут на этом и сыграли.


Автор: Яростный Меч, дата: 22 сентября, 2010 - 09:17
#permalink

IE, как водится, в роли ложки дегтя.


Автор: blessmaster, дата: 14 ноября, 2010 - 04:22
#permalink

Слишком много оговорок, чтобы на него расчитывать. При том, что последние jQuery таки научили с его помощью оптимизировать производительность, если он доступен, а jQuery будет работать и без него. Но за статью, конечно же, спасибо!


Автор: Гость (не зарегистрирован), дата: 25 октября, 2012 - 00:33
#permalink

я думаю querySelectorAll быстрее getElements* потому что последние возвращают "живые" коллекции. в этом и загвоздка для производительности видимо


Автор: Гость (не зарегистрирован), дата: 5 января, 2013 - 17:17
#permalink

querySelectorAll также возвращает "живые" коллекции


Автор: Grifit, дата: 10 января, 2014 - 19:42
#permalink

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


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
1 + 0 =
Введите результат. Например, для 1+3, введите 4.
 
Поиск по сайту
Другие записи этого автора
subzey
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Популярные таги
Последние комментарии
Последние темы на форуме
Forum