I am very new to coding. Below is a piece of JS code i am struggling to understand:
var btnContainer = document.getElementbyId(“linkcontainer”);
var btns = btnContainer.getElementsbyClassName(“btn”);
for (var i = 0; i < btns.length; i++){
btns.addEventListener(“click”, function(){
var current = document.getElementsbyClassName(“active”);
current[0].className = current[0].className.replace(‘active’, “”);
this.className += ‘active’;
});}
What difference does the [i] make in
btns[i].AddEventListener??
What is it exactly and what if there was no “i” in between the brackets? Also current[0]. It’s probably a stupid question, but please help me understand.
First of all there are no stupid question but only stupid answers.
In your code you get a list of DOM elements stored in an array called 'btns', then you iterate it with a loop.
So btns[i] allow you to retrieves the elements at the i position (It's important to note that array start at 0 in Javascript).
Example:
var fruits = ['Apple', 'Banana'];
console.log(fruits[0])
console.log(fruits[1])
So if you don't use the [i] you will iterate on the array itself and not on the element stored in it.
As the name of the method getElementsByClassName suggests, this queries the DOM and returns an array like object that contain multiple elements with the class name that was specified.
btns - will be an array that contains one or more elements.
To access a specific element from the array, you access it using the index of the current iteration.
btns[1] - Gives you access to the 2nd element in the list.
addEventListener - is used to bind a event handler to a single element. You cannot directly use this on array of objects.
// query the DOM for element with id - linkcontainer
var btnContainer = document.getElementbyId(“linkcontainer”);
// query the DOM for elements with className - btn
// This can return multiple elements, so btns will be
// as array like object of elements
var btns = btnContainer.getElementsByClassName(“btn”);
// iterate over the array that was just queried for
for (var i = 0; i < btns.length; i++) {
// bind a click event for each element in the array
btns[i].addEventListener(“click”, function() {
// query the dom for elements with className - active
var current = document.getElementsByClassName(“active”);
// access the first element and replace the active class
current[0].className = current[0].className.replace(‘active’, “”);
// add the active class to the element that was clicked
this.className += ‘active’;
});
}
The way I see it you will have to remove the active class for all the elements instead of just the first entity. A slightly better way to improve this code would be is
var btnContainer = document.getElementbyId(“linkcontainer”);
var btns = btnContainer.getElementsByClassName(“btn”);
btns.forEach(function(btn) {
btn.addEventListener(“click”, function() {
// query the dom for elements with className - active
var current = document.getElementsByClassName(“active”);
current.forEach(function(elem) {
elem.classList.remove('active');
});
this.classList.add('active');
});
});
As the other posters have mentioned, your code var btns = btnContainer.getElementsbyClassName(“btn”); should return an array of DOM elements so in your for loop, btns[i] will retrieve the specific element at index i of btns as i goes from 0 to btns.length. Removing the i will retrieve the entire array on each iteration.
To your second question: current is exactly the same thing as btns, an array of DOM elements so current[0] will retrieve the first element in this array.
Related
I am currently having problem with selecting and matching the innerHTML using querySelectorAll.
Here is what i did:
document.querySelectorAll('*[class^="js-display-url"]').contains('product')
What the html is:
<span class="js-display-url">product/poss/newslanges</span>
But i want to match the class js-display-url starting with product but can't do that. please help.
With DOM selectors you cannot select elements based on the inner HTML, but you can loop through all the elements with that class and single out the ones that match your condition:
var allElements = document.querySelectorAll('*[class^="js-display-url"]');
var myElements = [];
for (var i = 0; i < allElements.length; i++) {
if (allElements[i].innerHTML.startsWith('product')) {
myElements.push(allElements[i]);
}
}
There isn't a DOM selector which will filter based on text nodes, but you can filter using some array methods, since a NodeList is array-like.
In particular, Array.prototype.filter can get the elements that meet your criteria.
var filter = Array.prototype.filter;
var allElements = document.querySelectorAll('.js-display-url');
var productElements = filter.call(allElements, function(element) {
return element.innerText.startsWith('product');
});
console.log(productElements);
<span class="js-display-url">product/poss/newslanges</span>
I'm attempting to make a simple shopping cart that pulls in cart objects from another page using a JSON file. However when I begin the process of appending a cloned DOM object to the product-list div it only appends the last object in the loop, essentially overwriting any objects before the final iteration.
$('document').ready(function(){
var cartArray = JSON.parse(localStorage.getItem("cart"));
console.log(cartArray);
parseData(cartArray);
})
function parseData(item){
// grab a clone of the aside
$copy = $('.cart-selection').clone();
//loop through and append a clone with modified JSON information for each item in the cart array
for(i=0; i<item.length; i++){
console.log(i);
$('h2', $copy).html(item[i].name);
$('img', $copy).attr("src", item[i].url);
$copy.appendTo($('product-list'));
}
}
I've tried several different variations using both .append and .appendTo with no luck. Any help would be greatly appreciated.
You should create another copy of the clone inside the loop.
The idea is to create a "prototype" of the element, and then create a copy of that prototype for each item in your loop:
// grab a clone of the aside
$clone= $('.cart-selection').clone();
//loop through and append a clone with modified JSON information for each item in the cart array
for(i=0; i<item.length; i++){
// create a copy of the clone
$copy = $clone.clone();
console.log(i);
$('h2', $copy).html(item[i].name);
$('img', $copy).attr("src", item[i].url);
$copy.appendTo($('product-list'));
}
I guess product-list is a class name or ID something?
$copy.appendTo($('product-list')); <--
if so, you may need to use .product-list or #product-list
You need to create the clone in the loop else you will be creating only one clone of the target element and you are just changing its content in the loop
$('document').ready(function () {
var cartArray = JSON.parse(localStorage.getItem("cart"));
console.log(cartArray);
parseData(cartArray);
})
function parseData(item) {
//loop through and append a clone with modified JSON information for each item in the cart array
for (i = 0; i < item.length; i++) {
// grab a clone of the aside, need to do in the loop because for each item in the loop we need a new element
//also should clone only the first instance of the cart-selection else in each iteration you will get multiple elements
var $copy = $('.cart-selection').eq(0).clone();
console.log(i);
$('h2', $copy).html(item[i].name);
$('img', $copy).attr("src", item[i].url);
//need to fix the selector here, may be you need a class selector if product-list is a class
$copy.appendTo($('.product-list'));
}
}
Demo: Fiddle
I am trying to clear some (10 of 50) fields by selecting elements by their shared id. The problem is that I can only select one at a time with getElementById()
Here is the jsFiddle demonstrating the single element grab. jsFiddle
function toggleBankCount() {
secBankFields = document.getElementById('2ndBankFields');
secBankFields.value = '';
}
I am fairly certain I can do what I want with getElementsByName() which returns an array of elements. However, I am using Perl and the name of the elements must be different in order for %fdat to work properly on submit. Can you help me?
Here's how you can do it:
toggleBankCount = function () {
secBankFields = document.getElementsByTagName('input');
for (var i = 0; i < secBankFields.length; ++i) {
if(secBankFields[i].id === '2ndBankFields')
secBankFields[i].value = '';
}
}
though ids should be unique, consider using classes instead. Also, classes and ids shouldn't begin with a digit.
Check it out: JSFiddle
There are some Javascript functions which should help you.
Get element by ID attribute
Id should be unique.
var elm = document.getElementById("some_id");
Get elements by Tag name
// get all div elements
var elms = document.getElementsByTagName("div");
Get element by Class attribute
// get all elements with class .some_class
var elms = document.getElementsByClassName("some_class");
Get element by Name attribute
// get all elements with attribute name some_name
var elms = document.getElementsByName("some_name");
Get element by CSS selector
var elms = document.querySelectorAll("div.box, span.title");
for (var i = 0; i < elms.length; i++) {
var elm = elms[i];
console.log(elm);
}
How to relate or associate an object in JavaScript to an HTML element?
For each object in an array, I generate a checkbox input element based on that object.
If I add an event listener to capture the checkbox changes, how do I retrieve the object associated with that element?
As other answers point out, generating a unique id for each DOM node allows you to use those as keys in an Object.
Another possibility is that many frameworks provide utilities for assigning data to DOM elements. For example, in jQuery you can do it by writing $(checkbox).data('mydata', obj) or in YUI Y.one(checkbox).setData('mydata', obj).
You can generate an ID for each of the checkboxes, and store the ID in the corresponding object. Then, in the event handler, you can get the ID of the changed checkbox and find the appropriate object based on that by iterating over the array.
To make it even easier to find the object, you can also map the IDs to objects (e.g. objectsByID[someID] = someObject). With this approach, you don't even have to iterate over the array.
Example of how to create the objectsByID map:
var objectsByID = {};
for (var i = 0; i < objects.length; i++) {
var id = "checkbox_" + i;
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("id", id);
// ...
objectsByID[id] = objects[i];
}
You give a progressive ID to each checkbox, starting from "checkbox0", and when you click on a checkbox you check the relative ID number, which correspond to the object in array[x].
Here is a really simple example.
Create a global object, store additional objects when you're creating the checkboxes, identified by a unique name. Set the name or id attribute of the checkbox element to this unique name.
var source = [];
var data = []; //Your data
var appendTo = document.body;//Anywhere
for(var i=0; i<data.length;i++){
var identifier = "chk"+i;
var inp = document.createElement("input");
inp.type = "checkbox";
inp.name = identifier;//.name or .id - it's up to your preference
inp.addEventListener("change", function(ev){
if(this.checked){
callback(source[this.name]);//calls function callback, passing the original object as an argument.
}
}, true);
appendTo.appendChild(inp);
source[identifier] = ...//your object.
}
I have an element with multiple elements inside. All of the elements inside have the same name. Is there any way to remove them using one function?
(refer to this question for example Remove multiple children from parent?
Here's a solution that removes the first level children with the specified name for the parent with the specified id. If you want to go deeper, you can recursively call it on the child elements you get inside (you'll have to add a parent parameter as well).
function removeChildren (params){
var parentId = params.parentId;
var childName = params.childName;
var childNodes = document.getElementById(parentId).childNodes;
for(var i=childNodes.length-1;i >= 0;i--){
var childNode = childNodes[i];
if(childNode.name == 'foo'){
childNode.parentNode.removeChild(childNode);
}
}
}
And to call it:
removeChildren({parentId:'div1',childName:'foo'});
And a fiddle for testing:
Notes: You can only access the name element dependably in JavaScript when it supported on your element (e.g. NOT on DIVs!). See here for why.
UPDATE:
Here's a solution using className based on our conversation:
function removeChildren (params){
var parentId = params.parentId;
var childName = params.childName;
var childNodesToRemove = document.getElementById(parentId).getElementsByClassName('foo');
for(var i=childNodesToRemove.length-1;i >= 0;i--){
var childNode = childNodesToRemove[i];
childNode.parentNode.removeChild(childNode);
}
}
2021 Answer:
Perhaps there are lots of way to do it, such as Element.replaceChildren().
I would like to show you an effective solution with only one redraw & reflow supporting all ES6+ browsers.
function removeChildren(cssSelector, parentNode){
var elements = parentNode.querySelectorAll(cssSelector);
let fragment = document.createDocumentFragment();
fragment.textContent=' ';
fragment.firstChild.replaceWith(...elements);
}
Usage: removeChildren('.foo',document.body);: remove all elements with className foo in <body>
ok this should be easy. First get the parent element:
var theParent = document.getElementById("notSoHappyFather");
then get an array of the nodes that you want to remove:
var theChildren = theParent.getElementsByName("unluckyChild");
Lastly, remove them with a loop:
for (var i = 0; i < theChildren.length; i++)
{
theParent.removeChild(theChildren[i]);
}
A sample of your HTML would get you a more complete answer, but one can fairly easy call DOM functions to get the list of children and just remove them. In jQuery, remove all children would be something like this:
$("#target > *").remove();
or
$("#target").html("");
And, you can see a demo here: http://jsfiddle.net/jfriend00/ZBYCh/
Or, not using jQuery you could also do:
document.getElementById("target").innerHTML = "";
If you're trying to only remove a subset of the children (and leave others intact), then you need to be more specific how one would determine which children to leave and which to remove. In jQuery, you could use a .find() select or a filter() selector to narrow the list of children to just the children you wanted to target for removal.