как сократить названия классов с общим началом в массиве
Привет! К JS только-только подступился, но кое-что уже знаю. Проблема такая. Есть кусочек кода:
<div class="page bg1"> <div class="radio-controls"> <label class="label" for="radio1"></label> <label class="label" for="radio2"></label> <label class="label" for="radio3"></label> </div> </div> и к нему CSS: .page { position: relative; width: 400px; height: 250px; outline: 1px solid orange; } .bg1 { background-image: url("Gora-400x250.jpg"); } .bg2 { background-image: url("IMG_0863m.jpg"); } .bg3 { background-image: url("Torosskie-goryi-400x250.jpg"); } .radio-controls { position: absolute; bottom: 0; left: 50%; margin-left: -55px; width: 110px; height: 50px; text-align: center; line-height: 65px; outline: 1px solid red; } label { display: inline-block; width: 20px; height: 20px; background-color: dodgerblue; background-clip: padding-box; border: 5px solid transparent; border-radius: 50%; box-shadow: 0 0 2px 1px dodgerblue; } Нужно сделать так, чтобы при клике по любому label у div.page удалялся бы любой класс, имя которого начинается с bg-, если вдруг он обнаружился. Всё, что удалось мне придумать, это длинные условия, вроде: if(page[0].classList.contains("bg1") || page[0].classList.contains("bg2"))и т.д... А можно ли как-то укоротить текст условия? Ну, например, как в CSS есть селекторы по атрибуту [class^='bg-'], есть ли что-то подобное в синтаксисе JS? |
Alessio18911,
если в у div.page всего 2 класса "[class*='bg']" |
Пожалуйста, отформатируйте свой код!
Для этого его можно заключить в специальные теги: js/css/html и т.п., например: [js] ... ваш код... [/js] О том, как вставить в сообщение исполняемый javascript и html-код, а также о дополнительных возможностях форматирования - читайте http://javascript.ru/formatting. |
var page = document.querySelector('.page'); page.onclick = function(e) { if(e.target.tagName === 'label') { for(var i = 0; i < this.classList.length; i++) { if(this.classList[i].indexOf('bg') === 0) { this.classList.remove(this.classList[i]); break; } } } }; или var page = document.querySelector('.page'); page.onclick = function(e) { if(e.target.tagName === 'label') { this.className = this.className.replace(/bg\d+/, ''); } }; |
Цитата:
|
Цитата:
|
Цитата:
|
:write: зачем проверка нужна ??? className = "page"
|
Цитата:
|
Цитата:
|
Точно! Как это я не подумал... Хорошо, вот мой код. Он рабочий - в т.ч. я учёл Ваше замечание. Но думаю, что его можно сделать короче и красивее. Помогите, пожалуйста!
<body> <input type="radio" checked name="radio" id="radio1"> <input type="radio" name="radio" id="radio2"> <input type="radio" name="radio" id="radio3"> <div class="page bg1"> <div class="radio-controls"> <label for="radio1"></label> <label for="radio2"></label> <label for="radio3"></label> </div> </div> <script> [js]var page = document.getElementsByClassName("page"); var label = document.getElementsByTagName("label"); label[0].addEventListener('click', function() { page[0].classList.remove("bg2"); page[0].classList.remove("bg3"); page[0].classList.add("bg1"); }); label[1].addEventListener('click', function() { page[0].classList.remove("bg3"); page[0].classList.add("bg2"); }); label[2].addEventListener('click', function() { page[0].classList.remove("bg2"); page[0].classList.add("bg3"); });[/js] </script> </body> body { background-color: white; font-family: "Open Sans", sans-serif; padding: 5px 25px; font-size: 18px; margin: 0; color: #444; } h1 { font-family: "Merriweather", serif; font-size: 32px; } input { display: none; } .page { position: relative; width: 400px; height: 250px; outline: 1px solid orange; } .bg1 { background-image: url("Gora-400x250.jpg"); } .bg2 { background-image: url("IMG_0863m.jpg"); } .bg3 { background-image: url("Torosskie-goryi-400x250.jpg"); } .radio-controls { position: absolute; bottom: 0; left: 50%; margin-left: -55px; width: 110px; height: 50px; text-align: center; line-height: 65px; outline: 1px solid red; } label { display: inline-block; width: 20px; height: 20px; background-color: dodgerblue; background-clip: padding-box; border: 5px solid transparent; border-radius: 50%; box-shadow: 0 0 2px 1px dodgerblue; } #radio1:checked ~ .page label[for="radio1"], #radio2:checked ~ .page label[for="radio2"], #radio3:checked ~ .page label[for="radio3"] { background-color: red; box-shadow: 0 0 2px 1px red; } |
Alessio18911,
:-? <!DOCTYPE html> <html> <head> <title>Untitled</title> <meta charset="utf-8"> <style type="text/css"> .page { position: relative; width: 400px; height: 250px; outline: 1px solid orange; background-size: cover; background-repeat: no-repeat; } .bg1 { background-image: url("http://hd4desktop.online/images/m/a-squirrel-408002.jpg"); } .bg2 { background-image: url("http://www.nicefotos.ru/img/picture/May/30/f5cc5821bbc50c0920bc86fa65d9cb4c/mini_1.jpg"); } .bg3 { background-image: url("http://cybercomnet.fr/www/wallpaper/_data/i/galleries/Animaux/Lapin/Lapin-8-cu_s9999x200.jpg"); } .radio-controls { position: absolute; bottom: 0; left: 50%; margin-left: -55px; width: 110px; height: 50px; text-align: center; line-height: 65px; outline: 1px solid red; } label { display: inline-block; width: 20px; height: 20px; background-color: dodgerblue; background-clip: padding-box; border: 5px solid transparent; border-radius: 50%; box-shadow: 0 0 2px 1px dodgerblue; } </style> <script> window.addEventListener('DOMContentLoaded', function() { var page = document.querySelector('.page'), label = page.querySelectorAll('label'); [].forEach.call(label, function(item, i) { item.addEventListener('click', function() { page.className = "page bg"+ (i+1) }); }); }); </script> </head> <body> <div class="page bg1"> <div class="radio-controls"> <label class="label" for="radio1"></label> <label class="label" for="radio2"></label> <label class="label" for="radio3"></label> </div> </div> </body> </html> |
Очень красиво получилось, спасибо!:) А Вы могли бы мне пояснить некоторые детали:
1. Для чего нам DOMContentLoaded? 2. В строке 58 - почему в скобках именно .page label, а не просто label? 3. В строке 59 - что значат квадратные скобки в самом начале? 4. Как вообще работает эта функция [].forEach.call.....? 5. Что подключено самым первым скриптом? |
Цитата:
Это не обязательно если перенести скрипт в конец документа перед </Body> 2) можно и просто label но тогда это будет относиться ко всем label а не только с классом page. 3) https://developer.mozilla.org/en-US/...Each?v=example квадратные скобки пустой литерал массива 4) https://developer.mozilla.org/en-US/...Each?v=example 5. походу был jquery который рони грохнул. |
Ага. Теперь всё ясно. Очень благодарен всем! :)
|
Alessio18911,
5. уже ничего 1. чтобы скрипт работал из любого места страницы, а не только после слайда. 2. исправил. 3.4. https://learn.javascript.ru/traversi...массивы в основном чтобы сохранить i (усовершенствованный for) |
Рони, спасибо большое за разъяснение и помощь!
|
Рони, хочу уточнить такую вещь ещё: методы querySelector - возвращает одно значение, querySelectorAll - массив. В то же время методы, которые начинаются с getElementsBy - только массивы, правильно? А всё же, что лучше использовать для поиска элементов - querySelector/querySelectorAll или getElementsBy...?
|
Alessio18911,
псевдомассивы!!! а getElementsBy живые коллекции (значение будет менятся, всегда актуально) , querySelectorAll - срез на момент поиска (коллекция неизменна) |
Alessio18911,
особого значения не имеет как искать, если найденное тут же используется и структура найденного неизменна. (ничего не удалили или не добавили), если меняется то предпочтительнее querySelectorAll |
А почему? И ещё, forEach.сall - для меня новенькое. Я хотел бы узнать, чем отличаются
[].forEach.call(label, function(item, i)){...}и label.forEach(function(item, i)) {...}. У меня обе работают одинаково ) А также я попробовал - для аналогии записать эту функцию через "старенькое" - цикл for. Но что-то не получается, подскажите, пожалуйста, что я сделал не так: for (var i = 0; i < label.length; i++) { label[i].addEventListener('click', function() { page.className = "page bg" + (i+1); }); } |
Цитата:
Цитата:
нужно замыкание i как в forEach Пример ошибочного использования |
пример на понимание отличия getElementsBy от querySelectorAll
Alessio18911,
удаление тегов LI <!DOCTYPE html> <html> <head> <title>Untitled</title> <meta charset="utf-8"> <style type="text/css"> </style> </head> <body> <ul> <li>01</li> <li>02</li> <li>03</li> <li>04</li> <li>05</li> <li>06</li> <li>07</li> <li>08</li> <li>09</li> </ul> <script> var ul = document.getElementsByTagName("ul")[0], li = ul.getElementsByTagName("li"); for (; li[0] && ul.removeChild(li[0]); ); //удаляется только первый /* var ul = document.querySelector("ul"), li = ul.querySelectorAll("li"); for (var i=0, item; item = li[i]; i++) {ul.removeChild(item)} //удаляются все по очереди */ </script> </body> </html> |
Т.е. через forEach.call - это более старый способ?
Насчёт последнего - спасибо, даже не подозревал о таком.. |
Цитата:
https://developer.mozilla.org/ru/docs/Web/API/NodeList поддержка нового, по ссылке внизу (в ie нету) https://developer.mozilla.org/en-US/...deList/forEach |
Рони, и ещё: читаю тот пример ошибочного использования. Уже сломал голову - что значит строка:
divs[i].onclick = function(x) { return function() { alert(x) } }(i) Именно это i в конце! Каким боком оно к какой функции? Я только, конечно, начинаю влазить в JS, но не видел пока, чтобы то, что функция должна взять, стояло ЗА закрывающей скобкой... |
Alessio18911, создаётся функция и тут же вызывается, в неё передаётся "i" в качестве параметра "x". Внутри возвращается ещё одна функция, которая и будет в onclick, но у неё ещё будет доступен параметр "x" из замыкания первой функции.
|
Alessio18911,
функция создаётся и тут же запускается с аргументом i и возвращает другую функцию, в которой это i сохраняется. может, кто поможет, скажет как-то иначе ... |
Рони, Ruslan_xDD, спасибо! С этим (i) разобрался - оказывается, так можно вызывать функцию. Но почитал на learn.javascript про замыкание, про порядок обработки кода и инициализации. Примеры же там, конечно, даются простенькие совсем, например, когда переменная инициализируется ДО выполнения какой-то функции и потом ей же присваивается другое значение. Тогда всё понятно - значение берётся текущее. Но ведь также сказано, что на этапе запуска происходит поиск и подготовка всех Function Declaration и всех переменных, которые становятся undefined. А вот потом, уже когда код выполняется, идёт присваивание:
var phrase = 'Привет'; function sayHi(name) { alert(phrase + ', ' + name); } sayHi('Вася'); // Привет, Вася (*) phrase = 'Пока'; sayHi('Вася'); // Пока, Вася (**) Здесь всё предельно ясно: перед запуском подготовлена sayHi(name); и 2 переменные: name и phrase, которые undefined. Когда функция запущена, происходит присвоение name = 'Вася'; а дальше ищется phrase, которая, находясь раньше функции, получает значение 'Привет'. Соответственно, когда функция вызвана второй раз, то "ближайшей" к ней phrase будет уже вторая, с 'Пока' поэтому результат её изменится. Дальше возвращаемся к "ошибочному примеру". Там функция такова: function addEvents(divs) { for(var i=0; i<divs.length; i++) { divs[i].innerHTML = i divs[i].onclick = function() { alert(i) } } } Т.е. присвоение i значений же происходит последовательно, по мере того, как крутится цикл! Почему же тогда говорится, что "обработчик onclick активизируется, когда цикл закончен и i == 10;"?? Ведь divs[i].onclick = function() { alert(i) }расположен ВНУТРИ этого цикла, а не ЗА его пределами ниже! Почему тогда он берёт не текущую i, а финальную сразу? Или это свойство обработчика - активизироваться только после того, как отработает цикл, несмотря на то, что сам он находится внутри? |
Цитата:
Цитата:
|
Так а почему финальное? Ведь цикл выполняется последовательно, а это событие onclick лежит ВНУТРИ него!:-?
|
Alessio18911,
одна переменная на все клики, почему переменная должна показывать, не то что есть на данный момент?нужно сохранить значение на момент создания функции, тогда нужно замыкание. |
Простите, возможно, Вам кажется очень простым этот вопрос, но для меня непонятно. Да, я понял, что на момент создания функции переменная должна показывать то, что в ней есть. Да, она одна на все клики, согласен. Но ведь onclick в цикле! Т.е. - возьмём мой ошибочный пример:
for (var i = 0; i < label.length; i++) { label[i].addEventListener('click', function() { page.className = "page bg" + (i+1); }); } Получается, что - с учётом порядка действий при обработке кода - сначала выполнится цикл - т.е. i получит своё финальное значение. Затем будет выполняться функция add.EventListener и, соответственно, (i+1) получит сразу значение финального i + 1, т.е. 4. Так? Т.е. label[i], поскольку это массив, i будут присваиваться последовательно, по мере работы цикла, а функции - только последнее, т.к. это функция и она вложена в цикл? |
Alessio18911,
возможно кто-то пояснит лучше ... |
Не обижайтесь, дело во мне.
|
Alessio18911,
Попробую объяснить. Ваш пример for (var i = 0; i < label.length; i++) { label[i].addEventListener('click', function() { page.className = "page bg" + (i+1); }); } Здесь цикл по i - это создание обработчиков события click для всех элементов массива label. строка page.className = "page bg" + (i+1); при этом не исполняется - просто запоминается в конкретном обработчике (причем запоминается идентификатор i, а не его значение). По завершении цикла создания обработчиков i принимает финальное значение - и больше не меняется. При исполнении по событию click обработчика для любого элемента из списка label выполнится вышеуказанная строка, в которой будет использоваться это значение Иллюстрация: Вариант с использованием значения i при создании обработчика <div style="border:1px solid black">1</div> <div style="border:1px solid black">2</div> <div style="border:1px solid black">3</div> <div style="border:1px solid black">4</div> <script> var divs = document.querySelectorAll('div'); for(var i = 0; i < divs.length; i++) { divs[i].onclick = function(x) { return function() { alert(x) } }(i); } </script> Вариант с использованием идентификатора i при создании обработчика <div style="border:1px solid black">1</div> <div style="border:1px solid black">2</div> <div style="border:1px solid black">3</div> <div style="border:1px solid black">4</div> <script> var divs = document.querySelectorAll('div'); for(var i = 0; i < divs.length; i++) { divs[i].onclick = function() { alert(i) }; } </script> |
Здравствуйте, Dilettante_Pro! Спасибо, что не прошли мимо. :) Перескажу, что я понял, своими словами.
Итак, в моём неправильном примере функция, которая вложена в addEventListener не может быть выполнена в процессе выполнения цикла, т.к. в коде нигде не прописан запуск её программным кодом, зато написано, что только человек - клацнув мышью - это сделает. Поэтому цикл выполняется и результат его работы - у каждого элемента массива привешен ОДИН И ТОТ же обработчик с выражением page.className = "page bg" + (i+1); При этом это выражение не высчитывалось, поскольку функция была "мёртвой". Цикл закончился, i дошло по финала, стало 2 (у меня 3 элемента в массиве) и "застыло навеки". Когда человек клацнул на какой-либо элемент массива, только это "оживило" функцию и она стала искать i, найдя его только ЗА своими пределами, т.е. у цикла, который, однако, уже отработал. Теперь, независимо от того, какой элемент массива будет кликнут, одно и то же i приведёт к вычислению одного и того же результата, поэтому чуда не произойдёт. Берём правильный вариант. Для того, чтобы он был правильный, нужно заставить функцию работать при каждой итерации цикла. Для этого остаётся "оболочка" addEventListener, которая так и будет ждать клика от человека, а вот внутри неё - "живая" функция, которую принудительно будет оживлять вызов с (i) после её же закрытой фигурной скобки. В результате каждой итерации цикла выражение page.className = "page bg" + (i+1); будет высчитываться, и по окончании работы цикла у каждого элемента массива будет не просто обработчик с формулой, а "индивидуализированный" с готовым значением page.className. И клац мышью по любому элементу приведёт просто уже к присвоению соответствующего класса элементу. Правильно понял?:-? |
Добрый день, Rise! Только сегодня расплавившийся мозг вдруг выдал какую-то туманную идею, что, наверное, дело в том, как выполняется цикл с обработчиком. К обеду он приобрёл более-менее ясные очертания, а Ваше сообщение окончательно подтвердило мои раздумья. Возможно, я ещё не дошёл до тех глав учебника, где говорится про возможность НЕсинхронного выполнения цикла.. Но в самом начале - всё время идут примеры с последовательным выполнением строк за одну итерацию. И в главе про функции не говорится, что некоторые функции имеют такие особенности. Вот это незнание возможности асинхронности цикла и привело к загвоздке, которая разрешилась в результате совместных усилий. За что и благодарю и Вас, что уделили мне время. :)
|
Rise, прошу прощения, КОД :) - знАчимое уточнение )
|
Alessio18911,
Цитата:
|
Часовой пояс GMT +3, время: 23:46. |