I have such an issue : I need to make smooth sub menu items fading with delay on mouseenter/leave, and i made a code
HTML + tailwind
<nav class="hidden shrink-0 xl:flex justify-center gap-x-8 items-center hover:[&>div>a]:opacity-100 [&>div>a]:opacity-50 [&>div>a]:transition-all">
<div>
Nav Hover
<div class="sub-nav absolute pt-12 pb-3 flex flex-col gap-y-2 [&>a]:text-gray-500 hover:[&>a]:text-black [&>a]:transition [&>a]:ease-in-out [&>a]:duration-300">
Item1
Item2
Item3
Item4
Item5
</div>
</div>
<div>Nav 2</div>
<div>Nav 3</div>
<div>Nav 4</div>
</nav>
JS
document.querySelector('#header nav div').addEventListener('mouseenter', function () {
if (this.querySelector('div') != null) {
this.querySelector('div').classList.remove('hidden')
let subNav = Array.from(this.querySelectorAll('div.sub-nav > a'))
console.log(subNav.length)
for (let i = 0; i < subNav.length; i++) {
console.log(subNav[i]);
setTimeout(function () {
subNav[i].style.opacity = 1
}, 100 * i);
}
}
})
document.querySelector('#header nav div').addEventListener('mouseleave', function () {
if (this.querySelector('div') != null) {
if (!this.querySelector('div').classList.contains('hidden')) {
let subNav = Array.from(this.querySelectorAll('div.sub-nav > a'))
console.log(subNav.length)
for (let i = subNav.length-1, d = 0; i >= 0, d < subNav.length; i--, d++) {
console.log(subNav[i]);
setTimeout(function () {
subNav[i].style.opacity = 0
if(i == 0){
this.querySelector('div').classList.add('hidden')
}
}, 100 * d);
}
}
}
})
it has two main problems :
If I enter area and then quickly leave it - the last items keeps visible and fadeOut animation breaks. I tried to check if opacity > 0 and then set it to 0^ but it doesnt work
This gives me error "Uncaught TypeError: this.querySelector is not a function"
if(i == 0){
this.querySelector('div').classList.add('hidden')
}
What the solution and best practices to the code? Thanks!
I tried opacity checking
Related
I'm using the scrollspy of Bootstrap-5.2 to update the displayed text whithin the scrollBox, when reaching the top or bottom part (pre-reload-item/post-reload-item).
The funktion for the bottom shiftContentDown works fine, but for shiftContentUp it works the first time only. After that the scrollspy doesn't activate the reload-top element anymore unless I scroll down to the post-reload-item (->reload-bottom activates -> shiftContentDown fires) and then up again.
I suspect it has something to do with the line document.getElementById('scrollBox').scrollBy(0, count * 24). Without it the new content of the pre-reload-item would be in fokus and the reload-top element stays active. With it the previous content of the pre-reload-item now part of the mid-reload-item stays visible and the reload-top element is no longer active. I'd expect it to activate again if I scroll up now, but for some reason it doesn't.
let nextIndex = 0;
let allContent = [];
let displayed = [0, 0]
let eof = false;
let pre = document.getElementById('pre-reload-item');
let mid = document.getElementById('mid-reload-item');
let post = document.getElementById('post-reload-item');
document.getElementById('scrollBox').addEventListener('activate.bs.scrollspy', () => {
let current = document.querySelector('#indexBox > a.active');
if (current === document.getElementById('reload-bottom')) {
shiftContentDown()
} else if (current === document.getElementById('reload-top')) {
shiftContentUp()
}
});
function shiftContentDown(count = 100, buffer = 0.5) {
let shift = allContent.length - displayed[1];
if (!eof && shift < count) {
getMoreContent(nextIndex);
} else {
let addition = allContent.slice(displayed[1], displayed[1] + count);
addition.forEach(l => {
if (pre.childElementCount >= (count * buffer)) {
pre.removeChild(pre.childNodes[0]);
displayed[0]++;
}
if (mid.childElementCount >= (count * (1 + 2*buffer)))
pre.appendChild(mid.childNodes[0]);
if (post.childElementCount >= (count * buffer) || post.childNodes.length > mid.childNodes.length)
mid.appendChild(post.childNodes[0]);
post.appendChild(l);
displayed[1]++;
});
}
}
function shiftContentUp(count = 100) {
let addition = allContent.slice(Math.max(displayed[0] - count, 0), displayed[0]);
addition.reverse().forEach(l => {
pre.insertBefore(l, pre.childNodes[0]);
displayed[0]--;
mid.insertBefore(pre.childNodes[pre.childElementCount - 1], mid.childNodes[0]);
post.insertBefore(mid.childNodes[mid.childElementCount - 1], post.childNodes[0]);
post.removeChild(post.childNodes[post.childElementCount - 1]);
displayed[1]--;
});
document.getElementById('scrollBox').scrollBy(0, count * 24);
}
function getMoreContent(index, chunkSize=100, last=1000) {
for (let i=0; i < chunkSize; i++) {
if (nextIndex < last) {
let newLineNode = document.createElement('div');
newLineNode.innerHTML = '----- ' + (nextIndex++) + ' -----';
allContent.push(newLineNode);
} else {
eof = true;
}
}
shiftContentDown();
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<div id="indexBox" class="list-group" style="display: none">
<a id="reload-bottom" class="list-group-item list-group-item-action" href="#post-reload-item">reload bottom</a>
<a id="reload-top" class="list-group-item list-group-item-action" href="#pre-reload-item">reload top</a>
</div>
<div id="scrollBox" data-bs-spy="scroll" data-bs-target="#indexBox" data-bs-smooth-scroll="true" data-bs-offset="0" class="scrollspy-example" tabindex="0" style="height: 300px; overflow-y: scroll">
<div id="pre-reload-item" class="text-bg-danger" style="white-space: pre-line"></div>
<div id="mid-reload-item" class="text-bg-info" style="white-space: pre-line"></div>
<div id="post-reload-item" class="text-bg-danger" style="white-space: pre-line"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
I'm working on a Drupal website and I'm working on revising a component. I would like the first accordion menu item open by default, I have been kind of stuck on this for a little while.
I can not add to html because content is dynamic so has to be done with JS. Here is the script so far.
Any help would be great.
Bellow is the JS and HTML
jQuery(document).ready(() => {
const buttons = document.querySelectorAll('[data-accordion-button]');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', (e) => {
const toggle = (e.target.getAttribute('aria-expanded') === 'true') ? false : true;
const root = buttons[i].closest('[data-accordion-container]');
const panel = buttons[i].closest('[data-accordion-panel]');
const panelSiblings = getSiblings(panel);
const firstChild = getFirstChild();
const window = panel.querySelector('[data-accordion-window]');
const content = panel.querySelector('[data-accordion-content]');
buttons[i].setAttribute('aria-expanded', toggle);
buttons[i].setAttribute('tabindex', '0');
toggle ?
window.setAttribute('style', `height: ${content.offsetHeight}px; visibility: visible;`) :
window.setAttribute('style', 'height: 0; visibility: hidden;');
if (root.getAttribute('data-accordion-config-single') === 'true') {
panelSiblings.forEach((sibling) => {
const siblingButton = sibling.querySelector('[data-accordion-button]');
const siblingWindow = sibling.querySelector('[data-accordion-window]');
siblingButton.setAttribute('aria-expanded', 'false');
siblingWindow.setAttribute('style', 'height: 0; visibility: hidden;');
});
}
});
// Arrow key controls
buttons[i].addEventListener('keydown', (e) => {
if (e.keyCode === 38) {
if (i === 0) {
buttons[buttons.length - 1].focus();
} else {
buttons[i - 1].focus();
}
}
if (e.keyCode === 40) {
if (i === buttons.length - 1) {
buttons[0].focus();
} else {
buttons[i + 1].focus();
}
}
});
}
function getSiblings(element) {
let siblings = [];
let sibling = element.parentNode.firstChild;
while (sibling) {
if (sibling.nodeType === 1 && sibling !== element) {
siblings.push(sibling);
}
sibling = sibling.nextSibling
}
return siblings;
};
});
```
<div class="accordion" data-accordion-container data-accordion-config-single="false">
<article class="accordion__panel-container" data-accordion-panel>
<h3 class="accordion__header">
<button class="h4 accordion__button" data-accordion-button aria-expanded="false" tabindex="0">
Taxation & Regulation in a Token Economy
</button>
</h3>
<div class="accordion__window" data-accordion-window>
<div class="accordion__content wysiwyg" data-accordion-content>
<p>
This text is for placement only. Vestibulum id ligula porta felis euismod semper.
</p>
</div>
</div>
</article>
<article class="accordion__panel-container" data-accordion-panel>
<h3 class="accordion__header">
<button class="h4 accordion__button" data-accordion-button aria-expanded="false">
Regulatory Content Aggregation
</button>
</h3>
<div class="accordion__window" data-accordion-window>
<div class="accordion__content wysiwyg" data-accordion-content>
<p>We demonstrate our commitment to Total Rewards through:</p>
</div>
</div>
</article>
</div>
I have some simple vanilla accordion and I'm not really sure why the CSS transition is not applying here? The divs have correct height, what is going on here?
(Cannot post because apparently my issue is mostly code, so this text is a dirty fix, sorry).
HTML (simplified version)
<div class="container">
<ul>
<li class="accordion-item">
<button class="accordion-item__title">
Title
</button>
<div class="accordion-item__body not-active">
Body
</div>
</li>
</ul>
</div>
JS
document.addEventListener('DOMContentLoaded', function () {
const accordions = document.querySelectorAll('.block-accordion');
if (typeof (accordions) !== 'undefined' && accordions != null) {
//loop thorugh all acordions
for (let a = 0; a < accordions.length; a++) {
const accordion = accordions[a];
const accordionItems = accordion.querySelectorAll('.accordion-item');
//loop through all accordiond's items
for (let i = 0; i < accordionItems.length; i++) {
const accordionItem = accordionItems[i];
//show first by default
accordionItems[0].querySelector('.accordion-item__body').classList.remove('not-active');
accordionItems[0].querySelector('.accordion-item__body').parentElement.classList.add('active');
accordionItems[0].querySelector('.accordion-item__title').setAttribute("aria-expanded", true);
//hide each accordion on click
const accordionItemTitle = accordionItem.firstElementChild;
accordionItemTitle.addEventListener('click', function toggleAccordion(e) {
const accordionContent = accordionItem.querySelector('.accordion-item__body');
accordionContent.style.height = "auto";
if (accordionContent.previousElementSibling === e.target) {
accordionContent.classList.toggle('not-active');
accordionContent.parentElement.classList.toggle('active');
if (accordionContent.classList.contains('not-active')) {
accordionContent.style.height = '0px';
accordionContent.previousElementSibling.setAttribute("aria-expanded", false);
} else {
accordionContent.style.height = accordionContent.clientHeight + 'px';
accordionContent.previousElementSibling.setAttribute("aria-expanded", true);
}
}
});
}
}
}
});
SASS
.block-accordion {
.active {
display: block;
}
.not-active {
display: none;
transition: height 0.35s ease-in-out;
overflow: hidden;
}
}
My slider is allowing for content to go forwards and backwards when the Next/Previous links are clicked. When I switch the contentType to 'div' it only shows content in slides 1 and 3. Could someone please explain why the counter is not incrementing properly? Is there a more effecient way to do this? I have included my code below as well as a working example. The purpose of this script is to allow for images or content to be displayed in a slide. Any help is much appreciated!
$(document).ready(function() {
// VARIABLE DECLARATIONS
var $el = $('#showcase');
var $leftArrow = $('#left_arrow');
var $rightArrow = $('#right_arrow');
var contentType = $('div'); // change to img and reverse comment out HTML code
var slideCount = $el.children().length;
var slideNum = 1;
var $load = $el.find(contentType)[0];
// PRELOADS SLIDE WITH CORRECT SETTINGS
$load.className = 'active';
$leftArrow.addClass("disabled");
// CHECKS FOR FIRST AND LAST INDEX
function checkSlide() {
if (slideNum == 1) {
$leftArrow.addClass('disabled');
} else {
$leftArrow.removeClass('disabled');
}
if (slideNum == slideCount) {
$rightArrow.addClass('disabled');
} else {
$rightArrow.removeClass('disabled');
}
}
// NAVIGATIONAL LOGIC FOR PREVIOUS/NEXT BUTTONS
$leftArrow.click(function() {
if (slideNum > 1) {
var counter = $(".active").index();
counter--;
$('.active').addClass('slide');
$('.active').removeClass('active');
contentType.eq(counter).addClass('active');
slideNum--;
checkSlide();
console.log('slideNum: ' + slideNum);
console.log('counter: ' + counter);
}
})
$rightArrow.click(function() {
if (slideNum < slideCount) {
var counter = $(".active").index();
counter++;
$('.active').addClass('slide');
$(".active").removeClass('active');
contentType.eq(counter).addClass('active');
slideNum++;
checkSlide();
console.log('slideNum: ' + slideNum);
console.log('counter: ' + counter);
}
})
});
img {
width: 160px;
}
a {
color: blue;
}
.disabled {
color: red !important;
}
.slide {
display: none;
}
.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- <div id="showcase">
<img class="slide" src="https://picsum.photos/458/354" />
<img class="slide" src="https://picsum.photos/458/354/?image=306" />
<img class="slide" src="https://picsum.photos/458/354/?image=626" />
</div>
« Previous
Next » -->
<div id="showcase">
<div class="slide">Page 1 content</div>
<div class="slide">Page 2 content</div>
<div class="slide">Page 3 content</div>
</div>
« Previous
Next »
The issue is that your "contentType" var, which matches divs, also matches the "showcase" div, so you are getting 4 in your list, not 3 as you expect. In your left/right arrow click handlers, replace:
contentType.eq(counter).addClass('active');
with:
$el.find(contentType).eq(counter).addClass('active');
I have horizontal parallax site, it works fine, but when I add changing background-color on scroll like that:
var tennis = $('.tennis');
var tennisDivs = {};
var tennisColors = {};
$.each(tennis, function (index, value) {
var color = $(value).data('color');
var name = $(value).data('name');
var left = $(this).offset().left;
if ($(value).data('name'))
tennisDivs[name] = left;
tennisColors[left] = color;
});
var scrollArea = $('#tennis-container .slides');
var tempPosition = 'Info';
scrollArea.scroll(function () {
var position = Math.floor($('#tennis-container .slides').scrollLeft());
$.each(tennisDivs, function (index, value) {
if ((value - 2300) <= position) {
currIndex = index;
}
});
if (tempPosition != currIndex) {
tempPosition = currIndex;
$('.slide__bg').attr('class','slide__bg').addClass('color-' + tempPosition);
}
});
CSS:
.slide__bg {
transition: background-color 0.8s linear 0.3s;
}
.color-info, .color-stars,.basic-color {
background-color: #bdc1c6;
}
.color-courts {
background-color: #75695f;
}
html:
<div id="tennis-container">
<div class="slides">
<div class="slide tennis-info info" id="tennis-info" data-name="Info" data-color="yellow">
<div class="slide__bg"></div>
<div class="slide__content"></div>
</div>
<div class="slide ...." id="..." data-name="..." data-color="...">
<div class="slide__bg"></div>
<div class="slide__content"></div>
</div>
<div class="slide ...." id="..." data-name="..." data-color="...">
<div class="slide__bg"></div>
<div class="slide__content"></div>
</div>
</div>
</div>
The problem is, when the color changes on scroll, the site starts freezing and scrolling stops for couple seconds, as seen here:
Also I try changing background-color using jQuery animate(), but nothing changes.
Fixes like:
Avoid resizing big images;
Remove background-size CSS property;
Don't work for me...
It is all about performance issues. I have done some optimizations for the part of your code. Try this one:
var scrollArea = $('#tennis-container .slides');
var tempPosition = 'Info';
var slideBg = $('.slide__bg');
scrollArea.scroll(function () {
var position = Math.floor(scrollArea.scrollLeft());
$.each(tennisDivs, function (index, value) {
if ((value - 2300) <= position) {
currIndex = index;
}
});
slideBg.attr('class','slide__bg').addClass('color-' + tempPosition);
});