Для одного из проектов нужно было сделать аккордеон, решил показать в блоге, как его делать. Делаем аккордеон в блоке FAQ, поэтому там будут классы и стили со словом faq.
В вёрстке блок выглядит вот так. При клике должен показываться текст и иконка меняется с плюса на минус.
Логика
При клике на заголовок элемента, мы ищем его родителя и добавляем ему класс is-active
. В то же время удаляем такой класс у остальных элементов. Изначально контект и одна иконка скрыта. В стилях для блока с активным класом меняем отображения внутренних элементов.
Разметка
Сначала нам нужна разметка. В списке всего один элемент. Чтобы не
<div class="faqs-list">
<div class="faq faq-item">
<div class="faq-header">
<div class="faq-icon">
<img class="faq-icon-minus" src="./img/svg/faq-icon-minus.svg" alt="">
<img class="faq-icon-plus" src="./img/svg/faq-icon-plus.svg" alt="">
</div>
<div class="faq-title">Заголовок элемента</div>
</div>
<div class="faq-text">Текст элемента</div>
</div>
</div>
В разметке мы должны дать дополнительный класс с приставкой .js
, чтобы обращаться к нему из jQuery/Javascript. Дополнительный классы нужен, чтобы другой разработчик его не трогал. Чтобы понимал, что он не для стилей, а для интерактивности.
Нам нужно будет добавить 2 класса:
- для шапки —
js-faq-header
, - для элемента —
js-faq
Проставил классы в той же разметке. Выделил их жирным.
<div class="faqs-list">
<div class="faq faq-item js-faq">
<div class="faq-header js-faq-trigger">
<div class="faq-icon">
<img class="faq-icon-minus" src="./img/svg/faq-icon-minus.svg" alt="">
<img class="faq-icon-plus" src="./img/svg/faq-icon-plus.svg" alt="">
</div>
<div class="faq-title">Заголовок элемента</div>
</div>
<div class="faq-text">Текст элемента</div>
</div>
</div>
Стили
/* Стили по-умолчанию*/
.faq-icon-minus {
display: none;
}
.faq-text {
display: none;
}
/* Показываем текст */
.faq.is-active .faq-text {
display: block;
}
/* Меняем иконку */
.faq.is-active .faq-icon-minus {
display: block;
}
.faq.is-active .faq-icon-plus {
display: none;
}
Логика
При клике на faq-header
, мы ищем его родителя faq
и добавляем ему класс is-active
. А у всех остальных удаляем.
JavaScript
// Перебираем каждый элемент шапки
document.querySelectorAll('.js-faq-trigger').forEach(function(trigger) {
// Получаем родителя, элемент аккордеона
var parent = trigger.closest('.js-faq');
// клик по шапке
trigger.addEventListener('click', function(e) {
// если при клике у него уже есть активный класс
if (parent.classList.contains('is-active')) {
// то мы его удаляем
parent.classList.remove('is-active');
}
// если при клике мы не нашли у элемента активный класс
else {
// удаляем у всех элементов активный класс
document.querySelectorAll('.js-faq').forEach(function(item) {
item.classList.remove('is-active');
});
// добавляем класс тому элементу, по которому кликнули
parent.classList.add('is-active');
}
})
});
jQuery
// клик по шапке
$('.js-faq-trigger').on('click',function() {
// Получаем родителя, элемент аккордеона
var parent = $(this).closest('.js-faq');
// если при клике мы не нашли у элемента активный класс
if (parent.hasClass('is-active')) {
// то мы его удаляем
parent.removeClass('is-active');
}
else {
// удаляем у всех элементов активный класс
$('.js-faq').removeClass('is-active');
// добавляем класс тому элементу, по которому кликнули
parent.addClass('is-active');
}
})
Демо работы
Канал о фрилансе
Там я делюсь фишками о фрилансе: деньги, клиенты, автоматизация, фриланс. Раньше это был закрытый материал с платным доступом. Сейчас он открыт. Иногда подписчикам будет открываться платные материалы.
Подписаться
12 комментария
Огромное спасибо, все получилось!
Рад стараться. Может быть у вас есть на примете библиотеки, о которых вы бы хотели узнать подробнее?
Прямо таки спасибище)
🙂 Рад стараться!
Tweenmax, PIXI … вот как тут, например, искажение сделано — https://www.kommigraphics.com/en/
https://lincorwatches.com/ — или вот еще пример интересной анимации… было бы интересно посмотреть разбор примера на основе их скрипта
ничего не понял ,много специфики, а очень жаль!
Можете рассказать о трудностях, которые возникли? Могу ответить на ваши вопросы
Может вы подскажете в чем дело?
Правлю чужой код. Вот такой. А мне в консоли выдается, что ошибка Uncaught SyntaxError: missing ) after argument list.
<script>
(document).ready(function(){
var bLazy = new Blazy({
loadInvisible: true, // загрузка скрытых изображений
container: ‘.slick-track, b-lazy’ // загрузка скрытых изображений слайдера
});
bLazy.revalidate();
}
</script>
Нужно закрыть круглую скобку в конце функции ready().
Что то у меня нифига не получается, уже все перепробовал.
Есть код с изображениями:
<a class=»product-image» href=»$ENTRY_URL$»>
<img class=»b-lazy» alt=»$NAME$» src=»/images/blank-1-.gif» data-src-mobile=»<?if($SMALL$)?>$SMALL$<?else?>/img404/big.svg<?endif?>» data-src=»<?if($THUMB$)?>$THUMB$<?else?>/img404/big.svg<?endif?>» class=»gphoto» id=»$BLOCK_PREF$-gphoto-$ID$» title=»$NAME$»>
</a>
И пример вашего кода, который я подставляю:
<script>
var bLazy = new Blazy({
breakpoints: [{
width: 480, // Max width
src: ‘data-src-mobile’ // Name atribut
}]
});
</script>
Как он работает не понимаю, растолкуйте. По идеи должно быть если оригинал фото товара размерами больше 480х480, тогда формируется уменьшенное фото и оператор $SMALL$ выводит ссылку на уменьшенное фото.
Размер превью оператора $SMALL$, 80*80
и при этом еще постоянно ошибка в консоли:
Uncaught ReferenceError: Blazy is not defined
Если не трудно можете посмотреть в консоли:
https://www.aksshop.ru/shop/shtatnye-magnitoly-unison/magnitoly-unison
Здравствуйте. Подскажите, вот мой код, который я подключаю отдельным файлом
/*!
hey, [be]Lazy.js — v1.8.2 — 2016.10.25
A fast, small and dependency free lazy load script (https://github.com/dinbror/blazy)
(c) Bjoern Klinggaard — @bklinggaard — http://dinbror.dk/blazy
*/
(function(q,m){«function»===typeof define&&define.amd?define(m):»object»===typeof exports?module.exports=m():q.Blazy=m()})(this,function(){function q(b){var c=b._util;c.elements=E(b.options);c.count=c.elements.length;c.destroyed&&(c.destroyed=!1,b.options.container&&l(b.options.container,function(a){n(a,»scroll»,c.validateT)}),n(window,»resize»,c.saveViewportOffsetT),n(window,»resize»,c.validateT),n(window,»scroll»,c.validateT));m(b)}function m(b){for(var c=b._util,a=0;a=c.left&&b.bottom>=c.top&&b.left<=c.right&&b.top<=c.bottom}function z(b,c,a){if(!t(b,a.successClass)&&(c||a.loadInvisible||0<b.offsetWidth&&0<b.offsetHeight))if(c=b.getAttribute(u)||b.getAttribute(a.src)){c=c.split(a.separator);var d=c[A&&1<c.length?1:0],e=b.getAttribute(a.srcset),g="img"===b.nodeName.toLowerCase(),p=(c=b.parentNode)&&"picture"===c.nodeName.toLowerCase();if(g||void 0===b.src){var h=new Image,w=function(){a.error&&a.error(b,"invalid");v(b,a.errorClass);k(h,"error",w);k(h,"load",f)},f=function(){g?p||B(b,d,e):b.style.backgroundImage='url("'+d+'")';x(b,a);k(h,"load",f);k(h,"error",w)};p&&(h=b,l(c.getElementsByTagName("source"),function(b){var c=a.srcset,e=b.getAttribute(c);e&&(b.setAttribute("srcset",e),b.removeAttribute(c))}));n(h,"error",w);n(h,"load",f);B(h,d,e)}else b.src=d,x(b,a)}else"video"===b.nodeName.toLowerCase()?(l(b.getElementsByTagName("source"),function(b){var c=a.src,e=b.getAttribute(c);e&&(b.setAttribute("src",e),b.removeAttribute(c))}),b.load(),x(b,a)):(a.error&&a.error(b,"missing"),v(b,a.errorClass))}function x(b,c){v(b,c.successClass);c.success&&c.success(b);b.removeAttribute(c.src);b.removeAttribute(c.srcset);l(c.breakpoints,function(a){b.removeAttribute(a.src)})}function B(b,c,a){a&&b.setAttribute("srcset",a);b.src=c}function t(b,c){return-1!==(" "+b.className+" ").indexOf(" "+c+" ")}function v(b,c){t(b,c)||(b.className+=" "+c)}function E(b){var c=[];b=b.root.querySelectorAll(b.selector);for(var a=b.length;a—;c.unshift(b[a]));return c}function C(b){f.bottom=(window.innerHeight||document.documentElement.clientHeight)+b;f.right=(window.innerWidth||document.documentElement.clientWidth)+b}function n(b,c,a){b.attachEvent?b.attachEvent&&b.attachEvent("on"+c,a):b.addEventListener(c,a,{capture:!1,passive:!0})}function k(b,c,a){b.detachEvent?b.detachEvent&&b.detachEvent("on"+c,a):b.removeEventListener(c,a,{capture:!1,passive:!0})}function l(b,c){if(b&&c)for(var a=b.length,d=0;d<a&&!1!==c(b[d],d);d++);}function D(b,c,a){var d=0;return function(){var e=+new Date;e-d<c||(d=e,b.apply(a,arguments))}}var u,f,A,y;return function(b){if(!document.querySelectorAll){var c=document.createStyleSheet();document.querySelectorAll=function(a,b,d,h,f){f=document.all;b=[];a=a.replace(/\[for\b/gi,"[htmlFor").split(",");for(d=a.length;d—;){c.addRule(a[d],"k:v");for(h=f.length;h—;)f[h].currentStyle.k&&b.push(f[h]);c.removeRule(0)}return b}}var a=this,d=a._util={};d.elements=[];d.destroyed=!0;a.options=b||{};a.options.error=a.options.error||!1;a.options.offset=a.options.offset||100;a.options.root=a.options.root||document;a.options.success=a.options.success||!1;a.options.selector=a.options.selector||".b-lazy";a.options.separator=a.options.separator||"|";a.options.containerClass=a.options.container;a.options.container=a.options.containerClass?document.querySelectorAll(a.options.containerClass):!1;a.options.errorClass=a.options.errorClass||"b-error";a.options.breakpoints=a.options.breakpoints||!1;a.options.loadInvisible=a.options.loadInvisible||!1;a.options.successClass=a.options.successClass||"b-loaded";a.options.validateDelay=a.options.validateDelay||25;a.options.saveViewportOffsetDelay=a.options.saveViewportOffsetDelay||50;a.options.srcset=a.options.srcset||"data-srcset";a.options.src=u=a.options.src||"data-src";y=Element.prototype.closest;A=1=window.screen.width)return u=a.src,!1});setTimeout(function(){q(a)})}});
document.addEventListener(«DOMContentLoaded», function(event) {
var bLazy = new Blazy({
offset: 50,
});
});
Вот кусок html, где находится изображение
картинка-прелоадер загружается, а основная — нет.
Ошибок в консоли не выдает. Файл со скриптом загружается. Такое ощущение, что скрипт не отрабатывает. Подскажите, в чем может быть проблема