I have multiple groups of divs that I need to apply a random class to with no repeats in each of the groups. Once I've done this, I then need to 'reset' the Array back to the original values, and move onto the next group of divs where I apply a random class to each div again.
The idea is to get to something that looks like this:
<div class="mini-thumbnail-container">
<div class="mini-thumbnail-individual-image left">
<div class="mini-thumbnail-individual-image centre">
<div class="mini-thumbnail-individual-image right">
</div>
<div class="mini-thumbnail-container">
<div class="mini-thumbnail-individual-image centre">
<div class="mini-thumbnail-individual-image right">
<div class="mini-thumbnail-individual-image left">
</div>
<div class="mini-thumbnail-container">
<div class="mini-thumbnail-individual-image right">
<div class="mini-thumbnail-individual-image left">
<div class="mini-thumbnail-individual-image centre">
</div>
etc.
I've shamelessly taken code taken from another stackoverflow question to do with this question, but I can't figure out how to get it to work over all my elements, instead of the first three.
Here's the jQuery:
function shuffle(obj) {
var l = obj.length,
i = 0,
rnd,
tmp;
while (i < l) {
rnd = Math.floor(Math.random() * i);
tmp = obj[i];
obj[i] = obj[rnd];
obj[rnd] = tmp;
i += 1;
}
}
var classes = ["centre", "left", "right"];
shuffle(classes);
jQuery(".mini-thumbnail-individual-image").each(function() {
jQuery(this).addClass(classes.pop());
});
And here's a basic outline of my div structure – I have multiples of these which I want to iterate over and apply the random class to each <div class="mini-thumbnail-individual-image">
HTML:
<div class="col-1-6">
<div class="mini-thumbnail-container">
<div class="mini-thumbnail-individual-image">
<img class="mini-thumbnail-image" src="" />
</div>
<div class="mini-thumbnail-individual-image">
<img class="mini-thumbnail-image" src="" />
</div>
<div class="mini-thumbnail-individual-image">
<img class="mini-thumbnail-image" src="" />
</div>
</div>
</div>
I think I need to create a loop that looks is triggered after each iteration over the array, and once it spots the array is empty, I need to push the class names back in, and then loop over the next group of divs, until each group of divs has a random class applied, but maybe there is a simpler way that I haven't thought of yet.
Thanks in advance!
You're on the right track.
Without speaking of methods to optimize the code, here's (one) way to get quickly to where you need to be:
function shuffle(obj) {
var l = obj.length,
i = 0,
rnd,
tmp;
while (i < l) {
rnd = Math.floor(Math.random() * i);
tmp = obj[i];
obj[i] = obj[rnd];
obj[rnd] = tmp;
i += 1;
}
}
// declare OUTSIDE the function for correct scope
var classes;
// Simple function to set up the classes variable and shuffle.
function setUpClasses() {
classes = ["centre", "left", "right"];
shuffle(classes);
}
jQuery(".mini-thumbnail-individual-image").each(function() {
// Check if classes is set / empty. If so, set up the classes again.
if (!classes || classes.length < 1) {
setUpClasses();
}
jQuery(this).addClass(classes.pop());
});
If you want to look at cleaner / briefer ways to shuffle the array, this article has some other techniques. Here's one of them:
yourArray.sort(function() { return 0.5 - Math.random() });
So you could literally remove your shuffle function, and just do this:
function setUpClasses() {
classes = ["centre", "left", "right"];
classes.sort(function() { return 0.5 - Math.random() });
}
Or, if you wanted maximum brevity:
function setUpClasses() {
classes = ["centre", "left", "right"].sort(function() { return 0.5 - Math.random() });
}
Here is a working Fiddle
Related
Apologies if I get the terminology wrong here.
I have a 'grid' of images in html that I want to use jQuery to fade in each element randomly. One item in the grid is a Logo - I want it to fade in last. The grid size could be changed and the position of the 'logo' could also be different. Here is a reduced simplified output of the list.
<ul id="homepage-grid" class="projectsgrid row">
<div id="item1">
</div>
<div id="item2">
</div>
<div id="itemlogo" style="opacity: 0;">
<a href="#" class="block" style="padding-bottom: 100%;">
<div style="background-image:url('logoonly.png')" title="" class="logoblock"></div>
</a>
</div>
<div id="item4">
</div>
<div id="item5">
</div>
</ul>
I have the following script which will collect the elements into an array.
But i can't figure out how to match the element with the 'itemlogo' ID in the collection to split it out and push it to the end of the array so it is last to 'fade in'. I have tried "div#itemlogo", "#itemlogo", "itemlogo" but nothing seems to match, and perhaps not knowing the name of what I am doing I can't find any references.
var elems = $('#homepage-grid > div').get(); // collect elements
console.log(elems);
for (var i = elems.length - 1; i > 1; i--) { // Shuffle the order
var j = Math.floor(Math.random() * (i + 1));
var elem = elems[j];
elems[j] = elems[i];
elems[i] = elem;
}
elms = elems.push(elems.splice(elems.indexOf('div#itemlogo'), 1)[0]); // pull logo to last??
var i = 0;
var timer = setInterval(function() { // animate fade them sequentially
console.log(elems[i]).id();
$(elems[i]).fadeTo( "slow" , 1);
if (i === elems.length) {
clearInterval(timer);
}
i++;
}, 150);
You're on the right path, but the key here is that you need to find a particular item. Those items are DOM elements, not strings or selectors on their own.
elems.push(
elems.splice(
elems.findIndex(node=>node.id === 'itemlogo'),
1
)[0]
);
findIndex allows you to pass a function that should return true for the item you want - in this case, you want the item whose ID is itemlogo. The rest is just the same push-splice thing you have already.
I would also like to praise your correct use of array shuffling. You can simplify it a little bit with destructuring:
[elems[i], elems[j]] = [elems[j], elems[i]];
I am using Selection.js to create a selectable grid on my web page. In order for this to work, it needs to have x amount of divs that create the selectable area.
In my case, I create all the divs with a for loop, to then return it as an array
renderBoxes() {
let boxArr = [];
this.boxSize = this.state.wrapperWidth / this.props.columns;
let length = this.props.columns * this.props.rows;
for (let i = 0; i < length; i++) {
boxArr.push(<Box key={i}/>)
}
document.styleSheets[1].cssRules[0].style.setProperty('width', this.boxSize + "px", null);
document.styleSheets[1].cssRules[0].style.setProperty('height', this.boxSize + "px", null);
this.boxesCreated = true;
return boxArr;
}
When I set the column count to 100 and row count to 100, it takes quite some time to load.
So my question is: how can i generate lots of divs with the least amount of load time?
Render function:
render() {
return (
<div className="col-9 position-relative" id="box-wrapper">
<div className="background-wrapper">
<img src="img/design.jpg" alt=""/>
</div>
<section className="box-wrap green m-0">
<div className="boxes red">
{this.state.wrapperWidth ? this.renderBoxes() : ""}
</div>
<div className="handle py-2">
Drag to increase/decrease vertical box count
</div>
</section>
</div>
)
}
I've found a solution, thanks to #skyboyer. Creating the elements with document.createElement speeds up the process of rendering 100x100 divs by an insane amount.
I have an array of Elements and I want them in document order. I know this is trivial to achieve in XPath, but the logic I have to implement is a bit complicated for a single expression.
I did find Node.compareDocumentPosition(), but it generates a bit mask of quite a few combinations, so not very ideal for a comparator.
As I final resort, I could probably add a random attribute on to all the elements in the array and select them again using XPath, but I'd rather not do that if possible.
I don't necessarily agree that document.compareDocumentPosition() is insufficient for a comparator. Why do you consider this not ideal?
var elementArray = [];
elementArray.push(document.getElementById('div3'));
elementArray.push(document.getElementById('div2'));
elementArray.push(document.getElementById('div4'));
elementArray.push(document.getElementById('div1'));
function documentPositionComparator (a, b) {
if (a === b) {
return 0;
}
var position = a.compareDocumentPosition(b);
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
return -1;
} else if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
return 1;
} else {
return 0;
}
}
console.log('unsorted::', elementArray);
console.log('sorted::', elementArray.sort(documentPositionComparator));
<div id="div1">Div 1
<div id="div2">Div 2
<div id="div3">Div 3</div>
<div id="div4">Div 4</div>
</div>
</div>
The JS below works exactly like it should, my JS knowledge is 0%. Only thing is i need the code below to target multiple divs but same id or different id's does not matter. It also needs to be infinite. so every 50 seconds it needs to repeat. The goal of the js below is to reset CSS3 animations i have running. Project files can be found here: www.dreamsynk.com/img/slider.
// retrieve the element
element = document.getElementById("ani");
setTimeout(function() {
// -> removing the class
element.classList.remove("one");
// -> triggering reflow /* The actual magic */
// without this it wouldn't work. Try uncommenting the line and the transition won't be retriggered.
element.offsetWidth = element.offsetWidth;
// -> and re-adding the class
element.classList.add("one");
}, (50*1000)); //40 seconds
UPDATES:
<div class="slider">
<div class="inner">
<div id="ani" class="one"></div>
<div id="ani" class="two"></div>
<div id="ani" class="three"></div>
<div id="ani" class="four"></div>
<div id="ani" class="five"></div>
<div id="ani" class="six"></div>
</div>
</div>
// retrieve an array-like object with elements
var elements = document.querySelectorAll("ani");
setTimeout(function() {
for (var i=0; i<elements.length; i++) {
var element = elements[i];
element.classList.remove("one two three four five six");
element.offsetWidth = element.offsetWidth;
element.classList.add("one two three four five six");
}
}, (50*1000)); //50 seconds
I am doing something wrong?
You should use document.getElementsByClassName if you want to have an array of elements. document.getElementById normally only gives you the first element of that ID.
The difference between an ID and a class is that an ID can be used to identify one element, whereas a class can be used to identify more than one.
You can also use document.getElementsByName(),
document.getElementsByTagName(),
document.getElementsByTagNameNS() and document.querySelectorAll() of course.
The elements you get from document.getElementsByClassName is known as a HTMLCollection, which is just an array. So if you want to add elements into the collection you can use array.push.
var array = document.getElementsByClassName("classname");
var element = document.getElementById("ani");
array.push(element);
setTimeout(function() {
var i = array.length;
while(i--) {
array[i].classList.remove("one");
array[i].offsetWidth = element.offsetWidth;
array[i].classList.add("one");
}
}, (99999));
Updates:
.classList.remove() can only do 1 class at a time. The same goes
to .classList.add().
What you can do is to create a prototype for DOMTokenList
like this:
DOMTokenList.prototype.addMany = function(classes) {
var array = classes.split(' ');
for (var i = 0, length = array.length; i < length; i++) {
this.add(array[i]);
}
}
and remove/add the classes this way:
element.classList.addMany("one two three four five six");
By the way for .querySelectorAll(selector), you are not using
a proper CSS selector.
You can read more about the syntax of CSS selector here.
And as I have mentioned before, you can't have duplicate ids.
Ids are supposed to be unique.
You can use querySelectorAll to get elements with any css path and then every 50 seconds iterate between them and do the same steps you did for your single element.
Code fixed for your use case:
HTML:
<div class="slider">
<div class="inner">
<div class="ani" id="one"></div>
<div class="ani" id="two"></div>
<div class="ani" id="three"></div>
<div class="ani" id="four"></div>
<div class="ani" id="five"></div>
<div class="ani" id="six"></div>
</div>
</div>
JavaScript:
var container = document.querySelector('slider');
var elements = Array.prototype.slice.call(container.querySelectorAll(".ani"));
setTimeout(function() {
elements.forEach(function(el) { el.classList.remove('ani'); });
container.offsetWidth = container.offsetWidth;
elements.forEach(function(el) { el.classList.add('ani'); });
}, (50*1000)); //50 seconds
// retrieve the elements
var elements = document.querySelectorAll("div");
setInterval(function() {
for(var i = 0; i < elements.length; i++){
// -> removing the class
elements[i].classList.remove("one");
// -> triggering reflow /* The actual magic */
// without this it wouldn't work. Try uncommenting the line and the transition won't be retriggered.
elements[i].offsetWidth = element.offsetWidth;
// -> and re-adding the class
elements[i].classList.add("one");
}
}, (50*1000)); //50 seconds
What I need done is:
Original State:
<div class="shuffledv">
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
</div>
<div class="shuffledv">
<div id="4"></div>
<div id="5"></div>
<div id="6"></div>
</div>
After Shuffle:
<div class="shuffledv">
<div id="2"></div>
<div id="3"></div>
<div id="1"></div>
</div>
<div class="shuffledv">
<div id="5"></div>
<div id="4"></div>
<div id="6"></div>
</div>
The Divs within the first div stay there but get shuffled, and the same happens for the second div with the same class.
To shuffle divs inside a specific div I use something like this:
function shuffle(e) { // pass divs inside #parent to the function
var replace = $('<div>');
var size = e.size();
while (size >= 1) {
var rand = Math.floor(Math.random() * size);
var temp = e.get(rand); // grab a random div from #parent
replace.append(temp); // add the selected div to new container
e = e.not(temp); // remove our selected div from #parent
size--;
}
$('#parent').html(replace.html()); // add shuffled divs to #parent
}
Called lie this: shuffle('#parent .divclass')
Where all Divs with class divclass are inside #parent
I think it should start out something like
function shuffle() {
$(".shuffledv").each(function() {
and then do some form of the original function, but I've just gotten completely lost at this point. I have no idea how to go forward from here. Please let me know if you need anymore info.
Take a look at this jsfiddle. Essentially what we do is for each of the container shuffledv divs we find all children divs and store them in a list, then we remove them from the DOM, e.g.:
$(".shuffledv").each(function(){
var divs = $(this).find('div');
for(var i = 0; i < divs.length; i++) $(divs[i]).remove();
Then I grabbed the Fisher-Yates shuffling algorithm from here to randomise the list of our divs, and finally we append them back to the parent container, like this:
for(var i = 0; i < divs.length; i++) $(divs[i]).appendTo(this);
Hope this helps!
Gave this an initial run through:
(function () {
"use strict";
// Cycle over each .shuffledv HTMLElement
$(".shuffledv").each(function () {
// Remove all divs within, store in $d
var $d = $(this).find("div").remove();
// Sort $d randomnly
$d.sort(function () { return Math.floor(Math.random() * $d.length); });
// Append divs within $d to .shuffledv again
$d.appendTo(this);
});
}());
Demo: http://jsfiddle.net/uYyAH/2/