I have been using some code to add divs to my buttons for styling using the code below, it works fine on the first instance of the button but it does not add the divs to any buttons after that? what am I missing? I'm willing to learn and have tried to Google this but I'm getting buried deep in things I don't fully understand just yet. Would be great if any answer could be in plain Javascript and not jQuery.
JS
// Parent Element
const el = document.querySelector(".myclass");
// Create New Element
const newEl = document.createElement("span");
newEl.classList= "cm-bg";
const newEl2 = document.createElement("span");
newEl2.classList= "cm-base";
// Insert New Element BEFORE an Element
el.before(newEl);
el.before(newEl2);
HTML
<div class="elementor-button-wrapper">
<a href="#" class="elementor-button-link" role="button">
<span class="elementor-button-content-wrapper">
<span class="elementor-button-text">Click here</span>
</span>
</a>
</div>
querySelector finds the first matching element in the document. So your code always adds elements to the first .myclass in your document.
If you want to find all matching elements and update them, you use querySelectorAll and loop through the results:
const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
// Create New Element
const newEl = document.createElement("span");
newEl.className = "cm-bg"; // *** See comment on question
const newEl2 = document.createElement("span");
newEl2.className = "cm-base"; // *** See comment on question
// Insert New Element AFTER an Element
el.before(newEl);
el.before(newEl2);
}
Live Example:
const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
// Create New Element
const newEl = document.createElement("span");
newEl.className = "cm-bg"; // *** See comment on question
const newEl2 = document.createElement("span");
newEl2.className = "cm-base"; // *** See comment on question
// Insert New Element AFTER an Element
el.before(newEl);
el.before(newEl2);
}
.cm-bg::after {
content: "cm-bg"
}
.cm-base::after {
content: "cm-base"
}
<div class="elementor-button-wrapper">
<a href="#" class="elementor-button-link" role="button">
<span class="elementor-button-content-wrapper">
<span class="elementor-button-text">Click here</span>
</span>
</a>
</div>
<div class="elementor-button-wrapper">
<a href="#" class="elementor-button-link" role="button">
<span class="elementor-button-content-wrapper">
<span class="elementor-button-text">Click here</span>
</span>
</a>
</div>
<div class="elementor-button-wrapper">
<a href="#" class="elementor-button-link" role="button">
<span class="elementor-button-content-wrapper">
<span class="elementor-button-text">Click here</span>
</span>
</a>
</div>
<div class="elementor-button-wrapper">
<a href="#" class="elementor-button-link" role="button">
<span class="elementor-button-content-wrapper">
<span class="elementor-button-text">Click here</span>
</span>
</a>
</div>
That relies on the NodeList from querySelectorAll being iterable, which it is in modern environments (and per specification). If you need to handle older environments, see my answer here for how to polyfill it. Or just use a for loop:
for (let i = 0; i < list.length; ++i) {
const el = list[i];
// ...rest of loop body here...
}
Side note: Beware that no version of IE supports the before method on ChildNode. IE is actively being discontinued by Microsoft, but still has significant presence in large corporate or government installations.
FWIW, you can use insertAdjacentHTML which is universally supported and lets you write the elements as HTML (if that's desireable):
const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
el.insertAdjacentHTML(
"beforestart",
"<span class=cm-bg></span><span class=cm-base></span>"
);
}
Or just use insertBefore:
const list = document.querySelectorAll(".elementor-button-wrapper");
for (const el of list) {
// Create New Element
const newEl = document.createElement("span");
newEl.className = "cm-bg"; // *** See comment on question
const newEl2 = document.createElement("span");
newEl2.className = "cm-base"; // *** See comment on question
// Insert New Element AFTER an Element
el.parentElement.insertBefore(newEl, el);
el.parentElement.insertBefore(newEl2, el);
}
Related
I'm building a To Do list app and have a question regarding OOP and JavaScript. I want to create a value in the Constructor that holds my taskBody which contains the HTML and template literal that will be assigned by either the input value or an eventual population from local storage. My goal is to re-use this HTML in two separate functions, but I'm stuck with the template literal.
class Task {
constructor() {
let taskValue //Initializing a variable
this.taskBody = `<div class="task">
<span>${taskValue}</span> //Template Literal
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>`;
}
addTask = () => {
//Prevent empty task
if (this.input.value == "") {
this.setPlaceholder("Please Enter A Task");
return;
}
this.taskValue = this.input.value; //Trying to reassign taskValue to the input value
this.results.innerHTML += this.taskBody; //Trying to grab the HTML from the constructor and populate with taskValue able
ls.setLS(this.taskValue); //setting the Local Storage the Task Value, which works
};
}
I expect if I type "Stack Overflow" in the to-do list, "Stack Overflow" populates in the HTML and the Local Storage, however, it only populates in the Local Storage. The todo item is either undefined, null, or empty.
I've tried using this.taskValue, let taskValue = "", and let taskValue = null, but I get the results described above. Where am I going wrong, and more specifically, how can I reuse the HTML in different functions?
Here's a CodePen where you can see the issue:
Codepen
When you first instantiate the Task, the value of the this.taskBody is set as below:
<div class="task">
<span>undefined</span>
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>
with undefined value, because at the moment of instantiation, the taskValue is undefined.
If your todo list items are added dynamically (which is the case), consider having a function which will enable dynamic replacement, like:
getTaskBody = item => `<div class="task">
<span>${item}</span>
<span class="actions">
<a class="edit-button" title="Edit Task">Edit</a>
<button class="complete-button" title="Complete Task"><i class="fas fa-check"></i></button>
</span>
</div>`;
and use it later in line 123, instead of:
this.results.innerHTML += this.taskBody;
do:
this.results.innerHTML += getTaskBody(this.taskValue);
This question already has answers here:
removeChild() method is breaking for loop
(4 answers)
JavaScript DOM remove element
(4 answers)
Closed 2 years ago.
i want to remove specific child elements from my div element using javascript but i can't able to do that.
this is the format of element
<div class="div1">
<span class="spanp">span1<span class="spanc">spanchild1</span></span>
<span class="spanc">spanc</span>
<span class="spanp">span2<span class="spanc">spanchild2</span></span>
<span class="spanc">spanc</span>
<span class="spanp">span3<span class="spanc">spanchild3</span></span>
<span class="spanc">spanc</span>
</div>
i want to delete span elements of class="spanc" only.
how i can do it using javascript only
var list=document.getElementsByClassName("spanc");
for(var i=0;i<list.length;i++){
list[i].parentNode.removeChild(list[i]);
}
when i run this i'am able to delete only main spanc class elements and subelements of class spanp elements on odd indexs only.even index spanp class subelements are strill in list
I suppose this could do the trick:
const spanc = document.querySelectorAll('.div1 .spanc');
spanc.forEach(node => node.remove());
So part of the issue is the spans you are trying to remove have different parent elements; sometimes it's a span otherwise it's a div.
const itemsToRemove = document.getElementsByClassName('spanc');
const parents = document.getElementsByClassName('spanp');
[...parents].forEach(span => {
const itemsToRemove = span.getElementsByClassName('spanc');
[...itemsToRemove].forEach(item => span.removeChild(item));
});
const outerParent = document.getElementsByClassName('div1')[0];
const upperItemsToRemove = outerParent.getElementsByClassName('spanc');
[...upperItemsToRemove].forEach(item => outerParent.removeChild(item));
<div class="div1">
<span class="spanp">span1<span class="spanc">spanchild1</span></span>
<span class="spanc">spanc</span>
<span class="spanp">span2<span class="spanc">spanchild1</span></span>
<span class="spanc">spanc</span>
<span class="spanp">span2<span class="spanc">spanchild1</span></span>
<span class="spanc">spanc</span>
</div>
Thank you for the help i got the answer by using while loop instead of forloop
while(list.length>0){
let i=0;
list[i].parentNode.removeChild(list[i]);
}
I'm working on a simple project where I need to make an item in a list editable and then update a JS item to store this.
I'm using Content Editable = True, and it works when I comment out my handleItemEdit function, but when I turn it on, I can only insert one character at a time, forcing me to keep clicking to edit.
Clearly this problem stems from my function, but I can't seem to figure out why.
//Responsible for listening for an edit and updating my object with the new text.
function handleEditItem() {
$('.js-shopping-item').on('input', function(event) {
const itemIndex = getItemIndexFromElement(event.currentTarget); //assigning the index of the the editted item to itemIndex
const updatedItem = STORE.items[itemIndex];
updatedItem.name = event.currentTarget.innerHTML;
renderShoppingList();
});
}
//Returns the index of an Item in the Store
function getItemIndexFromElement(item) {
const itemIndexString = $(item)
.closest('.js-item-index-element')
.attr('data-item-index');
return parseInt(itemIndexString, 10);
}
//Function responsible for returning the template HTHML to insert into the html.
function generateItemElement(item) {
let itemIndex = STORE.items.indexOf(item);
return `
<li class="js-item-index-element" data-item-index="${itemIndex}">
<span contentEditable='true' class="shopping-item js-shopping-item ${item.checked ? 'shopping-item__checked' : ''}">${item.name}</span>
<div class="shopping-item-controls">
<button class="shopping-item-toggle js-item-toggle">
<span class="button-label">check</span>
</button>
<button class="shopping-item-delete js-item-delete">
<span class="button-label">delete</span>
</button>
</div>
</li>`;
}
I have some products on a page that I need to grab the Alt tag from. I need to turn them into an Object. After that they need to go into an Array.
I was thinking of creating a for loop to loop through the Alt tags, but I am stuck as to how to Split the Alt tag at the pipe '|'.I keep getting an Invalid or unexpected Token. This is code and below that is what I have.
Right at the end I have the jQuery version that works fine, but I want to know the Vanilla Javascript way. As I want to step away from jQuery and learn how to code better in Javascript.
<div class="product col-xl-3 col-lg-3 col-md-4 col-sm-6 col-12" alt="0016|AUX Cable|2.39|5m|Black|2|Tech-Aux">
<a href="/0016/product">
<img class="productImage" src="/static/store/images/products/download-10.jpg">
</a>
<a href="/0016/product">
<h3>AUX Cable</h3>
</a>
<h4 class="price">£2.39</h4>
<input type="button" class="button style_one addToCartOne" value="Add To Cart">
</div>
<div class="product col-xl-3 col-lg-3 col-md-4 col-sm-6 col-12" alt="0015|USB 2 Type A|3.49|10m|Black|300|Tech-Usb">
<a href="/0015/product">
<img class="productImage" src="/static/store/images/products/download_Jb4ucER.jpg">
</a>
<a href="/0015/product">
<h3>USB 2 Type A</h3>
</a>
<h4 class="price">£3.49</h4>
<input type="button" class="button style_one addToCartOne" value="Add To Cart">
</div>
Here is my code:
var products = document.querySelectorAll('.product');
for(var i=0; i<products.length; i++){
products[i].alt.split(“|”);}
Thank you for any advise. Also any help as to where I can look this up in the future would be great as well.
This is the jQuery code that works. And this is what I want to achieve in Javascript:
var products = [];
$.each($(".product"), function(){
var prodOb = {};
var prodDetails = $(this).attr("alt").split("|");
prodOb.id = prodDetails[0];
prodOb.name = prodDetails[1];
prodOb.price = prodDetails[2];
prodOb.brand = "Some Company";
products.push(prodOb)
})
console.log(products)
You're really close, you've done the big that people find tricky (using the DOM instead of jQuery to find the elements).
Two things:
You seem to have stopped in the middle. You have the code using jQuery to do this, and most of that code has nothing to do with jQuery, but you haven't used that code in your attempt.
You're using fancy quotes (“”), but JavaScript requires boring straight up-and-down quotes (").
If we just copy the code from the sample using jQuery into the code you've already written, and fix the quotes, use elements instead of products to conflict with the array you're creating, use a reasonable placement for }, and add a missing semicolon or two, we get:
var products = [];
var elements = document.querySelectorAll('.product');
for (var i = 0; i < elements.length; i++){
var prodDetails = elements[i].getAttribute("alt").split("|");
var prodOb = {};
prodOb.id = prodDetails[0];
prodOb.name = prodDetails[1];
prodOb.price = prodDetails[2];
prodOb.brand = "Some Company";
products.push(prodOb);
}
console.log(products);
Or we can use Array.prototype.map, but for one it isn't a lot more concise:
var products = Array.prototype.map.call(
document.querySelectorAll('.product'),
function(element) {
var prodDetails = elements.getAttribute("alt").split("|");
return {
id: prodDetails[0],
name: prodDetails[1],
price: prodDetails[2],
brand: "Some Company"
};
}
);
console.log(products);
Note: We have to use .getAttribute("alt") instead of .alt because the HTML is invalid. div elements have no alt attribute. I have to admit in the first version of this question I didn't look at the HTML and assumed that it was valid, where the alt attribute is used on an img element (which has the .alt property).
So I have an html file where an 'Add' button creates field box labeled
<a id="document_create:nxl_item:nxw_referenceHeaderList:0:nxw...." class="button smallButton listWidgetActionAdd" onclick="return ... </a>
If I keep clicking the 'Add' button, the id name continually changes every time where the 0 is. It can be:
document_create:nxl_item:nxw_referenceHeaderList:0:nxw....
document_create:nxl_item:nxw_referenceHeaderList:1:nxw....
document_create:nxl_item:nxw_referenceHeaderList:2:nxw....
document_create:nxl_item:nxw_referenceHeaderList:2:nxw....
document_create:nxl_item:nxw_referenceHeaderList:3:nxw....
Is there a way that I can extract the ID name without having to type it in manually? Like a loop or a function that can extract the ID from a class or something?
Any help would be appreciated! Thanks!
It would be a lot easier to help you if you showed some HTML context, but if new ones are being added after previous ones, you can find them by class (assuming some combination of the classes on them are unique) or based on where they are in the DOM structure, and use the last one:
var list = document.querySelectorAll("a.listWidgetActionAdd"),
last = list[list.length - 1];
if (last) {
// Use `last`, it's a reference to the element
// `last.id` is its `id` if you need it
}
Live Example:
var list = document.querySelectorAll("a.listWidgetActionAdd"),
last = list[list.length - 1];
if (last) {
// Use `last`, it's a reference to the element
// `last.id` is its `id` if you need it
document.body.innerHTML = "<code>last.id = " + last.id + "</code>";
}
<a id="document_create:nxl_item:nxw_referenceHeaderList:0:nxw...." class="button smallButton listWidgetActionAdd"></a>
<a id="document_create:nxl_item:nxw_referenceHeaderList:1:nxw...." class="button smallButton listWidgetActionAdd"></a>
<a id="document_create:nxl_item:nxw_referenceHeaderList:2:nxw...." class="button smallButton listWidgetActionAdd"></a>
Re your comment:
I should've mentioned this earlier that there's another add button within the field that's being created that has a similar class. Is there any way to distinguish them both? Like using a string RegExp? The difference is that the id names are different... Would that help?
If it's just a similar class name, then there's no problem — just use the other class name.
If it's the same class name, then yes, you can use the id to differentiate them. For instance, here's the above using an attribute starts with selector to only look at a elements with an id that starts with document_create:nxl_item:nxw_referenceHeaderList:
var list = document.querySelectorAll("a[id^='document_create:nxl_item:nxw_referenceHeaderList']"),
last = list[list.length - 1];
if (last) {
// Use `last`, it's a reference to the element
// `last.id` is its `id` if you need it
document.body.innerHTML = "<code>last.id = " + last.id + "</code>";
}
var list = document.querySelectorAll("a[id^='document_create:nxl_item:nxw_referenceHeaderList']"),
last = list[list.length - 1];
if (last) {
// Use `last`, it's a reference to the element
// `last.id` is its `id` if you need it
document.body.innerHTML = "<code>last.id = " + last.id + "</code>";
}
<a id="document_create:nxl_item:nxw_referenceHeaderList:0:nxw...." class="button smallButton listWidgetActionAdd"></a>
<a id="some-other-format:0:nxw...." class="button smallButton listWidgetActionAdd"></a>
<a id="document_create:nxl_item:nxw_referenceHeaderList:1:nxw...." class="button smallButton listWidgetActionAdd"></a>
<a id="some-other-format:1:nxw...." class="button smallButton listWidgetActionAdd"></a>
<a id="document_create:nxl_item:nxw_referenceHeaderList:2:nxw...." class="button smallButton listWidgetActionAdd"></a>
<a id="some-other-format:2:nxw...." class="button smallButton listWidgetActionAdd"></a>