I made a simple quote slider that changes the sentence on the screen randomly upon each click, but I don't want it to show the same quote twice in a row. How can I exclude the current quote from the Math.random algorithm and make the slider show any other quote upon each click?
(I don't want each quote to be shown only once, I just want to prevent the slider to show the same one in a row)
HTML:
<div class="container" onclick="changequote()">
<div class="quote">Sentence 1</div>
<div class="quote">Sentence 2</div>
<div class="quote">Sentence 3</div>
</div>
<script>window.onload = changequote()</script>
JS:
function changequote() {
var random = Math.floor(Math.random() * $('.quote').length);
$('.quote').hide().eq(random).fadeTo(500, 1);
}
CSS:
.quote {
display: none;
}
You could toggle a class to track the current quote. Then randomly pick from the quotes that are :not(.current).
Also I moved the javascript to the javascript section, which really helps for maintainability. Nothing like pouring through the .js file when debugging just to find out the issue is originating from the html.
function changequote() {
let $quotes = $('.quote:not(.current)');
$('.quote').removeClass('current').hide(); //reset all quotes
$quotes //pick from all quotes except the previously selected one
.eq(Math.floor(Math.random() * $quotes.length))
.addClass('current')
.fadeIn();
}
$('.container').on('click', changequote).click(); //click will initialize on page load
.quote{
cursor: pointer;
display: none;}
<div class="container">
<div class="quote">Sentence 1</div>
<div class="quote">Sentence 2</div>
<div class="quote">Sentence 3</div>
<div class="quote">Sentence 4</div>
<div class="quote">Sentence 5</div>
<div class="quote">Sentence 6</div>
<div class="quote">Sentence 7</div>
<div class="quote">Sentence 8</div>
<div class="quote">Sentence 9</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Just save the index into a global variable, using closures
const changeQuote = (() => {
let lastSelectedIndex;
return function() {
let random = Math.floor(Math.random() * $('.quote').length);
while(true)
{
random = Math.floor(Math.random() * $('.quote').length);
if(random !== lastSelectedIndex) break;
}
$('.quote').eq(lastSelectedIndex).hide();
$('.quote').eq(random).fadeIn(500);
lastSelectedIndex = random;
}
})();
EDIT: Here is a running example
const changeQuote = (() => {
let lastSelectedIndex;
return function() {
let random = Math.floor(Math.random() * $('.quote').length);
while(true)
{
random = Math.floor(Math.random() * $('.quote').length);
if(random !== lastSelectedIndex) break;
}
$('.quote').eq(lastSelectedIndex).hide();
$('.quote').eq(random).fadeIn(500);
lastSelectedIndex = random;
}
})();
.quote {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="quote">Sentence 1</div>
<div class="quote">Sentence 2</div>
<div class="quote">Sentence 3</div>
<button onclick="changeQuote()">Show Random Quote</button>
Related
I have problem with getting value of a div with class="idC". I need this value to remove object from my array. I know I probably gonna need to use parseInt but all I'm getting with this code is rowid: undefined when I use console.log("rowid: " + bikeId.value). bikeRow.remove(); works fine. It removes the row I want to.
const buttonDel = document.querySelectorAll(".deleteC");
buttonDel.forEach(button => {
console.log("jazda");
button.addEventListener('click', () => {
const bikeRow = button.parentNode;
const bikeId = bikeRow.firstChild;
if (!window.confirm())
return;
console.log("rowid: " + bikeId.value);
bikeRow.remove();
//bikeStorage.removeBike(bikeId.value);
})
})
<div class="bikeRows">
<div class="bikeRow">
<div class="idC"></div>${bike.id}</div>
<div class="frameC">${bike.frame}</div>
<div class="suspC">${bike.susp}</div>
<div class="wheelC">${bike.wheel}</div>
<div class="typeC">${bike.constructor.name}</div>
<div class="deleteC"><button class="delButton" id="${bike.id}">Delete</button></div>
</div>
</div>
You have invalid HTML and you really should delegate
I added the ID as a data-attribute to the row like this
<div class="bikeRow" data-id="${bike.id}">
Makes the code much simpler to debug and extend
document.querySelector(".bikeRows").addEventListener("click", e => {
const tgt = e.target.closest("button");
if (!tgt.matches(".delButton")) return; // not a delete button
if (!window.confirm("Sure?")) return; // they cancelled
const bikeRow = tgt.closest("div.bikeRow"),
id = bikeRow.dataset.id; // the ID to remove from the storage
bikeRow.remove();
//bikeStorage.removeBike(id);
})
<div class="bikeRows">
<div class="bikeRow" data-id="ID1">
<div class="idC">ID1</div>
<div class="frameC">Frame 1</div>
<div class="suspC">Susp 1</div>
<div class="wheelC">Wheel 1</div>
<div class="typeC">Constructor name 1</div>
<div class="deleteC"><button class="delButton">Delete</button></div>
</div>
<div class="bikeRow" data-id="ID2">
<div class="idC">ID 2</div>
<div class="frameC">Frame 2</div>
<div class="suspC">Susp 2</div>
<div class="wheelC">Wheel 2</div>
<div class="typeC">Constructor name 2</div>
<div class="deleteC"><button class="delButton">Delete</button></div>
</div>
<div class="bikeRow" data-id="ID3">
<div class="idC">ID 3</div>
<div class="frameC">Frame 3</div>
<div class="suspC">Susp 3</div>
<div class="wheelC">Wheel 3</div>
<div class="typeC">Constructor name 3</div>
<div class="deleteC"><button class="delButton">Delete</button></div>
</div>
</div>
How can I easily toggle/switch the classes between three or more divs in a sequence by click?
Example html:
<div class="box">Box 1</div>
<div class="box active">Box 2</div>
<div class="box">Box 3</div>
<div class="next">Next</div>
<div class="back">Back</div>
This way just works with two:
$(".next, .back").click(function(){
$(".box").toggleClass("active");
});
Simply to check if nth-child has the active class and loop.
$(".next").click(function(){
$(".box").each( function(i, j) {
if( $(this).hasClass('active') ) {
$(this).removeClass('active');
$(".box:nth-child("+(((i+1)%$(".box").length)+1)+")").addClass('active');
return false;
}
});
});
$(".back").click(function(){
$(".box").each( function(i, j) {
if( $(this).hasClass('active') ) {
$(this).removeClass('active');
$(".box:nth-child("+(((i-1)+$(".box").length)%$(".box").length+1)+")").addClass('active');
return false;
}
});
});
.active {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class="box">Box 1</div>
<div class="box active">Box 2</div>
<div class="box">Box 3</div>
<div class="back">Back</div>
<div class="next">Next</div>
</div>
This is pretty generic
$(".nav").on("click", function() {
var dir = $(this).is(".next") ? 1 : -1; // which direction are we going?
var active = $(".box.active").index() - 1;
var $boxes = $(".box");
active += (1 * dir);
if (active < 0) active = $boxes.length - 1; // wrap
else if (active >= $boxes.length) active = 0;
$(".box").removeClass("active");
$(".box").eq(active).addClass("active");
});
.active {
color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="box">Box 1</div>
<div class="box active">Box 2</div>
<div class="box">Box 3</div>
<br/>
<div class="nav back">Back</div>
<div class="nav next">Next</div>
I've created another solution that gets the button's ID (which I've added) and uses that to dictate where to go.
var box = $('.box');
var maxBoxes = box.length;
$("#nextBtn, #backBtn").click(function() {
var getBtn = $(this).get(0).id,
activeBox = $('.box.active'),
position = activeBox.index();
activeBox.removeClass('active');
if (getBtn === 'nextBtn') {
(position == maxBoxes) ? box.eq(0).addClass('active'): activeBox.next().addClass('active');
} else {
(position === 1) ? box.last().addClass('active'): activeBox.prev().addClass('active');
}
});
.active {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="box">Box 1</div>
<div class="box">Box 2</div>
<div class="box active">Box 3</div>
<div id="nextBtn" class="next">Next</div>
<div id="backBtn" class="back">Back</div>
I think that the following should be efficient, because it will not do unnecessary checks.
var last = $(".active")
function toggle(dir){
var future = last[dir]();
if(!(future.length && future.hasClass('box'))) {
return;
}
last.toggleClass("active")
last = future
last.toggleClass("active");
}
$(".next").click(toggle.bind(void 0, 'next'));
$(".back").click(toggle.bind(void 0, 'prev'));
I'm trying to toggle a div's class in pure javascript (unfortunately I cannot use jQuery). I've got some code working but it doesn't work for multiple instances of the div and I'd appreciate some help for that.
I can't give each element it's own specific ID, so I'd need a way to target only the div with a class of 'truncate' that is the parent of the particular button that is clicked. Currently I have hidden the results by default and am just toggling the 'show' class
<div class="truncate">
<div class="result">Result 1</div>
<div class="result">Result 2</div>
<div id="button">Show more</div>
</div>
collapse= document.getElementById('button');
collapse.onclick = function() {
collapse.parentElement.classList.toggle("show");
};
/* HIDE PLATES BY DEFAULT */
.truncate .result {
display: none;
}
/* SHOW RESULTS WHEN SHOW CLASS APPLIED */
.truncate.show .result {
display: block !important;
}
Thank you - help appreciated as always.
I think you need getElementsByClassName
(function() {
var collapse = document.getElementsByClassName('button');
for (var elIndex = 0; elIndex < collapse.length; elIndex++) {
collapse[elIndex].onclick = function() {
this.parentElement.classList.toggle("show");
};
}
})();
/* HIDE PLATES BY DEFAULT */
.truncate .result {
display: none;
}
/* SHOW RESULTS WHEN SHOW CLASS APPLIED */
.truncate.show .result {
display: block !important;
}
<div class="truncate">
<div class="result">Result 1</div>
<div class="result">Result 2</div>
<div class="button">Show more</div>
</div>
<div class="truncate">
<div class="result">Result 1</div>
<div class="result">Result 2</div>
<div class="button">Show more</div>
</div>
<div class="truncate">
<div class="result">Result 1</div>
<div class="result">Result 2</div>
<div class="button">Show more</div>
</div>
You should replace the id of Show more to class.
And use the following code.
collapse =document.getElementsByClassName('button');
for(let i = 0; i < collapse.length; i++){
let oneElement = collapse[i];
oneElement.addEventListener('click', function() {
oneElement.parentElement.classList.toggle("show");
})
}
Parse (loop over) all of your div.truncate elements and give themselves their own functionality via the Accordionize Function.
const Accordionize = el => {
const results = el.querySelectorAll('.result')
const toggleButton = el.querySelector('button')
let open = false
const _toggle = () => {
const action = open ? 'remove' : 'add'
results.forEach(item => item.classList[action]('show'))
open = !open
}
toggleButton.addEventListener('click', _toggle)
}
// get all div.truncate and apply them to their own instance of Accordionize
document.querySelectorAll('.truncate').forEach(Accordionize)
.truncate {
border: 2px solid red;
margin: 5
}
.result {
display: none;
}
.show {
display: block
}
<div class="truncate">
<div class="result">Result 1</div>
<div class="result">Result 2</div>
<button>Show more1</button>
</div>
<div class="truncate">
<div class="result">Result 3</div>
<div class="result">Result 4</div>
<button>Show more2</button>
</div>
<div class="truncate">
<div class="result">Result 5</div>
<div class="result">Result 6</div>
<button>Show more3</button>
</div>
<div class="truncate">
<div class="result">Result 7</div>
<div class="result">Result 8</div>
<button>Show more4</button>
</div>
My situation is the following: I have several classes with names obj1, obj2, ob3 and an output object. When one of obj# is clicked, i want a corresponding text to be added in output object. Here is my code:
<div class="obj obj1">Object 1</div>
<div class="obj obj2">Object 2</div>
<div class="obj obj3">Object 3</div>
<div class="output"></div>
And JavaScript:
function whichObject($obj) {
for (i = 0; i < 3; i++) {
switch($obj.hasClass("obj" + (i+1).toString())) {
case "obj1":
$(".output").text("Object 1");
break;
case "obj2":
$(".output").text("Object 2");
break;
case "obj3":
$(".output").text("Object 3");
break;
}
}
$(".obj").on("click", function () {
whichObject($(this));
});
I need the loop because the number of obj# objects may increase over time and I cannot predict a certain amount of obj# objects.
I'm surely missing something or the whole approach is wrong.
$('.obj').on('click', function(e){
$('.output').text(e.target.innerHTML);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="obj obj1">Object 1</div>
<div class="obj obj2">Object 2</div>
<div class="obj obj3">Object 3</div>
<div class="output"></div>
So, if somebody is interested I solved it this way:
function whichObject(obj) {
var $obj = $(obj);
if ($obj.attr("id") === "obj1") {
$(".output").text("Object 1");
} else if ($obj.attr("id") === "obj2") {
$(".output").text("Object 2");
} else if ($obj.attr("id") === "obj3") {
$(".output").text("Object 3");
}
}
$(".obj").on("click", function () {
whichObject(this);
});
while adding the following IDs to my HTML:
<div class="obj" id="obj1">Object 1</div>
<div class="obj" id="obj2">Object 2</div>
<div class="obj" id="obj3">Object 3</div>
<div class="output"></div>
I use below script to show dynamic data in a descending order:
var main = document.getElementById( 'container' );
[].map.call( main.children, Object ).sort( function ( a, b ) {
return +b.id.match( /\d+/ ) - +a.id.match( /\d+/ );
}).forEach( function ( elem ) {
main.appendChild( elem );
}
});
With PHP, it shows the following for instance:
<div id="container">
<div id="data-7">data 7</div>
<div id="data-6">data 6</div>
<div id="data-5">data 5</div>
<div id="data-4">data 4</div>
<div id="data-3">data 3</div>
<div id="data-2">data 2</div>
<div id="data-1">data 1</div>
</div>
I want to show only 5 divisions inside <div id="container"> so I tried adding this in the forEeach but it didn't work :
for (i = 0; i < 5; i++) {
}
Any thoughts ?
Just take the first five elements from the array, with .slice:
[].map.call( main.children, Object ).sort(...).slice(0, 5).forEach(...)