I would like to create something akin to a slide show, whenever the button #more-projectsis clicked 4 divs would show, when it's clicked again those divs get hidden and the next 4 divs show, this needs to loop infinitely and be applied to any number of divs.
In this context I have 6 divs with the class .thumbnail-cnt within the div '#container', when I click #more-projects I would like the first 4 of these divs to show, divs 1,2,3,4. When #more-projects is clicked again the next 4 divs in the cycle would be shown so 5,6,1,2. Clicked again, divs 1,2,3,4 are shown. How can I select the next index and make the function iterate over the elements infinitely?
<div id="container">
<div class="thumbnail-cnt" data-num="1">1
</div>
<div class="thumbnail-cnt" data-num="2">2
</div>
<div class="thumbnail-cnt" data-num="3">3
</div>
<div class="thumbnail-cnt" data-num="4">4
</div>
<div class="thumbnail-cnt" data-num="5">5
</div>
<div class="thumbnail-cnt" data-num="6">6
</div>
</div>
<button id="more-projects" > Next
</button>
My JS so far is
var startIndex = 0;
$('#more-projects').on("click", function() {
var endIndex = startIndex + 4;
var nextIndex = endIndex +1;
$('#container .thumbnail-cnt').slice(startIndex, endIndex).addClass('visible');
var startIndex = nextIndex;
}
CSS
.thumbnail-cnt {
display: none;
}
.visible {
display: block;
}
A solution can be based on:
save the initial value of max number of visible items as data value of your div
save the start index as another data value
So, the initial value is:
<div id="container" data-start-index="0" data-max-visible-length="4">
In the click handler compute the end index position. If it exceeds the max you need to start from the beginning. Save this new value as the new start index.
$('#more-projects').on("click", function() {
var startIndex = $('#container').data('startIndex');
var maxVisibleLength = $('#container').data('maxVisibleLength');
var endIndex = startIndex + maxVisibleLength;
var itemCounts = $('#container .thumbnail-cnt').length;
$('#container .thumbnail-cnt.visible').removeClass('visible');
if (endIndex > itemCounts) {
endIndex = endIndex - itemCounts;
$('#container .thumbnail-cnt').slice(startIndex).addClass('visible');
$('#container .thumbnail-cnt').slice(0, endIndex).addClass('visible');
} else {
$('#container .thumbnail-cnt').slice(startIndex, endIndex).addClass('visible');
}
$('#container').data('startIndex', endIndex);
});
.thumbnail-cnt {
display: none;
}
.visible {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container" data-start-index="0" data-max-visible-length="4">
<div class="thumbnail-cnt" data-num="1">1
</div>
<div class="thumbnail-cnt" data-num="2">2
</div>
<div class="thumbnail-cnt" data-num="3">3
</div>
<div class="thumbnail-cnt" data-num="4">4
</div>
<div class="thumbnail-cnt" data-num="5">5
</div>
<div class="thumbnail-cnt" data-num="6">6
</div>
</div>
<button id="more-projects" > Next
</button>
You can try this for your JS. This will ensure an infinite loop-
var startIndex = 0;
$('#more-projects').on("click", function() {
var count = 0;
var divs = $('#container .thumbnail-cnt');
var len = divs.length
var index;
while( count < 4 ){
index = (startIndex+count) % len;
divs[index].addClass('visible');
count++;
}
startIndex += count;
index++; //to ensure removal start from the next
while( count < len ){
index = index % len;
divs[index++].removeClass('visible');
count++;
}
}
% ensures the boundary and wrapping. Removing class visible for the other divs that need not be shown. Your current implementation must have been failing on this.
Related
I'm trying to create HTML like this:
<div class="container">
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
</div>
The component used to add el element:
<input type="text" name="elements" />
el elements will appended to the container based on what number is added in the input. Every 3 elements should be wrapped in wrap div.
What I have so far:
$("input[name=elements]").on("keydown keyup", function() {
var amount = parseInt($(this).val());
for(i = 0; i < amount; i++) {
$(".container").append('<div class="el"></div>');
}
});
It adds the el divs but I'm not sure how to simultaneously wrap every 3 in wrap. Also is it possible to also remove el divs? If say I first type 8 in the input then I type 3, 11 divs will be added instead having just 3. In other words, the number of el divs in the HTML should alway be equal to the number in the input value. Would it make sense just to clear out the HTML first every time on input type?
You could first create an array of elements based on number of input value, append it to container and then wrap every nth element into wrap element.
const container = $('.container')
$("input").on('keyup', function() {
const val = parseInt($(this).val()) || 0;
const html = Array.from(Array(val), () => (
$("<div>", {
'class': 'el',
'text': 'element'
})
))
container.html(html)
for (let i = 0; i < val; i += 3) {
container
.find('.el')
.slice(i, i + 3)
.wrapAll("<div class='wrap'></div>");
}
})
.wrap {
border: 1px solid green;
margin: 10px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text">
<div class="container"></div>
I have an object (div) which has four elements (with classes) inside.
Task: When height of the element A is lower than 40px then add to element B 20px margin-top.
However there are many objects on the page.
<div class="list">
<div class="block">
<div class="list-name" style="height: 20px">element A</div>
<div class="div1">another div here</div>
<div class="div2">another div here</div>
<div class="product-image-container">element B</div>
</div>
<div class="block">
<div class="list-name" style="height: 50px">element A</div>
<div class="div1">another div here</div>
<div class="div2">another div here</div>
<div class="product-image-container">element B</div>
</div>
(...)
</div>
Sorry, I tried this so far. However it works only if there are only two elements in the div.
$(document).ready(function(){
$('.list-name').each(function(index, obj){
console.log($(obj).height())
if($(obj).height() < 40)
{
$(obj).next('.product-image-container').css('margin-top', 20)
}
});
});
Thanks for any help.
Rob
As far as I understand, you need something like this:
var heightA = $(".list-name").css("height"); //get height of element. E.g. "20px"
heightA = heightA.substr(0, heightA.length - 2); //remove "px" from string => "20"
if (heightA < 40) { //if the height is less than 40
$(".product-image-container").css("margin-top", "20px"); //add margin-top 20px
}
To do this for all elements, you will need a for. Maybe try this:
var elements = $("body div").length; //get amount of divs. In the HTML you provided 'body' is the parent
for (i = 0; i < elements; i++) { //loop 'em all
var heightA = $("div:nth-child(" + i + ") .list-name").css("height"); //again get height of corresponding element
heightA = heightA.substr(0, heightA.length - 2); //remove "px"
if (heightA < 40) {
$("div:nth-child(" + i + ") .product-image-container").css("margin-top", "20px"); //set margin-top of corresponding element
}
}
Currently have a div that controls the width of an element as well as the background color. That div has a child div which has the content which is semi-transparent. Which is why I need the first div. So the background is solid.
Now, I added an event listener to the parent which expands the width of one and decreases the width of the other 2 so they all fit. However, when I click on the parent div I would like the child of that specific div to add a class and remove a class from the other 2. Which I can't seem to figure out. Here's the code. Sorry if my formatting is poor, first time posting on stack overflow and I've googled and searched everything for an answer and can't seem to find one.
var purchaseStepCont = document.querySelectorAll(".step-container");
var purchaseStep = document.querySelectorAll(".step");
for (var i = 0; i < purchaseStepCont.length; i++) {
purchaseStepCont[i].addEventListener("click", function() {
for (var i = 0; i < purchaseStepCont.length; i++) {
purchaseStepCont[i].classList.remove("stepContActive");
purchaseStepCont[i].classList.add("stepContDeactive");
this.classList.add("stepContActive");
this.classList.remove("stepContDeactive");
}
});
}
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
You're very close. But if you want to add the class to the .step, you need this.firstElementChild.classList.add(...) rather than this.classList.add(...) (since this will be the .step-container, not the .step; but the .step is its first element child). Or for more markup flexibility, you could use this.querySelector(".step").
You can also use just a single event handler function rather than recreating it in the loop:
var purchaseStepCont = document.querySelectorAll(".step-container");
var purchaseStep = document.querySelectorAll(".step");
function clickHandler() {
var thisStep = this.firstElementChild; // Or this.querySelector(".step") would be more flexible
for (var i = 0; i < purchaseStep.length; i++) {
if (purchaseStep[i] === thisStep) {
purchaseStep[i].classList.add("stepContActive");
purchaseStep[i].classList.remove("stepContDeactive");
} else {
purchaseStep[i].classList.remove("stepContActive");
purchaseStep[i].classList.add("stepContDeactive");
}
}
}
for (var i = 0; i < purchaseStepCont.length; i++) {
purchaseStepCont[i].addEventListener("click", clickHandler);
}
.stepContActive {
color: blue;
}
.stepContDeactive {
color: #ddd;
}
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
clickHandler could be a bit shorter if you don't need to support IE11:
function clickHandler() {
var thisStep = this.firstElementChild; // Or this.querySelector(".step") would be more flexible
for (var i = 0; i < purchaseStep.length; i++) {
purchaseStep[i].classList.toggle("stepContActive", purchaseStep[i] === thisStep);
purchaseStep[i].classList.toggle("stepContDeactive", purchaseStep[i] !== thisStep);
}
}
But IE11 doesn't support the second argument to classList.toggle.
I'm trying to develop a very simple carousel with Pure Javascript and CSS. I'm having trouble with the "next" and "previous" button, since they should communicate the with each other, setting the current position of the carousel.
Here is my current code (or JsFiddle: https://jsfiddle.net/kbumjLw2/1/):
// Slider
var listRecommendations = document.getElementById('list-recommendations');
listRecommendations.style.left = 0;
// Previous button
document.getElementById("btn-prev").onclick = function() {
// Making sure this functions will not run if it's the 0px of the slider
if (parseInt(listRecommendations.style.left) != 0) {
var currentPosition = parseInt(listRecommendations.style.left) + 100;
listRecommendations.style.left = currentPosition + 'px';
console.log(currentPosition);
};
}
// Next button
var num = 100;
var maxValue = 1000 + 120;
console.log(maxValue);
document.getElementById("btn-next").onclick = function() {
if (num < maxValue) {
num += 100;
listRecommendations.style.left = '-' + num + 'px';
console.log(num);
};
}
#list-recomendations{
position: absolute;
}
.wrap-items .item{
float: left;
}
<div class="recommendation">
<div id="slider">
<div id="list-recommendations" class="wrap-items">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
</div>
</div>
<!-- Slider controls -->
<button id="btn-prev" class="btn-slider prev">Previous</button>
<button id="btn-next" class="btn-slider next" title="Ver mais sugestões">Next</button>
</div>
As you can see on the console, the next button increases "100" at each click, and the previous button decreases "100" at each click. The previous button seems to be working fine, but the next button don't get the updated value, it always increase using the latest value it used.
Expected result:
Next button clicked: 100 > 200 > 300...
Prev button clicked: 200 > 100 > 0...
Next button clicked: 100 > 200 > 300...
Current result:
Next button clicked: 100 > 200 > 300...
Prev button clicked: 200 > 100 > 0...
Next button clicked: 400 > 500 > 600...
Any idea on what may be causing this issue?
check this updated fiddle
// Previous button
document.getElementById("btn-prev").onclick = function() {
// Making sure this functions will not run if it's the 0px of the slider
var currentPosition = parseInt(listRecommendations.style.left) - 100;
listRecommendations.style.left = currentPosition + 'px';
console.log(currentPosition);
}
// Next button
var num = 100;
var maxValue = 1000 + 120;
console.log(maxValue);
document.getElementById("btn-next").onclick = function() {
if (num < maxValue) {
var currentPosition = parseInt(listRecommendations.style.left) + 100;
num = currentPosition;
listRecommendations.style.left = num + 'px';
console.log(num);
};
}
you basically need to take the left value each time and do plus (+ next) minus (- prev) on it.
There is no use of num as you can play with the current position of the element.
Also note, - will concatenate the - symbol the the value, it will not subtract the value.
Ty this:
var maxValue = 1000 + 120;
var listRecommendations = document.getElementById('list-recommendations');
listRecommendations.style.left = 0;
document.getElementById("btn-prev").onclick = function() {
var val = parseInt(listRecommendations.style.left);
if (val != 0) {
val -= 100;
listRecommendations.style.left = val + 'px';
console.log(val);
};
}
document.getElementById("btn-next").onclick = function() {
var val = parseInt(listRecommendations.style.left);
if (val < maxValue) {
val += 100;
listRecommendations.style.left = val + 'px';
console.log(val);
};
}
#list-recomendations {
position: absolute;
}
.wrap-items .item {
float: left;
}
<div class="recommendation">
<div id="slider">
<div id="list-recommendations" class="wrap-items">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
</div>
</div>
<!-- Slider controls -->
<button id="btn-prev" class="btn-slider prev">Previous</button>
<button id="btn-next" class="btn-slider next" title="Ver mais sugestões">Next</button>
</div>
Fiddle here
I want to set up floating elements in a way it would depend of their amount.
<div id="whatever">
<div class="iwantthischangedto3elements">
element 1
element 2
element 3
</div>
<div class="iwantthischangedto2elements">
element 1
element 2
</div>
</div>
I can't really figure how to do this.
I'm guessing javascript is the answer, but can't get it work :
<script type="text/javascript">
function f() {
var list = document.getElementById("whatever");
var nbofelements = whatever.getElementsByTagName("a").length;
return nbofelements ;
whatever.getElementsByTagName("div").className += "elements + nbofelements";
}
</script>
Thanks for helping, I really struggle with javascript...
Edit:
Thanks all for your answers.
Sorry i didn't make myself clear enough, english isn't my first language.
Tambo did get what I meant, code works great.
However there's something I forgot...
An "h1" can be sometimes placed before the "a" list, and should not be counted as an element. Possibly other "h2", "h3", and so on... I want to count only "a" elements...
<div id="whatever">
<div class="iwantthischangedto3elements">
<h1>Do no not count this</h1>
element 1
element 2
element 3
</div>
Any idea how to proceed ?
Cheers
Vincent
You can use the for loop and Element.classList to add class
var whateverChild = document.querySelectorAll("#whatever div");
for(var i = 0; i < whateverChild.length; i++) {
//var anchor = document.querySelectorAll(whateverChild);
if(whateverChild[i].childElementCount == 2){
whateverChild[i].classList.add("two")
}else if(whateverChild[i].childElementCount == 3){
whateverChild[i].classList.add("three")
}
}
.two a{color: green}
.three a{color: red}
<div id="whatever">
<div class="iwantthischangedto3elements">
element 1
element 2
element 3
</div>
<div class="iwantthischangedto2elements">
element 1
element 2
</div>
</div>
If you have a paragraph in it you can use this
var whateverChild = document.querySelectorAll("#whatever div");
for(var i = 0; i < whateverChild.length; i++) {
var anchorLength = whateverChild[i].querySelectorAll("a").length;
if(anchorLength == 2){
whateverChild[i].classList.add("two")
}else if(anchorLength == 3){
whateverChild[i].classList.add("three")
}
}
.two a{color: green}
.three a{color: red}
<div id="whatever">
<div class="iwantthischangedto3elements">
element 1
<p> i am a paragraph </p>
element 2
element 3
</div>
<div class="iwantthischangedto2elements">
element 1
element 2
</div>
</div>
You can reduce the code to this
var numbersArray = ["zero", "one", "two", "three", "four", "five"],
whateverChild = document.querySelectorAll("#whatever div");
for(var i = 0; i < whateverChild.length; i++) {
var anchorLength = whateverChild[i].querySelectorAll("a").length;
whateverChild[i].classList.add(numbersArray[anchorLength])
}
.zero{color: orange}
.one{color: blue}
.two a{color: green}
.three a{color: red}
.four{color: purple}
.five{color: beige;}
<div id="whatever">
<div class="iwantthischangedto3elements">
element 1
<p> i am a paragraph </p>
element 2
element 3
</div>
<div class="iwantthischangedto2elements">
element 1
element 2
</div>
</div>