Модераторы: Sardar, Aliance
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> jpath - убийца xpath и cssquery 
:(
    Опции темы
cruelangel
Дата 28.10.2007, 01:16 (ссылка)   | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



основной недостаток xpath и css selectors заключается в том, что логика их работы основана на продвижении сверху вниз. это удобно, когда мы начинаем путь из корня и ищем узлы-потомки, но совершенно не удобно, если мы ищем соседей или родителей.
в jpath все направления движения равноправны - вы просто выбираете ось и двигаетесь по ней как и по любой другой.

пара примеров:
var cells= jpath( table, '/2tr /td' ); // выбирает все ячейки, находящиеся во второй строке таблицы
var table= jpath( cell, '^1table' )[0]; // выбирает самую вложенную таблицу в которой находится ячейка

как видно, каждый путь состоит из одного или большего количества операторов, пробельные символы между операторами игнорируются.
каждый оператор состоит из:
1. префикс оси. обязательный параметр. определяет в какую сторону будем двигаться. примеры перфиксов: ^ /  + - @
2. индекс. если он не указан, то будут выбраны все узлы находящиеся на этой оси. если оказан, то будет выбран только узел с указанным номером (1 - первый узел).
3. название узла. если он не указан - будут выбираться любые узлы. если указана звёздочка, то любые хтмл-элементы. ну и если указано собственно название, то будут выбираться только соответствующие узлы (регистр не важен). примеры названий: body table #text #comment

возвращаемое значение - массив найденых узлов.

пока реализовано 6 осей:
1. младшие братья. префикс +. на этой оси располагаются все братья, расположенные ниже текущего элемента. например, если вам нужно перейти к следующему параграфу, просто напишите:
var currentParagraf= jpath( currentParagraf, '+1p' )[0];
2. старшие братья. префикс -. то же самое, но в другую сторону. все предыдущие параграфы можно получить так:
var paragrafs= jpath( currentParagraf, '-p' )[0];
3. предки. префикс ^. на этой оси расположены все предки. непосредственного предка можно получить так:
var parentNode= jpath( node, '^1*' )[0];
4. дети. префикс /. например, следующий код, в отличие от childNodes, вернёт все дочерние html-элементы:
var childs= jpath( node, '/*' );
5. потомки. префикс //. на этой оси располагаются вообще все узлы-потомки. следующий код демонстрирует выборку всех таблиц на странице:
var tables= jpath( document, '//table' );
6. аттрибуты. префикс @. следующий код показывает как найти все ссылки на странице:
var links= jpath( document, '//a@href^1' );

в планах:
1. ось элементов расположенных ниже на странице. префикс ++. возвращает всех младших братьев узла и его родителей, а также всех их детей.
2. противоположная ось. префикс --.

ну и собственно текущий сырец: 
Код
var jpath= function( nodes, path ){ // version 0.7
    if(! nodes ) nodes= [ document ];
    else if(! ( nodes instanceof Array ) ) nodes= [ nodes ];
    path= path.replace(
        /(.*?)(\/\/|[-+\/.@^])(\d+|\?])?(\*|#?[a-zA-Z]\w*)?(?:(~)?\=(.)(.*?)\6(\w*))?\s*/g, 
        function( str, error, axis, numb, nodename, matchtype, valdel, value, regmod ){
            if( error ) throw 'unsupported jpath expession "' + error + '"';
            var newnodes= [];
            var namefilter= nodename
                ? ( ( nodename == '*' ) ? /^\w+$/ : new RegExp( '^' + nodename + '$', regmod ) )
                : /^/
            var valuefilter= new RegExp(
                ( matchtype === '~' )
                    ? '(?:^|\\s)' + value + '(?:\\s|$)'
                    : value
                , regmod
            )
            numb= parseInt( numb );
            for( var i=0; i < nodes.length; ++i ){
                var node= nodes[i];
                var found= [];
                switch( axis ){
                    case '/': found= node.childNodes; break;
                    case '//':
                        if( nodename ){
                            found= node.getElementsByTagName( nodename );
                        } else {
                            found.push( node );
                            var elems= node.getElementsByTagName( '*' );
                            for( var j= 0; j < elems.length; ++j ) found.push( elems[j] );
                            found= jpath( found, '/' );
                        }
                        break;
                    case '@':
                        var attrs= node.attributes;
                        for( var j= 0; j < attrs.length; ++j ){
                            if( attrs[j].nodeValue ) found.push({
                                'parentNode': node,
                                'nodeName': attrs[j].nodeName,
                                'nodeValue': attrs[j].nodeValue,
                                '$raw': attrs[j]
                            })
                        }
                        break;
                    case '.':
                        var raw= ( node.$raw || node );
                        for( var j in raw ){
                            found.push({
                                'parentNode': node,
                                'nodeName': j,
                                'nodeValue': raw[j],
                                '$raw': raw[j]
                            })
                        }
                        break;
                }
                var k= 0;
                var filtered= [];
                while( true ){
                    switch( axis ){
                        case '^': node= node.parentNode; break;
                        case '+': node= node.nextSibling; break;
                        case '-': node= node.previousSibling; break;
                        default: node= found[ k++ ];
                    }
                    if(! node ) break;
                    if( namefilter.test( node.nodeName ) && valuefilter.test( node.nodeValue ) ){
                        filtered.push( node );
                        if( numb === filtered.length ) break;
                    }
                }
                if( numb ) filtered= ( numb === filtered.length ) ? [ filtered[ numb - 1 ] ] : [];
                for( var j= 0; j < filtered.length; ++j )
                    for( var k= 0; k < newnodes.length; ++k )
                        if( filtered[ j ] === newnodes[ k ] )
                            { delete filtered[ j ]; break; }
                for( var j= 0; j < filtered.length; ++j )
                    if( j in filtered ) newnodes.push( filtered[ j ] );
            }
            nodes= newnodes;
            return '';
        }
    )
    if( path ) throw 'unsupported jpath expession "' + path + '"';
    for( var i= 0; i < nodes.length; ++i ) if( '$raw' in nodes[i] ) nodes[i]= nodes[i].$raw;
    return nodes;
}



Это сообщение отредактировал(а) cruelangel - 2.11.2007, 18:26
PM MAIL   Вверх
Sardar
Дата 28.10.2007, 01:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 16
Всего: 317



Цитата(cruelangel @  28.10.2007,  00:16 Найти цитируемый пост)
но совершенно не удобно, если мы ищем соседей или родителей

А как же с осями following-sibling/preceding-sibling и ancestor?
Также в XPath можно пользоваться (довольно ограниченным) набором функций в предикатах, например брать только префикс.

P.S. код аккуратный, читаю в удовольствие smile


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
cruelangel
Дата 28.10.2007, 02:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



блин, я опять изобрёл велосипед smile тем лучше, для dom-совместимых браузеров можно будет компилировать в xpath.

хотя нет, фильтрацию по свойствам дом-объектов в xpath не засунешь...

насчёт функций - думаю не стоит излишне перегружать язык запросов.

Это сообщение отредактировал(а) cruelangel - 28.10.2007, 02:20
PM MAIL   Вверх
SelenIT
Дата 28.10.2007, 02:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 1
Всего: 401



Цитата(cruelangel @  28.10.2007,  02:09 Найти цитируемый пост)
фильтрацию по свойствам

Сорри, а где таковая в jpath? А атрибуты вроде есть и там, и там...


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
cruelangel
Дата 28.10.2007, 02:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



SelenIT, этого пока нет, как и фильтрации вообще smile через префикс $ я планирую добавить доступ к свойствам. пока, правда, не решил как. либо их нужно сериализовать в строку, либо расширить jpath и на объектную модель яваскрипта...

второй вариант помог бы избежать таких монстров:

var height= document && document.documentEleent && document.documentElement.offsetHeight;

smile

Это сообщение отредактировал(а) cruelangel - 28.10.2007, 02:31
PM MAIL   Вверх
SelenIT
Дата 28.10.2007, 02:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 1
Всего: 401



А обязательно делать фильтрацию по свойствам встроенной в сам язык запросов? Не удобнее ли будет иметь два раздельных инструмента - универсальный XPath (можно даже со стандартным синтаксисом, дабы не множить сущности) и универсальный фильтр по JS-свойствам для коллекций (неважно каким путем полученных)?


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
cruelangel
Дата 28.10.2007, 12:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



ты предлагаешь что-то вроде этого:

jpath( jpath( '//div' ).filter( function( nodes ){ ... } ), '//span' )

вместо этого:

jpath( '//div[..]//span' )

?

а стандартный синтаксис слишком громоздкий...
PM MAIL   Вверх
cruelangel
Дата 28.10.2007, 19:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



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

найти все тэги с классом submit:
var submits= jpath( document, '//* @class=!\\bsubmit\\b! ^1' );
или:
var submits= jpath( document, '//* .className=!\\bsubmit\\b! ^1' );

первый вариант ищет по аттрибутам, второй - по свойствам.

ещё пример - поиск всех элементов с установленным стилем style.display="none":
var hiddens= jpath( document, '//* .style .display=!^none$! ^2' );

как видно в этом выражении 4 оператора, что они делают:
1. находит все элементы-потомки
2. находит для них свойства style
3. для всех свойств style находит свойства display значения которых удовлетворяют регулярному выражению обрамлённому восклицательными знаками (вместо них как и в pcre можно использовать любые другие символы, экранированные ограничители внутри регулярки использовать пока нельзя)
4. для всех найденных свойств display находит второго предка, то есть хтмл-элемент к которому он относится.



Это сообщение отредактировал(а) cruelangel - 28.10.2007, 19:34
PM MAIL   Вверх
SelenIT
Дата 28.10.2007, 19:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 1
Всего: 401



Цитата(cruelangel @  28.10.2007,  12:09 Найти цитируемый пост)
ты предлагаешь что-то вроде этого:
jpath( jpath( '//div' ).filter( function( nodes ){ ... } ), '//span' )

Сдаюсь, пример убедил smile

Цитата(cruelangel @  28.10.2007,  19:30 Найти цитируемый пост)
в общем, от квадратных скобочек я отказался

Имхо, зря... ;)


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
cruelangel
Дата 28.10.2007, 20:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



var headings= jpath( document, '//* .nodeName=/^H\\d$/ ^1' );

- ищет все заголовочные элементы. 

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

var headings= jpath( document, '//* .nodeName=/^h\\d$/i ^1' );

var all_h1= jpath( document, '//h1=!!i' );

Добавлено @ 20:15
SelenIT, просто если делать квадратные скобки, то для реализации возможности вкладывать их друг в друга придётся мутить полноценный токенайзер, а не как у меня сейчас - одной регуляркой идёт выделение операторов и поточная их обработка.

Это сообщение отредактировал(а) cruelangel - 28.10.2007, 20:15
PM MAIL   Вверх
cruelangel
Дата 28.10.2007, 22:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



позаимствовал из css один селектор:

var lefts= jpath( document, '//* @class~="left" ' );

- найдёт все элементы с классом left.

всё, что внутри кавычек также является регулярным выражением, но к нему будет автоматически дописано '(?:^|\\s)' вначале и '(?:\\s|$)' в конце


Это сообщение отредактировал(а) cruelangel - 28.10.2007, 23:05
PM MAIL   Вверх
cruelangel
Дата 31.10.2007, 16:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



пофиксил багу с неработающим '//'

Добавлено через 6 минут и 49 секунд
попытка воспользоваться благами цивилизации - xpath - благополучно провалилась smile лишком маломощная эта технология ;-)
PM MAIL   Вверх
cruelangel
Дата 2.11.2007, 15:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 319
Регистрация: 12.9.2007

Репутация: 1
Всего: 8



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

Это сообщение отредактировал(а) cruelangel - 2.11.2007, 15:44
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Здесь публикуют скрипты, которые уже проверены в обсуждениях других тем (при этом полезно поставить ссылки на все смежные обсуждения) или переносятся кем-либо из модераторов по просьбе участников, если видно, что в результате обсуждения темы был написан полезный или интересный скрипт. Третий возможный вариант - участник форума публикует скрипт, заведомо известный как полезный и эффективный, для, возможно, небольшой доработки и обсуждения.
 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Наши скрипты | Следующая тема »


 




[ Время генерации скрипта: 0.1027 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.