I have a fairly simple question. I need to create a list of items that is quite deeply nested, something of this sort (in reality every div has like 20+ classes, data-attributes and so forth):
<div class="one" data-foo="a x y z">
<div class="two" data-bar="x y z">
<ul class="three" data-foobar="a b c">
// contents
</ul>
</div>
</div>
My current code is:
const div1 = document.createElement("div");
div1.classList.add("one", all the other classes);
div1.setAttribute("data-foo", "lots of data attributes");
const div2 = document.createElement("div");
div2.classList.add("two", all the other classes);
div2.setAttribute("data-bar", "lots of data attributes");
const div3 = document.createElement("div");
div3.classList.add("three", all the other classes);
div3.setAttribute("data-foobar", "lots of data attributes");
div1.appendChild(div2);
div2.appendChild(div3);
// getting items for the list & other code
div3.appendChild(someOutputData);
I came up with a great idea of using a template literal so I don't have to add all the extra properties, classes etc. I know it seems like I could've fixed it with simple foreach but there are bazillion of other things like aria tags etc. etc.
So my old code becomes:
const wrapper = document.createElement("div");
const layout = `<div class="one" data-foo="a x y z">
<div class="two" data-bar="x y z">
<ul class="three" data-foobar="a b c">
</ul>
</div>
</div>`;
wrapper.innerHTML = layout;
wrapper.appendChild(someOutputData);
Million times clearer, right?
The problem is wrapper.appendChild(someOutputData); is not longer injecting data into .three but into <div> wrapper that is above .one.
Is there some way I can target nested DOM elements created via template literals? How can I push my someOutputData (list of nodes) into .three using the second snippet? Also can I omit wrapper somehow? I don't really need the "div" around my list.
So use variables in your template and select the element you created to append the elements
function createComponent(data1, data2, data3, listData) {
const wrapper = document.createElement("div");
const layout = `<div class="one" data-foo="${data1}">
<div class="two" data-bar="${data2}">
<ul class="three" data-foobar="${data3}">
</ul>
</div>
</div>`;
wrapper.innerHTML = layout;
wrapper.querySelector("ul").append(...listData);
return wrapper;
}
const makeLi = (text) => {
const li = document.createElement('li');
li.textContent = text;
return li;
};
const lis = ['foo', 'bar'].map(makeLi);
const elem = createComponent(1,2,3, lis);
document.body.append(elem);
const lis2 = [1,2,3,4,5].map(makeLi);
const elem2 = createComponent(1,2,3, lis2);
document.body.append(elem2);
It is a bit unclear what you mean.
Perhaps this using map and join?
document.getElementById("container").innerHTML = ["a","b","c","d"]
.map(text => `<div class="one" data-foo="a x y z">
<div class="two" data-bar="x y z">
<ul class="three" data-foobar="a b c">
<li>${text}</li>
</ul>
</div>
</div>`).join("");
<div id="container"></div>
Related
lets say i have a parent-div. And in this div-container, i want to display 5 elements which have all the same structure. For example:
<div class="element">
<p class="name">
</p>
<div class="logo">
</div>
</div>
Is there a way to make an object or prototype out of it, so i dont have to generate every single HTML Element with their classes and src values with the appendChild-function and Dot-Notations in a for-loop?
Im thinking of something like:
for(let i = 0; i<=5;i++){
var element = new element(class,src1,src2 ...);
}
And the "element" is defined in a external class file or something familiar.
Im a beginner, so please show mercy :)
You'll need to clone the node from the template's content. For example:
const templateElement = document.querySelector("#someTemplate")
.content
.querySelector(".element");
// create an Array of nodes (so in memory)
const fiveNodes = [];
for (let i = 0; i < 5; i += 1) {
const nwNode = templateElement.cloneNode(true);
// ^ clone the whole tree
nwNode.querySelector("p.name").textContent += ` #${i + 1}`;
fiveNodes.push(nwNode);
}
// append the nodes to document.body
// this is faster than appending every element in the loop
fiveNodes.forEach(el => document.body.append(el));
<template id="someTemplate">
<div class="element">
<p class="name">I am node</p>
<div class="logo"></div>
</div>
</template>
I'd like to order html elements in the same order as arrayData.
JavaScript
var arrayData = [59, 56, 57];
HTML
<div class="selectize-input">
<div class="item" data-value="56">Dog</div>
<div class="item" data-value="57">Rabbit</div>
<div class="item" data-value="59">Cat</div>
</div>
I want the result like this.
<div class="selectize-input">
<div class="item" data-value="59">Cat</div>
<div class="item" data-value="56">Dog</div>
<div class="item" data-value="57">Rabbit</div>
</div>
Is there any good way to achive this?
Thank you in advance.
[Additional]
I want to get result something like this, but don't now how to apply it to html elements and render on browser.
const orderRule = ['Cat', 'Rabbit', 'Dog', 'Pig', 'Mouse'],
array = ['Mouse','Rabbit', 'Pig', 'Dog', 'Cat'];
const sortArray = [...array].sort((a, b) => orderRule.indexOf(a) - orderRule.indexOf(b));
console.log(sortArray);
You can loop over your array using $.each and then use append to add div at particular position.
Here is demo code :
var arrayData = [59, 56, 57];
//looping through array
$.each(arrayData, function() {
//append div at particular positon using data-value
$(".selectize-input").append($("div [data-value=" + this + "]"));
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="selectize-input">
<div class="item" data-value="56">Dog</div>
<div class="item" data-value="57">Rabbit</div>
<div class="item" data-value="59">Cat</div>
</div>
So, I'm not quite sure if I fully understand the question. But if you're trying to dynamically render a div for each piece of data, you could try something like...
JS:
const arrayData = [
{value:59, title:"Cat"},
{value:56, title:"Dog"},
{...}
]
arrayData.forEach((dataObject) => {
$('.selectize-input').append(
document.createElement('div')
.innerHTML(dataObj.title)
.attr('data-value', dataObj.value)
.addClass('item')
)
}
I'm not really a jQuery person, so I'm not entirely sure that this will work, but I believe it will send you in the right direction.
First, I am making your array into an array of objects which include both the value of the item, but also the title of the item.
Then, I am using a forEach loop to run through each item and create a new div based on it. It appends these divs to the .selectize-input.
I hope it helps you.
I should say, the stuff that you're doing here is something that ReactJS is really good at. It's like a souped-up javascript that allows you to create and manipulate HTML elements really easily.
Check it out at https://reactjs.org/!
If you only have the parent element in html and want to add the child div in sorted order then :
const divList = [
{value:59, title:"Cat"},
{value:56, title:"Dog"}
]
divList.forEach((obj) => {
$("<div />")
.html(obj.title)
.attr('data-value', obj.value)
.addClass('item')
.appendTo( $('.selectize-input'))
})
Codepen : https://codepen.io/AdityaDwivedi/pen/pojMYRr
If you already have the div element added in your html & you want to sort them up :
<div class="selectize-input">
<div class="item" data-value="56" id="56">Dog</div>
<div class="item" data-value="57" id="57">Rabbit</div>
<div class="item" data-value="59" id="59">Cat</div>
</div>
`
const divOrder = ["59", "56", "57"]
const orderedDiv = $('.selectize-input > div').sort((a, b) =>
divOrder.indexOf(a.id) - divOrder.indexOf(b.id)
);
$('.selectize-input').empty()
orderedDiv.map((obj) =>{
$("<div />")
.html(orderedDiv[obj].innerHTML)
.attr('data-value', orderedDiv[obj].value)
.appendTo( $('.selectize-input'))})
`
Codepen : https://codepen.io/AdityaDwivedi/pen/QWjeopv?editors=1111
I want to change the HTML-value of the highlighted span below (class=percent-value):
<div id="verfuegbarstd" class="et_pb_number_counter_4" data-number-value="0" data-number-separator="">
<div class="percent">
<p>**<span class="percent-value">0</span>**<span class="percent-sign"></span></p>
</div>
<h3 class="title">Verfügbare Stunden</h3>
<canvas height="0" width="0"></canvas>
</div>
I tried the following:
var verfuegbareStd = document.getElementsByClassName('et_pb_number_counter_4').getElementsByClassName('percent').getElementsByClassName('percent-value');
var budget = document.getElementsByClassName('et_pb_number_counter_2').getElementsByClassName('percent').getElementsByClassName('percent-value');
var lohnProStd = document.getElementsByClassName('et_pb_number_counter_3').getElementsByClassName('percent').getElementsByClassName('percent-value');
var gebrauchteStd = document.getElementsByClassName('et_pb_number_counter_5').getElementsByClassName('percent').getElementsByClassName('percent-value');
function calcVerfuegbareStd() {
var calc = budget.innerHTML / lohnProStd.innerHTML;
verfuegbareStd.innerHTML = calc;
}
calcVerfuegbareStd();
Does that make any sense?
document.getElementsByClassName returns a collection of all elements in the document with the specified class name, as a NodeList object. So thats why i check the length.
You can use also document.querySelector which gets the first element in the document with the class "xxxx" is returned.
I put both!
You can do it with jquery also but i thought you want pure js.
var elements = document.getElementsByClassName('percent-value'); // List of elements
var spanQuery = document.querySelector('.percent-value'); // The first element in the document with the class "myclass" is returned:
spanQuery.innerHTML = 'Hello!!!';
if (elements.length > 0) {
var span = elements[0];
span.innerHTML = 'Hello!!!';
}
<div id="verfuegbarstd" class="et_pb_number_counter_4" data-number-value="0" data-number-separator="">
<div class="percent">
<p>**<span class="percent-value">0</span>**<span class="percent-sign"></span></p>
</div>
<h3 class="title">Verfügbare Stunden</h3>
<canvas height="0" width="0"></canvas></div>
Try this?:
document.getElementsByClassName("percent-value").innerHTML = "the content you want";
It is simpler to use querySelector(). This will return the first element.
var verfuegbareStd = document.querySelector('.et_pb_number_counter_4 .percent .percent-value');
console.log(verfuegbareStd.innerHTML)
<div id="verfuegbarstd" class="et_pb_number_counter_4" data-number-value="0" data-number-separator="">
<div class="percent">
<p>**<span class="percent-value">0</span>**<span class="percent-sign"></span></p>
</div>
<h3 class="title">Verfügbare Stunden</h3>
<canvas height="0" width="0"></canvas>
</div>
I have a function which accepts two parameters, each of type HTML element. It is supposed to return which element appears first in the document order. Is there any simple way to determine this?
Template -
<body>
<div id="div1">
<div id="div2">
</div>
</div>
<div id="div3">
<div id="div4">
</div>
</div>
</body>
JS -
const elem1 = document.getElementById('div2');
const elem2 = document.getElementById('div4');
const firstAppearingElement = checkOrder(elem1, elem2); // it should return elem1
function checkOrder(element1, element2) {
// check which one appears first in dom tree
}
You can try with Node.compareDocumentPosition()
The Node.compareDocumentPosition() method compares the position of the
given node against another node in any document.
The syntax is object.compareDocumentPosition (nodeToCompare);
let first = document.getElementById('a');
let second=document.getElementById('b');
// Because the result returned by compareDocumentPosition() is a bitmask, the bitwise AND operator has to be used for meaningful results.See link above for more
if (first.compareDocumentPosition(second) & Node.DOCUMENT_POSITION_FOLLOWING) {
console.log('element with id a is before element with id b'); //
} else {
console.log('element with id a is after element with id b');
}
<div id="a"></div>
<div id="b"></div>
I have a PHP function that generates hierarchical view of some blog posts according to their category, child category, grand child category and so on. It generates a string containing div tags with its data attributes. I want to convert those divs to html <ul><li> based on their value of attribute aria-level.
Actual output from php method
<div role="heading" aria-level="1">Test 1</div>
<div role="heading" aria-level="2">Test 1.1</div>
<div role="heading" aria-level="3">Test 1.1.1</div>
<div role="heading" aria-level="3">Test 1.1.2</div>
<div role="heading" aria-level="1">Test 2</div>
<div role="heading" aria-level="3">Test 2.1.1</div>
<div role="heading" aria-level="2">Test 2.2</div>
Desired Output using php/js/jquery/ any framework
Test 1Test 1.1Test 1.1.1Test 1.1.2Test 2Test 2.1.1Test 2.2
What I have achieved so far ?
function buildRec(nodes, elm, lv) {
var node;
// filter
do {
node = nodes.shift();
} while(node && !(/^h[123456]$/i.test(node.tagName)));
// process the next node
if(node) {
var ul, li, cnt;
var curLv = parseInt(node.tagName.substring(1));
if(curLv == lv) { // same level append an il
cnt = 0;
} else if(curLv < lv) { // walk up then append il
cnt = 0;
do {
elm = elm.parentNode.parentNode;
cnt--;
} while(cnt > (curLv - lv));
} else if(curLv > lv) { // create children then append il
cnt = 0;
do {
li = elm.lastChild;
if(li == null)
li = elm.appendChild(document.createElement("li"));
elm = li.appendChild(document.createElement("ul"));
cnt++;
} while(cnt < (curLv - lv));
}
li = elm.appendChild(document.createElement("li"));
// replace the next line with archor tags or whatever you want
li.innerHTML = node.innerHTML;
// recursive call
buildRec(nodes, elm, lv + cnt);
}
}
// example usage
var all = document.getElementById("content").getElementsByTagName("*");
var nodes = [];
for(var i = all.length; i--; nodes.unshift(all[i]));
var result = document.createElement("ul");
buildRec(nodes, result, 1);
document.getElementById("outp").appendChild(result);
<div id="outp">
</div>
<div id="content">
<h1>Test 1</h1>
<h2>Test 1.1</h2>
<h3>Test 1.1.1</h3>
<h3>Test 1.1.2</h3>
<h1>Test 2</h1>
<h3>Test 2.1.1</h3>
<h2>Test 2.2</h2>
<p></p>
</div>
Problem to be resolved ?
As you can see above it is using Heading tags to sort. But unfortunately my category hierarchy are not limited to only 6th level. It may grow. So I want a JS/Jquery/any framework to convert some tags to ul/li structure based on their attribute value. If any changes from Backend side is needed I can change those attributes/tags to any from PHP side. If it can be done from PHP side easily then some example code snippets is also welcome. Consider the above div tags as a single string input. :)