This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I made an animation with jquery and I find myself with 15400 line of code.
I have 26 div (images) with IDs: .article1, article2, .article3, .article4, ... article26.
When I click on one of them lot of translation will be applied on the others.
I want to decrease the number of code lines, i tried a For loop:
for (var i = 1 ; i<=26 ; i++)
{
$('.article' + i]).click(function(){
-- animations --
}
}
But it seems don't work because the value of i will take the last value of the loop (26) so the click function will work only on the div with id .article26.
Thank you.
this could be a way to manage an animation over many different named elements...
function AnimationCtrl($) {
var els = $('[class^="article"]');
els.click(function(event) {
$(this).toggleClass('is-active');
});
}
jQuery(document).ready(AnimationCtrl);
[class^="article"] {
padding: .5em 1em;
background: cyan;
border: 1px solid lightseagreen;
display: inline-block;
margin: .2em .5em;
cursor: pointer;
transition: 500ms all linear;
}
.is-active[class^="article"] {
background: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="article-1">elemnt number 1</div>
<div class="article-2">elemnt number 2</div>
<div class="article-3">elemnt number 3</div>
<div class="article-4">elemnt number 4</div>
<div class="article-5">elemnt number 5</div>
<div class="article-6">elemnt number 6</div>
<div class="article-7">elemnt number 7</div>
<div class="article-8">elemnt number 8</div>
<div class="article-9">elemnt number 9</div>
<div class="article-10">elemnt number 10</div>
<div class="article-11">elemnt number 11</div>
<div class="article-12">elemnt number 12</div>
<div class="article-13">elemnt number 13</div>
<div class="article-14">elemnt number 14</div>
<div class="article-15">elemnt number 15</div>
<div class="article-16">elemnt number 16</div>
<div class="article-17">elemnt number 17</div>
<div class="article-18">elemnt number 18</div>
<div class="article-19">elemnt number 19</div>
<div class="article-20">elemnt number 20</div>
<div class="article-21">elemnt number 21</div>
<div class="article-22">elemnt number 22</div>
<div class="article-23">elemnt number 23</div>
<div class="article-24">elemnt number 24</div>
<div class="article-25">elemnt number 25</div>
<div class="article-26">elemnt number 26</div>
Related
Description
I want to call a function after the javascript .scroll() function has finished.
Code
The interesting part is the javascript:
const vertScroll = document.getElementById('boxSlider');
$("#btnLeft").on("click", () => {
vertScroll.scroll({
left: vertScroll.scrollLeft - 120,
behavior: "smooth"
});
//This should print out the new value of
//vertScroll but instead always prints the value
//before scroll()
console.log(vertScroll.scrollLeft);
});
$("#btnRight").on("click", () => {
vertScroll.scroll({
left: vertScroll.scrollLeft + 120,
behavior: "smooth"
});
//same as above
console.log(vertScroll.scrollLeft);
});
.boxSlider {
width: 100%;
overflow-x: auto;
white-space: nowrap;
display: inline-block;
}
.box {
display: inline-block;
background-color: cyan;
width: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="boxSlider" class="boxSlider">
<div class="box">Box 1</div>
<div class="box">Box 2</div>
<div class="box">Box 3</div>
<div class="box">Box 4</div>
<div class="box">Box 5</div>
<div class="box">Box 6</div>
<div class="box">Box 7</div>
<div class="box">Box 8</div>
<div class="box">Box 9</div>
<div class="box">Box 10</div>
</div>
<input id="btnLeft" type="Button" value="Scroll left"/>
<input id="btnRight" type="Button" value="Scroll right" />
Expected Behaviour
console.log(...) should output the scroll value after the scroll() function has completed.
Not the answer
Don't just do console.log(scrollLeft - 120) (or +120) since I want to implement more complex code there and the console.log() is just for better understanding.
What I've tried
I've looked at the scroll() docs (mozilla scroll()) and it doesn't seem to have a callback function.
Question
How can I achieve the expected behaviour?
TIA
I have a list of DIVs that shows a contact list, some of these DIVs randomly might have a specific online class name.
I want to change the translateY value of the transform style property to push the online contacts to the top above the others.
My current scenario is like:
<div id="list">
<div class="contact" style="transform: translateY(0px)">Contact 1</div>
<div class="contact" style="transform: translateY(50px)">Contact 2</div>
<div class="contact online" style="transform: translateY(100px)">Contact 3</div>
<div class="contact" style="transform: translateY(150px)">Contact 4</div>
<div class="contact online" style="transform: translateY(200px)">Contact 5</div>
<div class="contact online" style="transform: translateY(250px)">Contact 6</div>
<div class="contact" style="transform: translateY(300px)">Contact 7</div>
</div>
What should happen after running my pure JavaScript function is pushing contact 3, 5 and 6 to the top above the orders, and the new DOM should be like:
<div id="list">
<div class="contact" style="transform: translateY(150px)">Contact 1</div>
<div class="contact" style="transform: translateY(200px)">Contact 2</div>
<div class="contact online" style="transform: translateY(0px)">Contact 3</div>
<div class="contact" style="transform: translateY(250px)">Contact 4</div>
<div class="contact online" style="transform: translateY(50px)">Contact 5</div>
<div class="contact online" style="transform: translateY(100px)">Contact 6</div>
<div class="contact" style="transform: translateY(300px)">Contact 7</div>
</div>
Now, I should see the contact list reordered as the DIVs that have the online class have a new translateY value.
Here is what I tried:
var contacts = document.getElementsByClassName("online");
for(var i = 0; i < contacts.length; i++)
{
var current = contacts[i].style.transform;
contacts[i].style.transform = "translateY(0px)";
var mainElements = document.getElementsByClassName("contact");
mainElements[i].style.webkitTransform = current;
mainElements[i].style.MozTransform = current;
mainElements[i].style.msTransform = current;
mainElements[i].style.OTransform = current;
mainElements[i].style.transform = current;
console.log('current element transform', current);
console.log('current element transform after change', slides[i].style.transform);
console.log('first element', mainElements[i].style.transform);
//value is not changing to the new value, it remains 0 --^
}
I think the code is clear, that is what I tried for a long time to do, I know I am doing something wrong but I couldn't find where exactly.
Note: I know there are lots of other ways to reorder the items, like moving the whole node or with using jQuery or other stuff, but for some reasons I don't want any other method different that the one I am using, pure JavaScript and changing translateY value (Just in case someone will try to advice me with other methods).
I hope I can find some help here.
Thanks.
It can be done way easier without translate, but since you said in other answer you have to stick to the translate, here is function for you
function orderByOnline() {
// Select all contants
const allContacts = document.querySelectorAll(".contact");
// Prepare arrays where you will add online and offline contacts
let onlineContacts = [], notOnlineContacts = [];
// Use spread operator on query selector and use filter function
// to check if contact has class online - means he is online
[...allContacts].filter(function(contact) {
if (contact.classList.contains("online")) {
onlineContacts.push(contact);
} else {
notOnlineContacts.push(contact)
}
});
// Now parse throu online contacts, start from 0 because variable
// i will be used to determine translate value
for (let i = 0; i < onlineContacts.length; i++) {
const translateValue = i * 50;
onlineContacts[i].style.transform = `translateY(${translateValue}px)`;
}
// Sort offline contacts, but starting point has to be length of online contacts
// so you can get proper values
for (let i = onlineContacts.length; i < notOnlineContacts.length; i++) {
const translateValue = i * 50;
notOnlineContacts[i].style.transform = `translateY(${translateValue}px)`;
}
}
Try following js
let d= document.querySelector('#list').children;
let ds=[...d].sort((a,b)=> [...b.classList].includes('online') ? 1 : -1 )
ds.map( (x,i)=> x.style.transform=`translateY(${i*50}px)`)
#list div {
position: absolute; /* I add this style to prettify output */
}
<div id="list">
<div class="contact" style="transform: translateY(0px)">Contact 1</div>
<div class="contact" style="transform: translateY(50px)">Contact 2</div>
<div class="contact online" style="transform: translateY(100px)">Contact 3</div>
<div class="contact" style="transform: translateY(150px)">Contact 4</div>
<div class="contact online" style="transform: translateY(200px)">Contact 5</div>
<div class="contact online" style="transform: translateY(250px)">Contact 6</div>
<div class="contact" style="transform: translateY(300px)">Contact 7</div>
</div>
Or remove translate and use pure CSS approach
#list {
display: flex;
flex-direction: column;
}
.contact {
margin-bottom: 30px;
order: 2;
}
.online {
order:1
}
<div id="list">
<div class="contact" >Contact 1</div>
<div class="contact" >Contact 2</div>
<div class="contact online">Contact 3</div>
<div class="contact" >Contact 4</div>
<div class="contact online" >Contact 5</div>
<div class="contact online" >Contact 6</div>
<div class="contact">Contact 7</div>
</div>
I need to create a layout in which you have three columns, in each column the corresponding row needs to have the same height. The requirements for this are.
Three columns
Inside each column there are multiple rows.
Each corresponding row must be the same height.
On mobile the columns will turn into a slider.
I've considered 2 layout options for this.
target each row in each column and set the height with Javascript
<div class="container">
<div class="col">
<div class="row">row 1</div>
<div class="row">row 2</div>
<div class="row">row 3</div>
</div>
<div class="col">
<div class="row">row 1</div>
<div class="row">row 2</div>
<div class="row">row 3</div>
</div>
<div class="col">
<div class="row">row 1</div>
<div class="row">row 2</div>
<div class="row">row 3</div>
</div>
</div>
This layout is ideal as I can target the column and add the drop shadow styles without having to target each row and apply to each one. Implementing the slider on mobile would be easier as there is three container columns. But, looping through each column and getting the corresponding row and updating the height on each one seems like a lot of work especially since the height will need to be recalculated every time there is an api call (there's filter above with allow you to update the data).
Use flex
<div class="container">
<div class="row">
<div class="col">
row 1 col 1
</div>
<div class="col">
row 1 col 2
</div>
<div class="col">
row 1 col 3
</div>
</div>
<div class="row">
<div class="col">
row 2 col 1
</div>
<div class="col">
row 2 col 2
</div>
<div class="col">
row 2 col 3
</div>
</div>
<div class="row">
<div class="col">
row 3 col 1
</div>
<div class="col">
row 3 col 2
</div>
<div class="col">
row 3 col 3
</div>
</div>
</div>
This approach is appealing because I don't need to use JS to set the height of each corresponding row in each column. But, i'll need to set the individual styles like drop shadows to each row which means i'll need to use all sorts of trickery so as not to apply the drop shadow to the top and bottom of each row, there's also the risk that this will not display correctly in different browsers (needs to go back as far as IE 10). Also the only slider effect i'll be able to active is overflow scroll which doesn't fit the requirments.
So, my questions are, are there other options i'm not considering? What would you do?
I would consider option 1 for the following reasons:
Your markup is semantically correct (3 columns, each with some rows) and clean
Your column styles can easily be applied
Looping through all rows just to calculate the maximum height isn't that cumbersome especially if you only have a few rows.
A solution with vanilla JavaScript could look like the following. You can wrap the code into a function and call it, whenever your filter is applied:
var rows = document.querySelectorAll(".col:nth-child(1) > .row").length, n
for (n = 1; n <= rows; ++n) {
var cells = document.querySelectorAll('.row:nth-child(' + n + ')')
var maxHeight = 0, i
for (i = 0; i < cells.length; ++i) {
maxHeight = (cells[i].clientHeight > maxHeight) ? cells[i].clientHeight : maxHeight
}
cells.forEach(function(cell){
cell.style.height = Number(maxHeight-10) + 'px'
})
}
.col {
display: inline-block;
width: calc(33% - 41px);
margin: 10px 10px;
padding: 10px;
border-radius: 15px;
box-shadow: 0 0 20px #aaa;
vertical-align: top;
}
.row {
border-top: 3px solid black;
margin: 5px 0;
padding: 5px;
}
<div class="container">
<div class="col">
<div class="row">row 1 Compare text 1</div>
<div class="row">row 2</div>
<div class="row">row 3</div>
</div>
<div class="col">
<div class="row">row 1 Compare text 1 this text is longer</div>
<div class="row">row 2</div>
<div class="row">row 3 with a little more text</div>
</div>
<div class="col">
<div class="row">row 1 Compare text 1 this text is even much much longer</div>
<div class="row">row 2</div>
<div class="row">row 3</div>
</div>
</div>
Trying to figure out somekind of logic that will let me distribute elements in a sequenced order. What size and position should be randomly generated within a given range (for example, element must be within viewport and the size of the element should be within a defined range such as minimum 10% of viewport, maximum 60%).
I'm not sure what would be the best way to approach something like this, any ideas?
I've attached a sketch below of what it could look like. The layout would be different at each load.
Markup
<div class="container">
<div class="post">Post 1</div>
<div class="post">Post 2</div>
<div class="post">Post 3</div>
<div class="post">Post 4</div>
<div class="post">Post 5</div>
<div class="post">Post 6</div>
<div class="post">Post 7</div>
<div class="post">Post 8</div>
<div class="post">Post 9</div>
<div class="post">Post 10</div>
<div class="post">Post 11</div>
<div class="post">Post 12</div>
<div class="post">Post 13</div>
<div class="post">Post 14</div>
<div class="post">Post 15</div>
</div>
Thanks, this was fun to code. I set some defaults in CSS and then did the logic in JS. Note that the buffer() function is nonlinear; we want most of the buffers to be close to zero and very few of them to be on the larger side, so we use powers of e (2.7⁰/12 = 0.08em to 2.7⁶/12 = 33.62em) for the scale and then multiply them out to get a bigger range of numbers (rather than powers of two divided by 12).
I assume the elements are already chronologically ordered. If not, that shouldn't be hard to do, just sort posts[] and insert them in order using appendChild() on the container.
var defaultMax = Math.exp(6) / 12; // e⁶ / 12 = 33.5em
// random buffer to give for spacing.
// growth is inverse exponential, so larger is less likely
function buffer(min=0.1, max=defaultMax, mult=1) {
return Math.min(max, Math.max(min,
min / 2 + Math.exp(Math.random() * 6) * Math.random() * mult / 12
))+"em";
}
function randomize() {
var posts = document.getElementsByClassName("posts");
for (var p = 0; p < posts.length; p++) {
// random buffered margins, ordered: top right bottom left.
// top is at least 0.1em, right and bottom are at least 0.25em.
// top and bottom are cut in half to limit lost vertical space.
posts[p].style.margin = buffer(0.1, defaultMax, 0.5) + " "
+ buffer(0.25) + " "
+ buffer(0.25, defaultMax, 0.5) + " "
+ buffer();
// random width and height (with sane minimum size: 8em x 5em)
posts[p].style.width = buffer(8);
posts[p].style.height = buffer(5);
}
}
.posts { float:left; background:#000; color:#fff;
padding:0.2em; text-align:center; }
.container { width:50em; max-width:100%; }
<body onload="randomize()">
<div class="container">
<div class="posts">Post 1</div>
<div class="posts">Post 2</div>
<div class="posts">Post 3</div>
<div class="posts">Post 4</div>
<div class="posts">Post 5</div>
<div class="posts">Post 6</div>
<div class="posts">Post 7</div>
<div class="posts">Post 8</div>
<div class="posts">Post 9</div>
<div class="posts">Post 10</div>
<div class="posts">Post 11</div>
<div class="posts">Post 12</div>
<div class="posts">Post 13</div>
<div class="posts">Post 14</div>
<div class="posts">Post 15</div>
</div>
</body>
So I've used Adams code as a base. So HTML and CSS stay the same:
HTML
<body onload="randomize()">
<div class="container">
<div class="posts">Post 1</div>
<div class="posts">Post 2</div>
<div class="posts">Post 3</div>
<div class="posts">Post 4</div>
<div class="posts">Post 5</div>
<div class="posts">Post 6</div>
<div class="posts">Post 7</div>
<div class="posts">Post 8</div>
<div class="posts">Post 9</div>
<div class="posts">Post 10</div>
<div class="posts">Post 11</div>
<div class="posts">Post 12</div>
<div class="posts">Post 13</div>
<div class="posts">Post 14</div>
<div class="posts">Post 15</div>
</div>
</body>
CSS:
.posts { float:left; background:#000; color:#fff; padding:0.2em; text-align:center; }
.container { width:50em; max-width:100%; }
JavaScript
Will change however. Update any of the properties in the options variable to change layout.
var options = {
width: {
min: 10,
max: 60,
unit: '%'
},
height: {
min: 10,
max: 30,
unit: '%'
},
margin: {
min: 5,
max: 10,
unit: 'px'
}
}
function getRandomInt (min, max, unit) {
return Math.floor(Math.random() * (max - min + 1)) + min + unit;
}
function randomize() {
var posts = document.getElementsByClassName("posts");
for (var p = 0; p < posts.length; p++) {
posts[p].style.margin = getRandomInt(options.margin.min,options.margin.max, options.margin.unit) + " " + getRandomInt(options.margin.min,options.margin.max, options.margin.unit);
posts[p].style.width = getRandomInt(options.width.min,options.width.max, options.width.unit);
posts[p].style.height = getRandomInt(options.height.min,options.height.max, options.height.unit);
}
}
I've created a fiddle: http://jsfiddle.net/qsUw7/
I'm trying to make a simple hard coded paging system via html and javascript.
I have given each element an id PM-1, PM-2, PM-3, etc and each page will list 10 of these items.
(I know this is a very inconvenient paging system but it's just for experimental purposes.)
So. my code html is as listed below -
<div id="PM-22">item 1</div>
<div id="PM-21">item 2</div>
<div id="PM-20">item 3</div>
<div id="PM-19">item 4</div>
<div id="PM-18">item 5</div>
<div id="PM-17">item 6</div>
<div id="PM-16">item 7</div>
<div id="PM-15">item 8</div>
<div id="PM-14">item 9</div>
<div id="PM-13">item 10</div>
<div id="PM-12">item 11</div>
<div id="PM-11">item 12</div>
<div id="PM-10">item 13</div>
<div id="PM-9">item 14</div>
<div id="PM-8">item 15</div>
<div id="PM-7">item 16</div>
<div id="PM-6">item 17</div>
<div id="PM-5">item 18</div>
<div id="PM-4">item 19</div>
<div id="PM-3">item 20</div>
<div id="PM-2">item 21</div>
<div id="PM-1">item 22</div>
<span style="text-align:right;"><p>Page 1 2 3</p></span>
And my javascript function as as follows -
<script type="text/javascript">
function PMPaging(num,pg) {
pg *= 10;
var upperlim = num - pg - 10;
var lowerlim = upperlim - 10;
if(lowerlim < 0) { lowerlim =0;}
for(num; num > 0; num--) {
document.getElementById('PM-'+num).style.display = 'none';
while (num <= upperlim && num > lowerlim) {
document.getElementById('PM-'+num).style.display = 'block';
num--;
}
}
}
</script>
Assume first 10 items are showing only on page load and the rest are hidden - Now whenever I run this code, it does show the first 10 items only, but when i click page 2 or 3 nothing happens, and if I click page 1 it shows the last 2 items? wtf? lol, first page is id number "22-13" and second page is "12-2", third page should be "2-1"..Thanks!
Is there a reason you aren't using the JQuery Pagination Plugin? Have a look at the demonstration.
If you need to be able to link to a specific page, have a look at this answer.
I get your point. You can use this modified script for the same purpose. I hope it helps. (You don't have to change your html part.
<script type="text/javascript">
function PMPaging(num,pg) {
pg *= 10;
var upperlim = pg+1;
var lowerlim = upperlim - 10;
if(lowerlim < 0) { lowerlim =1;}
for(i=1; i <= num; i++) {
if(i<=upperlim && i>=lowerlim){
document.getElementById('PM-'+i).style.display = 'block';
}else{
document.getElementById('PM-'+i).style.display = 'none';
}
}
}
</script>
surround your items with this div:
<div class="itms">
<div id="PM-22">item 1</div>
<div id="PM-21">item 2</div>
<div id="PM-20">item 3</div>
<div id="PM-19">item 4</div>
<div id="PM-18">item 5</div>
<div id="PM-17">item 6</div>
<div id="PM-16">item 7</div>
<div id="PM-15">item 8</div>
<div id="PM-14">item 9</div>
<div id="PM-13">item 10</div>
<div id="PM-12">item 11</div>
<div id="PM-11">item 12</div>
<div id="PM-10">item 13</div>
<div id="PM-9">item 14</div>
<div id="PM-8">item 15</div>
<div id="PM-7">item 16</div>
<div id="PM-6">item 17</div>
<div id="PM-5">item 18</div>
<div id="PM-4">item 19</div>
<div id="PM-3">item 20</div>
<div id="PM-2">item 21</div>
<div id="PM-1">item 22</div>
</div>
<span style="text-align:left;"><p>Page 1 2 3</p></span>
then use this code for JS:
function Paginate(itemsPerPage) {
var items = document.querySelectorAll(".itms div"),
iL = items.length || 0;
this.turnPage = function(pageNum) {
var startItem = (pageNum*itemsPerPage) - itemsPerPage;
for (var i = 0; i < iL; i++) {
items[i].style.display = (startItem <= i && i < (startItem + itemsPerPage)) ? "block" : "none";
}
}
}
var P = new Paginate(10);//10 items per page
to turn pages, use:
P.turnPage(2); //2 for the page Number