X

Здравствуйте, гость ( Вход | Регистрация )

Скрыть объявления

Объявления

Реализация метода getElementsByClassName  
 Saveliy
сообщение 27.4.2016, 11:41
Сообщение #225982


Special

Группа: Главные администраторы

Сообщений: 12973
Спасибо сказали: 1772 раза

Реализация метода getElementsByClassName

Что-то у меня голова идет кругом. Обычно все в голове по полочкам, а тут не могу сконцентрироваться и все перемешалось.

И снова этот IE (Internet Explorer).. Ради него и некоторых других недобраузеров снова костыли.. Но нужно сделать элегантно и универсально (кроссбраузерно).

Давайте напишем вместе универсальную реализацию метода getElementsByClassName (поиск элементов по классу).
Сколько я не искал в сети, нет ни одной нормальной реализации. Вариантом и инфы масса, отчего и запутался в конец, хотя по сути вопрос простой. Может я и быстрее решу данный вопрос, но конспектируя данным образом условное ТЗ и свои действия, может в голове проще будет собрать все во едино.

Некоторые условия функции:
  • Самое главное и прямое назначение, найти элементы, содержащие указанный класс (или несколько классов).
  • Возможность производить поиск не от всего документа document.getElementsByClassName, а от конкретного элемента elem.getElementsByClassName
  • Возможность делать выборку по нескольким классам в режиме OR (или).
  • Возможность делать выборку по нескольким классам в режиме AND (и).
Что получается: по сути в нашей функции в выборке будут участвовать методы:
querySelectorAll, getElementsByClassName, getElementsByTagName
В зависимости от наличия метода в браузере, будем производить разбор селекторов по одному из них.

Некоторые скажут, в принципе все решается одной строкой методом querySelectorAll, так как большинство браузеров имеют данный нативный метод. Но! Парадокс, не парадокс, а у многих на работе (и дома) используются старые ОС и главное, старые браузеры, которые пользователи просто почему-то не хотят, не могут и т.д. их обновить или установить адтернативу родному. Ну что поделать, не будем скидывать со счетов таких пользователей и наша задача их не обделить (хотя некоторые и ратуют за то, чтобы заставить пользователей обновляться, а не писать ради них лишний программный код и не лепить костыли). Но все же..

У кого есть идеи и предложения своих вариантов, выкладывайте.

P.S. Библиотеку jQuery не предлагать, ее не рассматриваем.
  Пользователя нет на форуме
 
 
Ответить
Ответов (1 - 8)
 Zmeiarm
сообщение 27.4.2016, 11:47
Сообщение #225985


Любопытный

Группа: Супермодератор

Сообщений: 3331
Спасибо сказали: 1171 раз

:super_smilies_001: :super_smilies_001: Сава на форуме может пара человек поняли что ты написал.
куда там еще советовать...
  Пользователя нет на форуме
 
 Saveliy
сообщение 27.4.2016, 11:54
Сообщение #225986


Special

Группа: Главные администраторы

Сообщений: 12973
Спасибо сказали: 1772 раза

Будем приобщать! :icon_dance3: :super_smilies_001:
  Пользователя нет на форуме
 
 Saveliy
сообщение 27.4.2016, 20:11
Сообщение #226024


Special

Группа: Главные администраторы

Сообщений: 12973
Спасибо сказали: 1772 раза

Итак. Пока выложу шаблон, который мы и заполним так, как поставим условия проверки.

CODE
//-------------------------------------------------------------------//
// Функция поиска элементов по классу.
// Никаких здесь поисков по TAG и атрибутам. Данная функция будет,
// как часть метода find, в котором уже и будет выборка по типу
// селекторов.
//
// (c) Saveliy Severniy
//-------------------------------------------------------------------//

ss.getElementsByClass = function(getClass,parent){
     var elem = parent || document;
     var result = [];
    
     if( elem.querySelectorAll ) {
         //-------------------------------------------------------------------//
         // Самое оптимальное, быстрое и современное, если доступно,
         // используем querySelectorAll.
         // Возвращаем найденные элементы нативным способом браузера.
         // возвращает все элементы внутри elem, удовлетворяющие CSS-селектору.
         //-------------------------------------------------------------------//
        
         // Наш код //
        
         return result;
     }
     else if( elem.getElementsByClassName ) {
         //-------------------------------------------------------------------//
         // Если браузер не поддерживает querySelectorAll, но
         // поддерживает getElementsByClassName.
         //-------------------------------------------------------------------//
        
         // Наш код //
        
         return result;
     }
     else {
         //-------------------------------------------------------------------//
         // Ручной поиск и выборка элементов.
         // Ни один выше метод не поддерживается, поэтому
         // используем метод getElementsByTagName.
         //-------------------------------------------------------------------//
        
         // Наш код //
        
         return result;
     }
};
  Пользователя нет на форуме
 
 Doctortehno
сообщение 27.4.2016, 20:20
Сообщение #226027


Master

Группа: Админ

Сообщений: 3023
Спасибо сказали: 400 раз

Цитата(Zmeiarm @ 27.4.2016, 12:47) *
:super_smilies_001: :super_smilies_001: Сава на форуме может пара человек поняли что ты написал.
куда там еще советовать...

Не многовато пару ты назвал? :a_aah:
Тут я думаю, сам спросил, сам ответил!!! :a_aal:
  Пользователя нет на форуме
 
 Zmeiarm
сообщение 27.4.2016, 20:30
Сообщение #226028


Любопытный

Группа: Супермодератор

Сообщений: 3331
Спасибо сказали: 1171 раз

Не.. с вопросом всё понятно. Но Саве не нужна стандартная библиотека. Хочет сам написать функцию под свои потребности.

А для этого нужен чел с каждодневным опытом работе в java. А мы тут каждый день пытаемся прокормить семью
  Пользователя нет на форуме
 
 Saveliy
сообщение 29.4.2016, 3:01
Сообщение #226105


Special

Группа: Главные администраторы

Сообщений: 12973
Спасибо сказали: 1772 раза

Реализация метода getElementsByClassName()

Наш новый метод под именем getElementsByClassNameEmulNative()

Сколько не рылся в сети, нигде не смог найти корректную реализацию метода getElementsByClassName().
Вариантов масса, но все они обладают одним недостатком, ни в одной реализации никто не предложил вариант поиска по нескольким классам одновременно. Поиск срабатывал только по одному указанному классу, либо по всем, например указано два класса, но будут найдены все элементы, которые содержат один из классов, то есть по принципу логического ИЛИ (OR) и отсутствие логического И (AND). Это ошибка. По спецификации на данный метод все наоборот. Нет логического ИЛИ, есть только И, то есть если указано два класса, то должны быть выбраны только те элементы, которые содержат эти два класса и никак иначе.

Много встречал вопросов людей, которые ищут реализацию метода getElementsByClassName() аналогично нативному (родному) методу. Для чего нужна такая реализация? Да просто не во всех браузерах есть данный метод, а использую данную реализацию, можно не переживать за тип используемого браузера.


Выкладываю решение, которое полностью соответствует нативному методу.
Конечно это не решение задачи, поставленной в оглавлении темы, это будет только эпизод из будущего решения. Ведь данный метод хоть и кроссбраузерный, но он в оригинальном исполнении на мой взгляд не обладает польностью нужной логической функциональностью, например отсутсвует поиск по логическому ИЛИ. Конечно я мог бы сюда добавить и логическое ИЛИ, и реализацию querySelectorAll(), но тогда бы мы ушли от спецификации по данному методу. Поэтому просто была создана функция, которая полностью реализует то, что было заложено по спецификации. А более удобное, универсальное и доработанное решение будет позже. Хотя честно сказать, в 95% случаев большинство кодеров используют поиск элементов всего по одному классу, но это как правило не в больших проектах. Ну раз народ искал данный вариант реализации, пожалуйста..


P.S. Встречал часто такие варианты реализаций от умных товарищей, которые всегда ведут поиск по всему документу, без возможности сузить круг поиска, утверждая, что по спецификации данный метод ищет только по всему документу. Но это огромная лишняя нагрузка и потеря времени. Если нам надо найти элемент с нужным классом только в определенном блоке, таблице, ну зачем нам проходить тысячи элементов, сканируя весь документ?? По спецификации getElementsByClassName() может быть вызвана для любого элемента, а не только для "document"!

Об этом не только сказано, как должно быть, но и реальные тесты подтверждают обратное.
https://dom.spec.whatwg.org/#dom-document-getelementsbyclassname

Примеры родной функции:
getElementsByClassName() ( описание метода и примеры )


Ну и обещанная реализация метода getElementsByClassName()..

CODE
/**
* getElementsByClassNameEmulNative()
*
* This method of real emulate getElementsByClassName().
*
* Функция, которая полностью реализует то, что заложено по
* спецификации getElementsByClassName().
*
* При указании нескольких классов их разделяют пробелом.
* Если указано несколько классов, будут найдены элементы по типу
* логического И (AND).
*
* (c) Saveliy Severniy
*/

getElementsByClassNameEmulNative = function(getClass,parent){
    var elem = parent || document;
    var result = [];
    
    if( !getClass ) return result;
    
    var list = elem.getElementsByTagName('*');    // Выбираем все дочерние узлы.
    var classArray = getClass.split(/\s+/);        // Разбиваем список классов
    var lenClasses = classArray.length;            // Сколько у нас искомых классов.
    var lenList = list.length;                    // Сколько узлов нашлось.
    
    var i,j,p;
    
    // Перебираем все дочерние узлы
    for(i = 0; i < lenList; i++) {
        p = 0;
        // Перебираем все классы
        for(j = 0; j < lenClasses; j++) {
            // В паттерне \b = это граница слова.
            if(list[i].className.search('\\b' + classArray[j] + '\\b') != -1) {
                 p++;
            }
        }
        
        // Нашлись все указанные классы в данном элементе?
        if( lenClasses == p ) {
            result.push(list[i]); // Добавляем найденный элемент.
        }
    }
    return result;
};
  Пользователя нет на форуме
 
 Saveliy
сообщение 29.4.2016, 22:33
Сообщение #226183


Special

Группа: Главные администраторы

Сообщений: 12973
Спасибо сказали: 1772 раза

Улучшенная реализация метода getElementsByClassName()

Наш новый метод под именем getElementsByClassNameExt()

В данном методе мы отошли от спецификации, а точнее улучшили и дополнили данный метод логическими возможностями.
Теперь можно искать элементы как логическому принципу И (AND), так и логическому ИЛИ (OR), а также и группировать поиск. Дополнительно решили вопрос с дублями найденных элементов, повторов найденных элементов не будет. Ни в одном подобном решении не были корректно решены все моменты.

Все протестировано, пользуйтесь. Найдете ошибки, пишите свои комментарии и советы.


CODE
/**
* Ищет значение в массиве.
*
* Если оба операнда являются объектами, то JavaScript сравнивает внутренние ссылки,
* которые равны в том случае, если они ссылаются на один и тот же объект в памяти.
*
* @param value
* @param array
* @returns {Boolean}
*/

inArrValue = function(value, array)
{
    var len;
    
    if( array ) {
        len = array.length;
        
        for(var i = 0; i < len; i++)
        {
            if(array[i] === value) return true;
        }
    }
    return false;
};


/**
* getElementsByClassNameExt();
*
* Имена классов пишем без точки.
*
* Варианты поиска классов:
*
* 1. Логическое ИЛИ (OR). Группы классов указываем через запятую.
* 2. Логическое И (AND). Классы указываем через пробел.
*
* Пример 1. Чтобы найти элемент, содержащий любой из двух классов
* (class1 или class2), нужно написать так: 'class1, class2'.
*
* Пример 2. Чтобы найти элемент, содержащий одновременно два
* класса (class1 и class2), нужно написать так: 'class1 class2'.
*
* Можно группировать поиск через запятую.
* Пример 3. Чтобы найти элементы, содержащие класс class1 или
* классы class2 и class3, нужно написать запрос так:
* 'class1, class2 class3'.
*
* Внутри группы повторно нельзя использовать запятую.
*
* (c) Saveliy Severniy
*/

getElementsByClassNameExt = function(getClass,parent){
    var elem = parent || document;
    var result = [];
    
    if( !getClass ) return result;
    
    // Получаем группы
    var groups = getClass.split(',');        // Разбиваем на группы классов.
    var lenGroups = groups.length;            // Сколько у нас будет групп.
    var elemAdded = [];                        // Избегаем дублей найденных элементов.
    
    //console.log('Нашлось групп: ' + lenGroups);
    
    // Заранее объявляем переменные перед циклом.
    var list = [], classArray = [], lenClasses, lenList;
    var g,i,j,p;
    
    // Чтобы не дергать каждый раз DOM, заранее один раз получим все элементы.
    list = elem.getElementsByTagName('*');    // Выбираем все дочерние узлы.
    lenList = list.length;                    // Сколько узлов нашлось.
    
    // Перебираем все группы. Логическое ИЛИ (OR).
    for(g = 0; g < lenGroups; g++) {
        classArray = groups[g].split(/\s+/);    // Разбиваем список классов
        lenClasses = classArray.length;            // Сколько у нас искомых классов.
        
        // Перебираем все дочерние узлы.
        for(i = 0; i < lenList; i++) {
            p = 0;
            // Перебираем все классы. Логическое И (AND).
            for(j = 0; j < lenClasses; j++) {
                // В паттерне \b = это граница слова.
                if(list[i].className.search('\\b' + classArray[j] + '\\b') != -1) {
                     p++;
                }
            }
            
            // Нашлись все указанные классы в данном элементе?
            if( lenClasses == p ) {
                if( !inArrValue(i,elemAdded) ) {
                    result.push(list[i]);    // Добавляем найденный элемент в массив результатов.
                    elemAdded.push(i);        // Помечаем найденные элементы.
                }
            }
        }
    }
    return result;
};
  Пользователя нет на форуме
 
 Saveliy
сообщение 7.5.2016, 21:33
Сообщение #226520


Special

Группа: Главные администраторы

Сообщений: 12973
Спасибо сказали: 1772 раза

В реализации метода getElementsByClassName() внесены улучшения.
Добавлена защита от дублей найденных элементов при логическом поиске в режиме ИЛИ (OR).

Новая измененная версия метода сообщением выше:
Реализация метода getElementsByClassName - Сообщение №226183
  Пользователя нет на форуме
 
Ответить
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
Режим отображения: ·


  Сейчас: 22.2.2020, 16:44