I recently learned about web development, and I want to build my own portfolio website using Github Pages. For the 'Portfolio' section, I want to use a slider using Javascript. Then, when I copy paste the code and change the title to 'Blog' section, this new section didn't apply the slider that seen in Portfolio.
I already seen multiple online sources to see where is my problem, and it shows that the getElementById only can be used one time. I try to modify the code to use querySelectorAll but it's still doesn't work.
This is the JS code that I used:
const sliderContainer = document.getElementById('sliderContainer');
const slider = document.getElementById('slider');
const cards = slider.getElementsByTagName('li');
var elementsToShow = 3;
if (document.body.clientWidth < 1000) {
elementsToShow = 1;
} else if (document.body.clientWidth < 1500) {
elementsToShow = 2;
}
var sliderContainerWidth = sliderContainer.clientWidth;
var cardWidth = sliderContainerWidth / elementsToShow;
slider.style.width = cards.length * cardWidth + 'px';
slider.style.transition = 'margin';
slider.style.transitionDuration = '1s';
for (var index = 0; index < cards.length; index++) {
const element = cards[index];
element.style.width = cardWidth + 'px';
}
function next() {
if (+slider.style.marginLeft.slice(0, -2) != -cardWidth * (cards.length - elementsToShow)) slider.style.marginLeft = +slider.style.marginLeft.slice(0, -2) - cardWidth + 'px';
}
function prev() {
if (+slider.style.marginLeft.slice(0, -2) != 0) slider.style.marginLeft = +slider.style.marginLeft.slice(0, -2) + cardWidth + 'px';
}
function autoPlay() {
prev();
if (+slider.style.marginLeft.slice(0, -2) === -cardWidth * (cards.length - elementsToShow)) {
slider.style.marginLeft = '0px';
}
setTimeout(() => {
autoPlay();
}, 3000);
}
and this is my HTML code. I use TailwindCSS for the CSS framework.
<div class="flex">
<div class="flex items-center">
<div class="w-full text-right">
<button class="bi bi-arrow-left-circle-fill mb-2 ml-2 flex h-9 w-9 items-center justify-center rounded-full border border-slate-300 text-slate-300 hover:border-primary hover:bg-primary hover:text-white"
onclick="prev()"
></button>
</div>
</div>
<div id="sliderContainer" class="overflow-hidden">
<ul id="slider" class="flex w-full">
<li>Slide 1</li>
<li>Slide 2</li>
<li>Slide 3</li>
</ul>
</div>
<div class="flex items-center">
<div class="w-full">
<button
onclick="next()"
class="bi bi-arrow-right-circle-fill mr-2 mb-2 ml-2 flex h-9 w-9 items-center justify-center rounded-full border border-slate-300 text-slate-300 hover:border-primary hover:bg-primary hover:text-white slider__btn--right"
></button>
</div>
</div>
</div>
Please help me modify the JS code so it can be use multiple times. thanks in advance!
Related
I am trying to build a website and have this carousel, that was made with Alpine JS and Tailwind CSS. I took the Alpine Javascript from a template and it works according to specifications. But I would want to make it mouse draggable as well.
Here is the carousel slider
<div class="mt-24">
<script>
window.carousel = function () {
return {
container: null,
prev: null,
next: null,
init() {
this.container = this.$refs.container
this.update();
this.container.addEventListener('scroll', this.update.bind(this), {passive: true});
},
update() {
const rect = this.container.getBoundingClientRect();
const visibleElements = Array.from(this.container.children).filter((child) => {
const childRect = child.getBoundingClientRect()
return childRect.left >= rect.left && childRect.right <= rect.right;
});
if (visibleElements.length > 0) {
this.prev = this.getPrevElement(visibleElements);
this.next = this.getNextElement(visibleElements);
}
},
getPrevElement(list) {
const sibling = list[0].previousElementSibling;
if (sibling instanceof HTMLElement) {
return sibling;
}
return null;
},
getNextElement(list) {
const sibling = list[list.length - 1].nextElementSibling;
if (sibling instanceof HTMLElement) {
return sibling;
}
return null;
},
scrollTo(element) {
const current = this.container;
if (!current || !element) return;
const nextScrollPosition =
element.offsetLeft +
element.getBoundingClientRect().width / 2 -
current.getBoundingClientRect().width / 2;
current.scroll({
left: nextScrollPosition,
behavior: 'smooth',
});
}
};
}
</script>
<style>
.scroll-snap-x {
scroll-snap-type: x mandatory;
}
.snap-center {
scroll-snap-align: center;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
<div class="flex mx-auto items-center">
<div x-data="carousel()" x-init="init()" class="relative overflow-hidden group">
<div x-ref="container"
class="ml-4 flex overflow-x-scroll scroll-snap-x space-x-4 no-scrollbar touch-pan-x cursor-pointer">
{% for origin in origins %}
<div class="group/item relative ml-4 flex-auto flex-grow-0 flex-shrink-0 w-4/5 sm:w-4/5 xl:w-2/5 rounded-lg bg-gray-100 items-center justify-center snap-center overflow-hidden shadow-md"><!-- items container -->
<div class="min-w-full h-full rounded-lg bg-gray-100 overflow-hidden shadow-md">
<div><img src="{{ origin.imgurl }}" alt="{{ origin.alt }}" class="object-cover h-96" /></div>
<div class="absolute bg-gray-800 bg-opacity-0 group-hover/item:bg-opacity-50 top-2/3 inset-x-0 text-center px-2 py-3">
<div class="text-2xl text-transparent group-hover/item:text-emerald-500 font-extrabold uppercase">{{ origin.name }}</div>
<div class="text-xl text-transparent group-hover/item:text-white font-bold">{{ origin.country }}</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div #click="scrollTo(prev)" x-show="prev !== null"
class="block absolute top-1/2 left-0 bg-white text-3xl p-2 rounded-full transition-transform ease-in-out transform -translate-x-full -translate-y-1/2 group-hover:translate-x-0 cursor-pointer">
<div><ion-icon name="chevron-back-outline"></ion-icon></div>
</div>
<div #click="scrollTo(next)" x-show="next !== null"
class="block absolute top-1/2 right-0 bg-white p-2 text-3xl rounded-full transition-transform ease-in-out transform translate-x-full -translate-y-1/2 group-hover:translate-x-0 cursor-pointer">
<div><ion-icon name="chevron-forward-outline"></ion-icon></div>
</div>
</div>
</div>
</div>
</div>
I tried adding Alpine script from a different carousel that has this function, but it does not do anything. My understanding of Javascript is rudimentary.
for(let x = 0; x < 10; x++){container_For_Dices.push(Math.round(Math.random()*10))}
let Ids = ["Dice1","Dice2","Dice3","Dice4","Dice5","Dice6","Dice7","Dice8","Dice9","Dice10"]
let id = -1
container_For_Dices= container_For_Dices.map((value=>{
id +=1
return(
<button
className="w-20 h-20 m-2 rounded-lg bg-slate-300 content-center pt-4 shadow shadow-gray-500 text-4xl font-bold dices"
id={Ids[id]}
onClick={()=>{
var element = document.getElementById(`${Ids[id]}`)
element.classList.remove("dices")
element.style.backgroundColor = "rgba(255,255,255,0.5)"
}} >
{value}
</button>
)
}))
The problem is definitely in the onClick part, but I have no clue how to fix it. If I click on one of the elements, the 10th element gets picked. I already checked that every element got the correct id...
Try passing event argument onClick={(e)=>{ and extract element from event var element = e.target;
<button
className="w-20 h-20 m-2 rounded-lg bg-slate-300 content-center pt-4 shadow shadow-gray-500 text-4xl font-bold dices"
id={Ids[id]}
onClick={(e)=>{
var element = e.target;
element.classList.remove("dices")
element.style.backgroundColor = "rgba(255,255,255,0.5)"
}} >
I have a page with progress, that should show progress between items, and when you arrive at the item, the bubble turns yellow. The bubble part works fine with getBoundingClientRect, however the scroll calculation is wrong (screenshot):
Code:
scollbar HTML (blade):
<div class="container grid grid-cols-12">
<div class='col-span-3 w-full text-white flex bg-cold85 pt-20 pl-11 pb-28 h-full sticky top-[90px] max-h-fit' >
<div class='flex shrink-0 items-center'>
<div class="w-[1px] h-full mt-[12px] -mr-[1px] z-30">
<div class="line w-[1px] h-[0%] !z-30 relative bg-yellow scroll-line"></div>
{{-- <div class="line w-[1px] h-[calc(100%_-_20px)] relative bg-cream"></div> --}}
</div>
<div class="w-[1px] h-full mt-[12px] -mr-[6px] z-0">
{{-- <div class="line w-[1px] h-[50%] !z-30 relative bg-yellow"></div> --}}
<div class="line w-[1px] h-[calc(100%_-_20px)] relative bg-cream"></div>
</div>
<div class="scrollcontainer">
#foreach ($items as $item)
<div class="flex items-center gap-x-6 mb-13 last:mb-0 children:first:border-yellow children:first:bg-yellow scrollbar-item" id='{{$item['id']}}'>
<i class="z-10 w-[11px] h-[11px] rounded-full border-2 border-cream bg-cold">
</i>
{{$item['text']}}
</div>
#endforeach
</div>
</div>
</div>
<div class="col-span-9 scroll-part">
#php(the_content())
</div>
</div>
script:
const items = document.querySelectorAll('.scrollbar-selector');
const scrollContainer = document.querySelector('.scrollcontainer');
window.addEventListener('scroll', () => {
const scroll = window.scrollY;
// const doc = document.body.clientHeight;
// const win = window.innerHeight;
const scrollPart = document.querySelector('.scroll-part').clientHeight;
let value = (scroll / (scrollPart)) * 100;
items.forEach(i => {
if (i.getBoundingClientRect().top <= 500) {
let id = i.getAttribute('id');
let sItem = scrollContainer.querySelector(`#${id} i`);
sItem.style.borderColor = '#F4C514';
sItem.style.backgroundColor = '#F4C514';
} else {
let id = i.getAttribute('id');
let sItem = scrollContainer.querySelector(`#${id} i`);
sItem.style.backgroundColor = '#151616';
sItem.style.borderColor = 'white';
}
});
const line = document.querySelector('.scroll-line');
line.style.height = `${value}%`;
});
I'm trying to build a menu that could be scrolled horizontally with two buttons : one left, one right. Note that I can't accept the "onClick" solution as what I really want is the same behaviour as a traditional scrollbar button (which is => Scroll While Pressed).
I'm using js/jquery to do this, but the only result I could came up with is the scroll to occur only on MouseUp event... my guess is that the obj.animate() jquery method only executes once the recursion call comes to an end, but I thought that the callback method was made for such cases...
Any idea ? Most examples I found on the net assumes my code should work... but it doesn't !
I am building my site with ASP.NET MVC 5, bootstrap 5.1 and JQuery 3.6.0 (no other versions since I updated the package via th PMC)
<div class="offcanvas offcanvas-top bg-dark vh-50" tabindex="-1" id="MainMenuCanvas" aria-labelledby="MainMenuLabel">
<div class="offcanvas-header">
...
</div>
<div class="offcanvas-body text-light px-5 hidden-scrollbar">
#*<h2>Menu</h2>*#
<div class="d-flex flex-row justify-content-between">
<button id="scrollLeftButton" class="btn border border-1 border-danger bg-transparent rounded-pill p-3 align-self-center">
<img class="ratio ratio-1x1" style="width:25px; transform: rotate(90deg);" src="~/Content/Visuals/Icons/right-arrow-lite-red.png" />
</button>
<ul id="scrollableSections" class="navbar-nav flex-row overflow-scroll hidden-scrollbar">
#for (int i = 0; i < Menu.Sections.Count; i++) {
... Display sections ...
}
</ul>
<button id="scrollRightButton" class="btn border border-1 border-danger bg-transparent rounded-pill p-3 align-self-center">
<img class="ratio ratio-1x1" style="width:25px; transform: rotate(-90deg);" src="~/Content/Visuals/Icons/right-arrow-lite-red.png" />
</button>
</div>
</div>
</div>
<script type="text/javascript">
var scrolling = false;
var target = $("#scrollableSections");
var param = "";
$("#scrollLeftButton").mousedown(function () {
event.preventDefault();
scrolling = true;
param = "-=250px";
startScrolling();
})
$("#scrollLeftButton").mouseup(function () {
event.preventDefault();
scrolling = false;
});
$("#scrollRightButton").mousedown(function () {
event.preventDefault();
scrolling = true;
param = "+=250px";
startScrolling();
})
$("#scrollRightButton").mouseup(function () {
event.preventDefault();
scrolling = false;
});
function startScrolling() {
if(scrolling)
target.animate({ scrollLeft: param }, { duration: 50, easing: "linear", complete: startScrolling });
}
</script>
I am new to javascript and creating the 2 tab to display the data. It is using the handlebar template and pure javascript. I want to add the loading spinner before the data is loaded in each tabs. How can implement the loading spinner to the multiple tabs when the data is retrieved using append method. i try to add the loading spinner but it always show the loading spinner eventhough the data has been loaded and displayed.When i try to add loading spinner it got the error of 'Cannot read property 'appendChild' of null' Any help will be appreciate. Below is the code without implement the loading spinner
function reload_api(){
let source = document.getElementById("template").innerHTML;
let template = Handlebars.compile(source);
let html = template(data_api.data);
document.getElementById("contents").innerHTML = html;
render_tab_contents();
document.getElementById("index-0").click();
}
function tab_contents() {
//this part is to display the data (tab content)
data_api.data.dropdown.forEach((agoptions) => {
data_api.data.fyafyidropdown.forEach((dropOptions) => {
for (const [key, value] of Object.entries(data_api.data.listing)) {
let groupName = `dropdowngroup_${agoptions.ag_id}_${agoptions.ad_id}_${dropOptions.value}`;
if (key == groupName) { document.getElementById(`ag_${agoptions.ag_id}_${agoptions.ad_id}`);
for (const [key1, val1] of Object.entries(value)) {
if (key1 != 'more_url') {
let contents = document.createElement('div');
contents.innerHTML ='<div class="line text-gray-500 flex flex-col divide-black divide-y-2 divide-opacity-25"><div class="flex justify-between items-center cursor-pointer p-2 transition duration-200 ease-in-out hover:bg-gray-100 hover:shadow-xl" ><div class="flex flex-col items-start">' + '<p class="font-semibold pb-1">' +val1.mail_serial_no + '</p><p class="pb-1 text-sm">'+ val1.mail_subject + '</p><div class="flex items-center pb-1"></div></div>'+ '<div class="flex-none"><div class="flex flex-col text-sm"><p class="px-2 py-1 font-semibold ">' + val1.pending_count + '</p></div></div></div></div>'; document.getElementById(`ag_${agoptions.ag_id}_${agoptions.ad_id}`).appendChild(contents);
}
}
}
}
});
if(document.getElementById(`ag_${agoptions.ag_id}_${agoptions.ad_id}`).innerHTML == ''){
document.getElementById(`ag_${agoptions.ag_id}_${agoptions.ad_id}`).innerHTML = '<div class="flex justify-center mt-4">No Records</div>';
}
});
}
function open(evt, Name) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(Name).style.display = "block";
evt.currentTarget.className += " active";
}
<script type="text/x-handlebar-template" id="template">
<div class="tab">
<div class="mb-1">
<div class="flex text-center text-gray-400">
{{#each dropdown}}
<button class="tablinks flex-1 py-2 px-4 border-b-2 cursor-pointer " id="index-{{#index}}" onclick="open(event, 'ag_{{ag_id}}_{{ad_id}}')">{{full_name}}</button>
{{/each}}
</div>
</div></div>
{{#each dropdown}}
<div id="agency_{{ag_id}}_{{ad_id}}" data-scope="ag_{{ag_id}}_{{ad_id}}" class="tabcontent">
//here is the data displayed. the data is retrieved and displayed using the javascript in tab_contents() //
</div>
{{/each}}
</script>
<div id="contents"></div>
// Here is the sample data from api
//data: {,…}
//dropdown: [{ag_id: "60", ad_id: "1", agency_short_name: "HQ", agency_full_name: "HQ",…},…]
//fyafyidropdown: [{label: "FYA", value: "fya"}, {label: "FYI", value: "fyi"}]
//listing: {agadgroup_60_1_fya: {,…},…}
//group_60_1_fya: {,…}
//group_60_1_fyi: {0: {mail_serial_no: "(Filing)", mail_subject: "123", pending_count: 1,…},…}