reorder specific DIVs with "transform" style property - javascript

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>

Related

jQuery: alternate switching of classes in several div blocks

I have 2 identical div blocks on the page, inside of which there are several div blocks with different texts. It looks something like this:
<div class="dynamic_titles">
<div class="dynamic_title active">text 1</div>
<div class="dynamic_title inactive">text 2</div>
<div class="dynamic_title inactive">text 3</div>
<div class="dynamic_title inactive">text 4</div>
<div class="dynamic_title inactive">text 5</div>
</div>
<div class="dynamic_titles">
<div class="dynamic_title active">text 6</div>
<div class="dynamic_title inactive">text 7</div>
<div class="dynamic_title inactive">text 8</div>
<div class="dynamic_title inactive">text 9</div>
<div class="dynamic_title inactive">text 10</div>
</div>
I need to change classes from inactive to active in each dynamic_title block in turn. That is, the next one takes active, and the previous one with active in inactive and so on in a circle.
When I have one dynamic_titles block, everything works fine, but 2 interfere with each other, break. I'm trying not to make 2 functions for each block separately, I want to make one universal one that would be applied to each block separately.
Now I have such a code, but it does not work as expected. It runs once and goes no further. If done without .each, it works, but again not as expected and eventually breaks. What is my mistake?
$('.dynamic_titles').each(function () {
var index = 1,
max = $(this).find('.dynamic_title').length;
function setActiveHeadline() {
setTimeout(function () {
$(this).find('.active').removeClass('active').addClass('inactive');
index++;
if (index > max) {
index = 1;
}
$(this).find('.dynamic_title:nth-of-type(' + index + ')').addClass('active').removeClass('inactive');
setActiveHeadline();
}, 3000);
}
setActiveHeadline();
widthTitle = $(this).find('.dynamic_title.active').width();
$(this).css({
'width': widthTitle,
});
function setWidthHeadlines() {
setTimeout(function () {
widthTitle = $(this).find('.dynamic_title.active').width();
$(this).css({
'width': widthTitle,
});
setWidthHeadlines();
}, 3000);
}
setWidthHeadlines();
})
Thank you for your help!
I think you could simplify this into one function that uses setInterval, please see the following snippet:
$(function(){
//use setInterval to continue the changes
setInterval(() => {
//iterate the blocks
$('div.dynamic_titles').each(function() {
//get the children
const $children = $(this).find('div.dynamic_title');
//get the number of children
const numofchildren = $children.length;
//index for switching active in children
let activeindex = 0;
//iterate children to find the active one
$children.each(function(i, el) {
//if active, remove active and add inactive classNames
//store the index
//break the each loop
if($(el).hasClass('active')){
$(el).removeClass('active').addClass('inactive');
activeindex = i + 1;
return false;
}
});
//if index has reached the last child, reset to zero
if(activeindex > numofchildren - 1){
activeindex = 0;
}
//set the next child to active and remove inactive className
$children[activeindex].classList.add('active');
$children[activeindex].classList.remove('inactive');
});
}, 3000);
})
.active {
color:green;
}
.inactive {
color:grey;
}
<div class="dynamic_titles">
<div class="dynamic_title active">text 1</div>
<div class="dynamic_title inactive">text 2</div>
<div class="dynamic_title inactive">text 3</div>
<div class="dynamic_title inactive">text 4</div>
<div class="dynamic_title inactive">text 5</div>
</div>
<div class="dynamic_titles">
<div class="dynamic_title active">text 6</div>
<div class="dynamic_title inactive">text 7</div>
<div class="dynamic_title inactive">text 8</div>
<div class="dynamic_title inactive">text 9</div>
<div class="dynamic_title inactive">text 10</div>
</div>
<script
src="https://code.jquery.com/jquery-3.6.1.js"
integrity="sha256-3zlB5s2uwoUzrXK3BT7AX3FyvojsraNFxCc2vC/7pNI="
crossorigin="anonymous"></script>

Javascript forEach loop with current index after new element was deleted

I am trying to get the updated index of the forEach loop after an element has been removed from the DOM.
Here is my current script ...
function delete(){
var elm = document.querySelectorAll('.divs');
elm.forEach(function(item, index){
item.addEventListener('click', function(e){
document.querySelectorAll('.divs')[index].remove();
});
}
after the element was removed the calculations for the index are incorrect.
how can I get the index of the clicked item after one or more items has been removed?
I've a very simple solution, but I don't know if you can accept it!
Node list in DOM is always live, if you refer to it as a variable, this variable is active. Check here:
https://developer.mozilla.org/en-US/docs/Web/API/NodeList
I propose you just to hide it!
listDivs1();
deleteDiv1();
function listDivs1() {
let ht = '';
let els = document.querySelectorAll('#method1 .divs');
els.forEach(function(e, index) {
ht += 'index: ' + index + ' - ' + e.innerHTML + '<br>';
});
document.querySelector('.elements1').innerHTML = ht;
}
function deleteDiv1() {
let els = document.querySelectorAll('#method1 .divs');
els.forEach(function(item, index) {
item.addEventListener('click', function(e) {
document.querySelectorAll('.divs')[index].remove();
listDivs1();
});
});
}
listDivs2();
deleteDiv2();
function listDivs2() {
let ht = '';
let els = document.querySelectorAll('#method2 .divs');
els.forEach(function(e, index) {
ht += 'index: ' + index + ' - ' + e.innerHTML + '<br>';
});
document.querySelector('.elements2').innerHTML = ht;
}
function deleteDiv2() {
let els = document.querySelectorAll('#method2 .divs');
els.forEach(function(item, index) {
item.addEventListener('click', function(e) {
e.target.remove();
listDivs2();
});
});
}
listDivs3();
deleteDiv3();
function listDivs3() {
let ht = '';
let els = document.querySelectorAll('#method3 .divs');
els.forEach(function(e, index) {
if (e.style.display !== 'none') {
ht += 'index: ' + index + ' - ' + e.innerHTML + '<br>';
}
});
document.querySelector('.elements3').innerHTML = ht;
}
window.addEventListener('load', listDivs3);
function deleteDiv3() {
let els = document.querySelectorAll('#method3 .divs');
els.forEach(function(item, index) {
item.addEventListener('click', function(e) {
e.target.style.display = 'none';
listDivs3();
});
});
}
.container {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: 1fr;
grid-column-gap: 0px;
grid-row-gap: 0px;
padding: 1em;
}
<h1>your delete</h1>
<div class="container">
<div id="method1">
<div class="divs">div 0</div>
<div class="divs">div 1</div>
<div class="divs">div 2</div>
<div class="divs">div 3</div>
<div class="divs">div 4</div>
<div class="divs">div 5</div>
<div class="divs">div 6</div>
<div class="divs">div 7</div>
<div class="divs">div 8</div>
<div class="divs">div 9</div>
</div>
<div class="elements1"></div>
</div>
<hr>
<h1>e.target</h1>
<div class="container">
<div id="method2">
<div class="divs">div 0</div>
<div class="divs">div 1</div>
<div class="divs">div 2</div>
<div class="divs">div 3</div>
<div class="divs">div 4</div>
<div class="divs">div 5</div>
<div class="divs">div 6</div>
<div class="divs">div 7</div>
<div class="divs">div 8</div>
<div class="divs">div 9</div>
</div>
<div class="elements2"></div>
</div>
<hr>
<h1>display none</h1>
<div class="container">
<div id="method3">
<div class="divs">div 0</div>
<div class="divs">div 1</div>
<div class="divs">div 2</div>
<div class="divs">div 3</div>
<div class="divs">div 4</div>
<div class="divs">div 5</div>
<div class="divs">div 6</div>
<div class="divs">div 7</div>
<div class="divs">div 8</div>
<div class="divs">div 9</div>
</div>
<div class="elements3"></div>
</div>
In the snippet you 2 columns, the one with the divs to click, and second one with index and div content to check after each delete the result.
So with your function, index and div disconnect. With e.target same. With display none, it's working because nothing has been really removed, only "visually" removed. If you have other functions iterating through all those divs you'll have to exclude the display none like in listDivs3 function.
If this display none doesn't suit you, The only way would be:
store all div in temporary variable
remove the one you need
remove the node list
put the divs from the new list
re add your even listener

Wrap divs depends on their classes with pure JavaScript

I have the next code:
<div class="components_container">
<div class="first_group">Element 1</div>
<div class="first_group">Element 2</div>
<div class="first_group">Element 3</div>
<div class="second_group">Element 4</div>
<div class="second_group">Element 5</div>
<div class="second_group">Element 6</div>
</div>
And I would like to wrap both groups in different div tags, like this:
<div class="components_container">
<div class="group1">
<div class="first_group">Element 1</div>
<div class="first_group">Element 2</div>
<div class="first_group">Element 3</div>
</div>
<div class="group2">
<div class="second_group">Element 4</div>
<div class="second_group">Element 5</div>
<div class="second_group">Element 6</div>
</div>
</div>
To do what you are trying to accomplish, you can do this in pure javascript:
Get a reference to the container (this code implies that you have only one element with that class):
var container = document.querySelector(".components_container");
Create the two external divs, add the classes and append them to the container:
var first_cont = document.createElement("div");
var second_cont = document.createElement("div");
first_cont.classList.add("group1");
second_cont.classList.add("group2");
container.appendChild(first_cont);
container.appendChild(second_cont);
Get the divs with a specific class and change their parent (remove child / append child):
var first_elements = container.querySelectorAll(".first_group");
var second_elements = container.querySelectorAll(".second_group");
for (el of first_elements) {
container.removeChild(el);
first_cont.appendChild(el);
}
for (el of second_elements) {
container.removeChild(el);
second_cont.appendChild(el);
}
This will do what you want, but please, next time try to add some more information, like some context, the steps that you tried or where are you finding difficulties :)

How can I hide elements in my list and add a 'show more' feature?

I'm using javascript to build a list of results. I have a for-loop that iterates over some data and creates a mydata div, and adds that to the results div. Let's pretend it looks something like this:
<div id="results">
<div class="mydata">data 1</div>
<div class="mydata">data 2</div>
...
<div class="mydata">data 20</div>
</div>
What I want to do is only display 5 results at a time, and should the user wish to see more, they can click a show next 5 or show more button (or something similar). Any ideas?
Just to clarify, every time the user clicks "show more" I want to 'unhide' the next 5 elements, not ALL the remaining elements. So each click reveals more elements until all are displayed.
You can use the gt() and lt() selectors along with :visible pretty well here.
The following will show the next 5 results on clicking and removes the link once all items are visible.
$('.mydata:gt(4)').hide().last().after(
$('<a />').attr('href','#').text('Show more').click(function(){
var a = this;
$('.mydata:not(:visible):lt(5)').fadeIn(function(){
if ($('.mydata:not(:visible)').length == 0) $(a).remove();
}); return false;
})
);
working example: http://jsfiddle.net/niklasvh/nTv7D/
Regardless of what other people are suggesting here, I would not hide the elements using CSS, but do it in JS instead, because if a user has JS disabled and you hide the elements using CSS, he won't get them visible. However, if he has JS disabled, they will never get hidden, nor will that button appear etc, so it has a full noscript fallback in place + search engines don't like hidden content (but they won't know its hidden if you do it on DOM load).
My solution is here: jsFiddle.
You can put this link somewhere:
show more
and use the following code:
var limit = 5;
var per_page = 5;
jQuery('#results > div.mydata:gt('+(limit-1)+')').hide();
if (jQuery('#results > div.mydata').length <= limit) {
jQuery('#results-show-more').hide();
};
jQuery('#results-show-more').bind('click', function(event){
event.preventDefault();
limit += per_page;
jQuery('#results > div.mydata:lt('+(limit)+')').show();
if (jQuery('#results > div.mydata').length <= limit) {
jQuery(this).hide();
}
});
where limit is your current number of results displayed and per_page is number of results shown with each click on "show more". The link disappears if all the results are displayed. See how it works on jsFiddle.
You can create a CSS class like:
.hiddenData { display: none }
and attach it to any quantity of divs that exceeds 5.
After that make handlers for adding/deleting this class from the needed quantity of divs.
jQuery for class removing:
$(".hiddenData").removeClass("hiddenData")
Create a class with something like:
.hidden_class{
display: none;
}
Add this class to all the mydata div's that you dont want seen.
when the user click the button, remove it from the next 5 div's.
repeat everytime the user clicks the "read more" button
This should work...Let me know how it goes
<script type="text/javascript">
function ShowHide(id) { $("#" + id).toggle(); }
</script>
<div id="results">
<div class="mydata">data 1</div>
<div class="mydata">data 2</div>
<div class="mydata">data 3</div>
<div class="mydata">data 4</div>
<div class="mydata">data 5</div>
<div style="clear:both" onclick="ShowHide('grp6')">More</div>
<div id="grp6" style="display:none">
<div class="mydata">data 6</div>
<div class="mydata">data 7</div>
<div class="mydata">data 8</div>
<div class="mydata">data 9</div>
<div class="mydata">data 10</div>
</div>
<div style="clear:both" onclick="ShowHide('grp11')">More</div>
<div id="grp11" style="display:none">
<div class="mydata">data 11</div>
<div class="mydata">data 12</div>
<div class="mydata">data 13</div>
<div class="mydata">data 14</div>
<div class="mydata">data 15</div>
</div>
</div>
In your forloop, you also have to add these divs hidden container
<div style="clear:both" onclick="ShowHide('grp6')">More</div>
<div id="grp6" style="display:none">
You get the idea.
Here you have:
<style>
/*This hides all items initially*/
.mydata{
display: none;
}
</style>
Now the script
<script>
var currentPage = 1; //Global var that stores the current page
var itemsPerPage = 5;
//This function shows a specific 'page'
function showPage(page){
$("#results .mydata").each(function(i, elem){
if(i >= (page-1)*itemsPerPage && i < page*itemsPerPage) //If item is in page, show it
$(this).show();
else
$(this).hide();
});
$("#currentPage").text(currentPage);
}
$(document).ready(function(){
showPage(currentPage);
$("#next").click(function(){
showPage(++currentPage);
});
$("#prev").click(function(){
showPage(--currentPage);
});
});
</script>
And a sample html:
<div id="results">
<div class="mydata">data 1</div>
<div class="mydata">data 2</div>
<div class="mydata">data 3</div>
<div class="mydata">data 4</div>
<div class="mydata">data 5</div>
<div class="mydata">data 6</div>
<div class="mydata">data 7</div>
<div class="mydata">data 8</div>
<div class="mydata">data 9</div>
<div class="mydata">data 10</div>
<div class="mydata">data 11</div>
<div class="mydata">data 12</div>
</div>
Previous
<span id="currentPage"></span>
Next
The only thing remaining is to validate fot not going to a page lower than 1 and higher than the total. But that will be easy.
EDIT: Here you have it running: http://jsfiddle.net/U8Q4Z/
Hope this helps. Cheers.

Paging javascript

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

Categories

Resources