Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   QSA CSS Selector Engine v1.0 - Выкладываю CSS-селектор по просьбе трудящихся (https://javascript.ru/forum/project/24330-qsa-css-selector-engine-v1-0-vykladyvayu-css-selektor-po-prosbe-trudyashhikhsya.html)

poorking 01.02.2012 00:33

float,
ну так именно об этом и речь, главное скорость и достоверные результаты. Чем-то приходится жертвовать. Хотя что-то мне подсказывает, что совсем не сильно понизится производительность если в цикле будет 5 дополнительных if, я б даже сказал что практически совсем не понизится, будет регулярка такая TYPE|ID|CLASS|ATTR|PSEUDO|UNMATCHED, все виды простых селекторов могут быть описаны со всей грубостью и жестокостью, а если не один из них не подойдет под критерий, то мы увидим UNMATCHED, и просто смотрим, если этот карман содержит значение, кидаем ошибку. Ну на словах просто, на практике чуть больше проверок будет возможно..

devote 01.02.2012 00:47

poorking,
спасибо, я реально от подобных повисаний писал специально условие. Но как оказалось не дописал. То-есть нужно было еще в одном месте написать условие.

Сейчас исправил версию, подправил регу. Дописал условие на проверку гнилых селекторов.

devote 01.02.2012 01:03

насчет ковычек хотел сказать. Парсер работает по такому принципу:
допустим есть атрибут [blah*="test"] или такой [blah*='test'] или такой [blah*=test] в этом случае он его разобъет так:
blah
*=
test
если же попадется такой как вы указали: [blah*='test\"]
то он разобъет его так:
blah
*=
'test\"
тоесть будет искать подстроку вместе с кавычками в атрибуте.

poorking 01.02.2012 01:15

Нашел оставленную на память регуляшку. Описание скобок:

( 1 - Запятая или комбинатор слева - ошибка если заполнено)?

(

2 - литерал ИД или КЛАССА (чтобы понимать для кого скобка 3)
3 - значение ИД или КЛАССА

ИЛИ

(4 - Пространство имен селектора ТИПА)?
5 - Значение селектора типа

ИЛИ

( 6 - Пространство имен селектора атрибута )?
7 - имя атрибута
(8 - тип сравнения значения аттрибута с переданной строкой
9 или 10 или 11 - значение аттрибута)?

ИЛИ

12 - псевдокласс
(13 - аргумент псевокласса)?

ИЛИ

14 - ошибка
)

(
15 - запятая или комбинатор (если нет его, то далее будет сова прстой селектор)
)?

var GRINDER = /\s*([,>+~])?\s*(?:([#\.])(-?(?:\\[\s\S]|[_a-zA-Z\u0080-\uFFFF])(?:\\[\s\S]|[\w\-\u0080-\uFFFF])*)|(?:(-?(?:\\[\s\S]|[a-zA-Z_\u00C0-\uFFFF])(?:\\[\s\S]|[\-\w\u00C0-\uFFFF])*|\*)?\|)?(-?(?:\\[\s\S]|[a-zA-Z_\u00C0-\uFFFF])(?:\\[\s\S]|[\-\w\u00C0-\uFFFF])*|\*)|\[\s*(?:(-?(?:\\[\s\S]|[a-zA-Z_\u00C0-\uFFFF])(?:\\[\s\S]|[\-\w\u00C0-\uFFFF])*|\*)?\|)?(-?(?:\\[\s\S]|[_a-zA-Z\u0080-\uFFFF])(?:\\[\s\S]|[\w\-\u0080-\uFFFF])*)(?:\s*(\S|)=\s*(?:'((?:\\[\s\S]|[^'\n\r\f])*)'|"((?:\\[\s\S]|[^"\n\r\f])*)"|(-?(?:\\[\s\S]|[_a-zA-Z\u0080-\uFFFF])(?:\\[\s\S]|[\w\-\u0080-\uFFFF])*)))?\s*\]|:(-?(?:\\[\s\S]|[_a-zA-Z\u0080-\uFFFF])(?:\\[\s\S]|[\w\-\u0080-\uFFFF])*)(?:\(((?:'(?:\\[\s\S]|[^'\n\r\f])*'|"(?:\\[\s\S]|[^"\n\r\f])*"|\((?:\\[\s\S]|[^()])*\)|\\[\s\S]|[^()])*)\))?|([^,>+~\s]+))(?:\s*(?:$|([\s,>+~])\s*))?/g;


Если это погонять в цикле,
while ( m = GRINDER.exec(selectror) )

то из

var selector = " NS|TYPE#ID.CLASS + :PSEUDO(ARG)[NS|ATTR='VAL'] "


можно собрать что-то типа
[ [ ["TYPE", "NS"], ["ID"], ["CLASS"] ], [ ["PSEUDO", "ARG"], ["ATTR", "=", "VAL", "NS"] ] ]


Каждую итерацию по номеру заполненных скобок мы можем узнавать какого типа простой селектор и сразу брать его, уже разобранного на части. Когда появлялся m[15], я по содержимому, определял что будет дальше, следующая последовательность (комбинатор + последовательность простых) или вообще следующий одиночный селектор (если там запятая)

Чтобы понимать в коде выборок к какому типу простого селектора относятся эти распарсенные куски, можно определить константы для разных типов и записывать значения например свойством массива (в котором кусочки простого селектора). Так же для оберток этих простых селекторов (последовательностей простых селекторов) и комбинаторов. Потом можно будет эти sequense сортировать, чтобы применять в том порядке в каком лучше. Ну это я так думаю, регулярное выражение писал давно, мне лень тестировать его производительность, но теоретически оно не шибко шустрое, 15 скобок, хотя я не исследовал производительность регекспов.

За парсер, код которого к сожалению не сохранился, могу только одно сказать, что синтаксис селекторов полностью соответствовал спецификации и нельзя было передать не правильный селектор, 100% было бы SyntaxError

poorking 01.02.2012 01:20

Цитата:

Сообщение от devote
тоесть будет искать подстроку вместе с кавычками в атрибуте.

Ну вот в реге что я привел, я значение ищу без ковычек, поэтому, значение находится в одной из 3х скобок '(STRING1)'|"(STRING2)"|(IDENT), не оптимально конечно, но тогда было интересно именно разобрать правильно, как я уже говорил на механизме выборок я остановился, find Просто возвращал [], а match false, хотя каркас был, надо будет восстановить

devote 01.02.2012 01:20

а что начет таких селекторов?
NS|TYPE#ID.CLASS + :PSEUDO(ARG)[NS|ATTR='VAL'][NS|ATTR='VAL'][NS|ATTR='VAL']

несколько атрибутов подряд

poorking 01.02.2012 01:23

все будет ок, за одну итерацию находит один простой селектор

devote 01.02.2012 01:26

Цитата:

Сообщение от poorking
все будет ок, за одну итерацию находит один простой селектор

да но эти атрибуты относятся к одному элементу, тоесть это не разные селекторы, а просто для элемента нужно найти допустим:
a[href=blah][rel=test]
то есть найти элемент А с трибутом href который содержит blah и атрибутом rel который содержит test

Octane 01.02.2012 01:30

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

<prefix:tag-name class="class\name">

alert("prefix\\:tag-name.class\\\\name".replace(/\\([^\w\s])/g, "$1"))

все что внутри кавычек в значениях атрибутов предварительно через escape заменено.

devote 01.02.2012 01:36

Цитата:

Сообщение от Octane
можешь сказать достаточно ли такой регулярки, чтобы убрать ненужные слэши?

не понимаю зачем поставлены слеши после prefix это что двоеточие за экранировали чтоль? тут какие-то правила экранирования как я понял. обычно достаточно двойные слеши заменить на одиночные.

poorking 01.02.2012 01:42

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

devote 01.02.2012 01:51

Цитата:

Сообщение от poorking
Да, это можно определить и собирать распарсенные простые селекторы в кучки - последовательности

вот именно это и приводит к тому что TYPE проходит в любой части селектора... Увы но регой четко не сделаешь... Моя рега работает так же по правилам стандартов. То-есть все раскладывает по правильному порядку. Но в виду того что нужно выяснять принадлежность дополнительных атрибутов для элементов приводит к тому что вся логика просто рушится. Я не говорю что нельзя все нормально организовать, конечно же можно. Просто это повлияет на скорость, причем значительно. Поэтому порядок не особо страшный казус. Хотя у меня все же он вроде соблюдается... если судить по реге... Что то не припомню что бы такой селектор воспринимался:
a:not([href*=test]).test

poorking 01.02.2012 01:54

я убираю такой
string.replace(/\\([\s\S])/g, "$1");

Octane 01.02.2012 01:56

Цитата:

Сообщение от devote
не понимаю зачем поставлены слеши после prefix это что двоеточие за экранировали чтоль?

Ну да в CSS надо экранировать такие символы
prefix\:tag-name.class\\name {…}

а второй слэш появляется из-за экранирования в JavaScript-строке

devote 01.02.2012 01:59

Цитата:

Сообщение от Octane
Ну да в CSS надо экранировать такие символы

ну в CSS да.. но в селекторах что мы обсуждаем, двоеточие не экранируется... ибо Namespace там нужно указывать так:
prefix|tag-name.class\name

poorking 01.02.2012 02:00

Цитата:

Сообщение от devote
вот именно это и приводит к тому что TYPE проходит в любой части селектора

не ) это уже не регексп решает, но проверить просто, вы смотрите, если это начало последовательности, то после сборки распарсенного простого селектора ставите флажок, мол TYPE уже был. А в том ветвлении, где вы проверяете не селектор ли типа заматчен, вы смотрите на этот флажок и кидаете ошибку если он тру. Если вы в итерации видите что это конец последовательности простых селекторов то флажок переключаете на фолс

alert(document.querySelectorAll("a:not([href*=test]).test"));


Нативный qsa ест, значит так можно )

devote 01.02.2012 02:14

Цитата:

Сообщение от poorking
Нативный qsa ест, значит так можно )

Да и правда, мой тоже ест :D только что проверил :)

monolithed 02.02.2012 14:28

var hasQSA = (function() {
    var div = document.createElement('div');
    div.innerHTML = "<p class='TEST'></p>";

    if (!div.querySelectorAll || div.querySelectorAll(".TEST")|0)
        return 0;
    
    div = null;
    return 1;
})();

alert(hasQSA);

melky 02.02.2012 14:30

monolithed, а почему не прокатывает простая проверка наличия этого свойства в документе* ?
var hasQSA = "querySelector" in document;

* при условии, что это свойство не добавили для "заглушки".

poorking 02.02.2012 14:42

melky,
Из Sizzle
// Safari can't handle uppercase or unicode characters when
		// in quirks mode.
		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
			return;
		}

melky 02.02.2012 15:23

Цитата:

Сообщение от poorking (Сообщение 154790)
Safari can't handle uppercase or unicode characters when in quirks mode.

EN ---> RU

Safari в режиме совместимости не может обрабатывать символы в верхнем регистре или в юникоде.

Наверное, не понял предложения, но при чём тут проверка наличия поддержки querySelector?
На MDN нашёл баг на Safari, это он и есть ?

Если она не поддерживает символы в верхнем регисте в режиме совместимости, почему бы не перевести строку селектора к нижнему регистру? .TEST или .test, в html разницы быть не должно.

ЗЫ Если вам показалось, что я доказываю свою правоту, то это не так. (после марума может так казаться :)) Я хочу разобраться и понять.

Цитата:

Сообщение от monolithed (Сообщение 154791)
В IE7- тоже не прокатывает

не имплементировано, IE8+.

poorking 02.02.2012 15:32

Цитата:

Сообщение от melky
при чём тут проверка наличия поддержки querySelector?

Возможно семантически не верно названа переменная (hasQSA), но этот баг, не смотря на поддержку браузером метода querySelector - есть причина использовать самописный движок нежели нативный, а значение hasQSA в QSA CSS Selector Engine именно для этого и вычисляется

poorking 02.02.2012 15:34

Цитата:

Сообщение от melky
почему бы не перевести строку селектора к нижнему регистру? .TEST или .test, в html разницы быть не должно.

All Selectors syntax is case-insensitive within the ASCII range
http://www.w3.org/TR/selectors/#casesens

Возможно в этом, хотя я задумался и почти уже не знаю даже


ASCII включает национальный алфавит, так что, выходит вы правы с мыслью о том, что можно привести селектор к нижнему регистру, надо потестить

devote 02.02.2012 18:15

Цитата:

Сообщение от monolithed
if (!div.querySelectorAll || div.querySelectorAll(".TEST")|0)

я думаю пусть этим занимается обфускатор

monolithed 03.02.2012 09:21

Цитата:

Сообщение от devote
я думаю пусть этим занимается обфускатор

вообще я хотел сделать акцент на этом div = null;

devote 03.02.2012 10:18

Цитата:

Сообщение от monolithed
вообще я хотел сделать акцент на этом div = null;

как я заметил обфускатор вообще удаляет все эти div = null; считает что без них лучшее.

monolithed 03.02.2012 10:37

Цитата:

Сообщение от devote
как я заметил обфускатор вообще удаляет все эти div = null; считает что без них лучшее.

Я имею ввиду, то что нет смысла писать это выражение два раза

var hasQSA = (function() {
    var div = document.createElement('div');
    div.innerHTML = "<p class='TEST'></p>";

    if (!div.querySelectorAll || div.querySelectorAll(".TEST")|0)
        return 0;
    
    *!* 
    div = null; 
    */!*
    return 1;
})();

alert(hasQSA);

devote 03.02.2012 10:44

Цитата:

Сообщение от monolithed
Я имею ввиду, то что нет смысла писать это выражение два раза

да я понял к чему ты ведешь.

devote 14.03.2012 10:34

во время работы с селектором, обнаружил ошибку:
не искал по селекторам если они содержали слеш "\"

Качаем новую версию, в первом топике.

так же залил на github

FINoM 18.03.2012 01:13

Цитата:

Сообщение от devote
qsa.querySelectorAll( "div + p > a", document.getElemetById("myNode") );

Давно хотел узнать: а можно ли повесить в прототип html узла метод, чтоб работало в старых IE?
Если рассматривать эту библиотеку в качестве примера, то можно было-бы убрать контекст из второго аргумента.

Nekromancer 18.03.2012 01:52

FINoM,
htc файлик подключи, там вешай сразу но ноде это свойство. Ещё document.createElement заменить надо, что бы перед возвращением вставляло ноду в документ.

FINoM 18.03.2012 01:56

Цитата:

Сообщение от Nekromancer
Ещё document.createElement заменить надо, что бы перед возвращением вставляло ноду в документ.

Эм, я думал htc применяется ко всем, в том числе и созданным элементам.

devote 18.03.2012 13:59

Цитата:

Сообщение от FINoM
Эм, я думал htc применяется ко всем, в том числе и созданным элементам.

Так и есть, но только после того как его поместили в DOM. А сразу же после создания нет. А вообще если нужен .htc могу скинуть, у меня сделан специально для ИЕ<8 подобный файл. Но сюда я его не выкладывал.

FINoM 18.03.2012 17:16

Понятно. А querySelectorAll и querySelector применяются на не вставленные в dom элементы?

devote 18.03.2012 17:39

Цитата:

Сообщение от FINoM
Понятно. А querySelectorAll и querySelector применяются на не вставленные в dom элементы?

да

devote 18.03.2012 17:52

добавил на гитхаб HTC файлы, если кому надо качайте.

FINoM 18.03.2012 17:55

Цитата:

Сообщение от devote
добавил на гитхаб HTC файлы, если кому надо качайте.

Может автоматически подключишь? Мне лично, пока, оно не надо, да и я сам бы мог это сделать, но хорошо бы было иметь кроссбраузерный querySelector и querySelectorAll, работающий, как в спецификации, с одним аргументом.

devote 18.03.2012 18:01

Цитата:

Сообщение от FINoM
но хорошо бы было иметь кроссбраузерный querySelector и querySelectorAll, работающий, как в спецификации, с одним аргументом.

htc файлы которые я залил именно это и делают, интегрируются в элементы и работают с одним аргументом как в спецификации.

FINoM 18.03.2012 18:03

Круть

devote 18.03.2012 18:09

ну а подключать примерно так:
<!--[if lt IE 9]>
<style type="text/css">
	* { behavior: url('qsa.min.htc'); }
</style>
<![endif]-->

В ИЕ версии 8 расширяет встроенные селекторы до поддержки CSS3 селекторов.

-


Часовой пояс GMT +3, время: 07:51.