Today I have a question: How to change index of elements via plain JS?
I have this example code:
<div class="foo">
<div class="f6x1"></div>
<div class="f6x2"></div>
<div class="f6x3"></div>
</div>
So I want to change order of (for example) first element, like this:
<div class="foo">
<div class="f6x2"></div>
<div class="f6x1"></div>
<div class="f6x3"></div>
</div>
Is there any "non-laging" example how to do that?
You need to select element and the move it before the next element after the second:
var first = document.querySelector('.f6x1'),
next = first.nextElementSibling;
first.parentNode.insertBefore(first, next.nextElementSibling);
<div class="foo">
<div class="f6x1">1</div>
<div class="f6x2">2</div>
<div class="f6x3">3</div>
</div>
Reference:
Element.querySelector
Node.nextElementSibling
Node.insertBefore
A more generic way to order the elements in any order you need:
first, start indexing using data-*-attributes:
<div class="foo">
<div data-index="1" class="f6x1"></div>
<div data-index="2" class="f6x2"></div>
<div data-index="3" class="f6x3"></div>
</div>
Second, use a function to reorder, something like:
var sortElement = document.querySelector('[data-sortable]');
document.querySelector('#buttonblock').addEventListener('click', doSort);
function doSort(e) {
var origin = e.target || e.srcElement;
if (/button/i.test(origin.nodeName)) {
var sortorder = origin.getAttribute('data-order').split(',');
orderElements(sortElement, sortorder);
}
return true;
}
function orderElements(elementRoot, order) {
var placeholder = document.createElement('div');
while(order.length) {
var current = order.shift();
var element = elementRoot.querySelector('[data-index="'+current+'"]');
if (element) {
placeholder.appendChild(element);
}
}
elementRoot.innerHTML = '';
elementRoot.innerHTML = placeholder.innerHTML;
}
<div data-sortable="true" class="foo">
<div data-index="1" class="f6x1">f6x1</div>
<div data-index="2" class="f6x2">f6x2</div>
<div data-index="3" class="f6x3">f6x3</div>
</div>
<div id="buttonblock">
<button data-order="2,1,3">sort 2, 1, 3</button>
<button data-order="3,2,1">sort 3, 2, 1</button>
<button data-order="3,1,2">sort 3, 1, 2</button>
</div>
Related
I wanted to make each element inside myArray will have it's unique action, but I end up with only one of them working.
I have tried one more way of doing it that worked, but it was a complete boilerplate and I'm looking for a better solution than that.
More details:
For each element (Another array of buttons) inside myArray it will have unique action like scrollIntoView of some element in HTML.
In HTML I have 4 divs that share the same class and it looks like that:
<div class='firstDiv'>
<button class="teamBtn"></button>
<button class="serviceBtn"></button>
etc..
</div>
<div class='secondDiv'>
<button class="teamBtn"></button>
<button class="serviceBtn"></button>
etc..
</div>
let aboutSection = document.querySelector('.about')
let serviceSection = document.querySelector('.services')
let teamSection = document.querySelector('.team')
let homeBtn = document.querySelectorAll('.homeBtn');
let aboutBtn = document.querySelectorAll('.aboutBtn');
let serviceBtn = document.querySelectorAll('.serviceBtn')
let teamBtn = document.querySelectorAll('.teamBtn')
let myArray = [];
myArray[0] = homeBtn;
myArray[1] = aboutBtn;
myArray[2] = serviceBtn;
myArray[3] = teamBtn;
myArray.forEach(el => {
addEventListener('click', () => {
teamBtn.forEach(() => {
teamSection.scrollIntoView();
});
serviceBtn.forEach(() => {
serviceSection.scrollIntoView();
});
})
})
You really want delegation from a container wrapping ALL divs. Then only one event handler is needed for all buttons
document.getElementById("nav").addEventListener("click",function(e) {
const tgt = e.target.closest("button")
if (tgt && (tgt.classList.contains("teamBtn") ||tgt.classList.contains("serviceBtn"))) {
document.getElementById(tgt.dataset.id).scrollIntoView();
}
})
section div {
height: 500px;
background-color: red;
border: 1px solid black;
}
<nav id="nav">
<div class="firstDiv">
<button class="teamBtn" data-id="team1">Team 1</button>
<button class="serviceBtn" data-id="service1">Service 1</button>
</div>
<div class="secondDiv">
<button class="teamBtn" data-id="team2">team 2</button>
<button class="serviceBtn" data-id="service2">Service 2</button>
</div>
</nav>
<section id="content1">
<div id="team1">Team 1</div>
<div id="service1">Service 1</div>
</section>
<section id="content2">
<div id="team2">Team 2</div>
<div id="service2">Service 2</div>
</section>
I would like to sort a few divs in ascending order based on their data-id. How can I do that?
<div class="container" data-id="1000">
<div id="H1"></div>
<div id="sub">sub 1</div>
<div id="sub">sub 2</div>
</div>
<div class="container" data-id="3000">
<div id="H1"></div>
<div id="sub"></div>
<div id="sub"></div>
</div>
<div class="container" data-id="2000">
<div id="H1"></div>
<div id="sub"></div>
<div id="sub"></div>
</div>
I've found the solution to my problem a while ago:
function sortOut() {
// get the classname chapcontainer
var classname = document.getElementsByClassName('container');
// create a variable and put the classes it into an array.
var divs = [];
for (var i = 0; i < classname.length; ++i) {
divs.push(classname[i]);
}
// Sort the divs based on data-id.
divs.sort(function(a, b) {
return +a.getAttribute("data-id") - +b.getAttribute("data-id");
});
};
divs.sort does the trick. More info about this function can be found here:
https://www.w3schools.com/jsref/jsref_sort.asp
My html is like below:
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_1" />
</div>
</div>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_2" />
</div>
</div>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_3" />
</div>
</div>
Using jquery, I want to create an object like following from the above HTML:
[
{
position :1,
url: "https://link_to_Item_1"
},
{
position :"2",
url: "https://link_to_Item_1"
},
{
position :"3",
url: "https://link_to_Item_2"
}
]
I tried something like below:
var myData = [];
var myDataElements = {};
$('.list-item').find('a').each(function(index, ele){
myDataElements.position = index;
myDataElements.url = $(this).attr('href');
myData.push(myDataElements);
});
But the result was the position and URL became the same for all elements in myData.
The issue is you are declaring the object outside. But you need the object in each iteration, declare that inside the function:
var myData = [];
$('.list-item').find('a').each(function(index, ele){
var myDataElements = {};
myDataElements.position = index + 1;
myDataElements.url = $(this).attr('href');
myData.push(myDataElements);
});
console.log(myData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_1" />
</div>
</div>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_2" />
</div>
</div>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_3" />
</div>
</div>
You can also use jQuery's .map() and .get() like the following way:
var myData = $('.details').map(function(i, el){
return {
position: i+1,
url: $(el).find('a').attr('href')
}
}).get();
console.log(myData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_1" />
</div>
</div>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_2" />
</div>
</div>
<div class="list-item">
<div class="details">
<a href="https://link_to_Item_3" />
</div>
</div>
You need to initialize the variable myDataElements inside the loop :
var myDataElements = {};
And make sure you're closing the anchor tag well like :
Instead of :
<a href="https://link_to_Item_1" />
NOTE: You can increase the index by 1 if you want to start counting from 1 instead of zero since the index is zero-based.
var myData = [];
$('.list-item a').each(function(index, ele) {
var myDataElements = {};
myDataElements.position = index + 1;
myDataElements.url = $(this).attr('href');
myData.push(myDataElements);
});
console.log(myData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="list-item">
<div class="details">
</div>
</div>
<div class="list-item">
<div class="details">
</div>
</div>
<div class="list-item">
<div class="details">
</div>
</div>
myDataElements here pointing to the same object. And with each iteration you are basically changing the value of its 2 properties, position and url and then pushing it to the array. All array elements of myData are same object which you created here var myDataElements = {};. That's is why you ended up with same objects in myData.
What you want here is:
myData.push(Object.assign({}, myDataElements);
// or myData.push({...myDataElements});
Now you are creating a new object by copying the myDataElements before pushing it it the array myData. This gives you the desired output.
I have the following html:
<div id="prog" class="downloads clearfix">
<div class="item">
<div class="image_container">
<img src="/img/downloads/company.png" width="168" height="238" alt="">
</div>
<div class="title">
pricelist: <label id="pr1"></label>
</div>
<div class="type">
pdf document
</div>
<div class="link">
<a id="pdfdocument" class="button" target="_blank" href="#">start Download </a>
</div>
</div>
</div>
I want build HTML which is inside the <div id="prog"> with Javascript:
<div id="prog" class="downloads clearfix"></div>
I'm trying to use this Javascript, but without success:
var tmpDocument, tmpAnchorTagPdf, tmpAnchorTagXls, parentContainer, i;
parentContainer = document.getElementById('prog');
for (i = 0; i < documents.length; i++) {
tmpDocument = documents[i];
tmpAnchorTagPdf = document.createElement('a id="pdfdocument" ');
tmpAnchorTagPdf.href = '/role?element=' + contentElement.id + '&handle=' + ope.handle;
tmpAnchorTagPdf.innerHTML = 'start Download';
tmpAnchorTagXls = document.createElement('a');
tmpAnchorTagXls.href = '/role?element=' + contentElement.id + '&handle=' + ope.handle;
tmpAnchorTagXls.innerHTML = 'start Download';
parentContainer.appendChild(tmpAnchorTagPdf);
parentContainer.appendChild(tmpAnchorTagXls);
}
If this is a section of code that you will be using more than once, you could take the following approach.
Here is the original div without the code you want to create:
<div id="prog" class="downloads clearfix">
</div>
Create a template in a hidden div like:
<div id="itemtemplate" style="display: none;">
<div class="item">
<div class="image_container">
<img src="/img/downloads/company.png" width="168" height="238" alt="">
</div>
<div class="title">
pricelist: <label></label>
</div>
<div class="type">
pdf document
</div>
<div class="link">
<a class="button" target="_blank" href="#">start Download </a>
</div>
</div>
</div>
Then duplicate it with jquery (OP originally had a jquery tag; see below for JS), update some HTML in the duplicated div, then add it to the document
function addItem() {
var item = $("#itemtemplate div.item").clone();
//then you can search inside the item
//let's set the id of the "a" back to what it was in your example
item.find("div.link a").attr("id", "pdfdocument");
//...the id of the label
item.find("div.title label").attr("id", "pr1");
//then add the objects to the #prog div
$("#prog").append(item);
}
update
Here is the same addItem() function for this example using pure Javascript:
function JSaddItem() {
//get the template
var template = document.getElementById("itemtemplate");
//get the starting item
var tempitem = template.firstChild;
while(tempitem != null && tempitem.nodeName != "DIV") {
tempitem = tempitem.nextSibling;
}
if (tempitem == null) return;
//clone the item
var item = tempitem.cloneNode(true);
//update the id of the link
var a = item.querySelector(".link > a");
a.id = "pdfdocument";
//update the id of the label
var l = item.querySelector(".title > label");
l.id = "pr1";
//get the prog div
var prog = document.getElementById("prog");
//append the new div
prog.appendChild(item);
}
I put together a JSFiddle with both approaches here.
Let's say I have an HTML structure like this:
<li id="jkl">
<div class="aa">
<div class="bb">
<div class="cc">
<div class="dd">
<a ...><strong>
<!-- google_ad_section_start(weight=ignore) -->Test
<!-- google_ad_section_end --></strong></a>
</div>
</div>
</div>
<div class="ee">
<div class="ff">
<div class="gg">
<div class="excludethis">
<a...>Peter</a>
</div>
</div>
</div>
</div>
</div>
</li>
My goal is to set the content(innerhtml) of <li id="jkl"> to '' if inside of <li id="jkl"> there is any word of a list of words(In the example below, Wortliste) except when they are in <div class="excludethis">.
In other words, ignore <div class="excludethis"> in the checking process and show the html even if within <div class="excludethis"> there are one or more words of the word list.
What to change?
My current approach(that does not check for <div class="excludethis">)
Wortliste=['Test','Whatever'];
TagListe=document.selectNodes("//li[starts-with(#id,'jk')]");
for (var Durchgehen=TagListe.length-1; Durchgehen>=0; Durchgehen--)
{
if (IstVorhanden(TagListe[Durchgehen].innerHTML, Wortliste))
{
TagListe[Durchgehen].innerHTML = '';
}
}
with
function IstVorhanden(TagListeElement, Wortliste)
{
for(var Durchgehen = Wortliste.length - 1; Durchgehen>=0; Durchgehen--)
{
if(TagListeElement.indexOf(Wortliste[Durchgehen]) != -1)
return true;
}
return false;
}
Only has to work in opera as it's an userscript.