addEventListener on class - javascript

I need to use getElementsByClassName because I have several same buttons etc.
I work on a wordpress loop that displays a button for each new article, and a registration form must appear on each event when we click on the button.
When I click on the button, I want the form to be displayed and the button to be hidden.
Someone can help me ?
Sorry if there are mistakes, I am French.
var bouton = document.getElementsByClassName('btn_inscription');
var formulaire = document.getElementsByClassName('formulaire');
var MyFonction = function{
formulaire.style.display = 'block';
bouton.style.display ='none';
}
for (var i = 0; i < bouton.length; i++) {
bouton[i].addEventListener('click', MyFonction);
}

getElementsByClassName (along with .getElementsByTagName and .getElementsByName) return node list objects (array-like containers). You can't interact with individual element properties and methods on node lists, you have to work with objects within the container. To set up event handlers on all the elements in the node list, you can loop through the container and set the handler one at a time.
Now, getElementsByClassName returns a "live node list", which is one that re-scans the document every time you interact with it to ensure that your container has the most up to date set of matching elements. This can cause big drops in page performance and the need for live node lists is pretty uncommon. Instead, use the more modern .querySelectorAll(), which allows you to pass any valid CSS selector in and returns a static node list.
// Get all the desired elements into a node list
let elements = document.querySelectorAll(".test");
// Convert the node list into an Array so we can
// safely use Array methods with it
let elementsArray = Array.prototype.slice.call(elements);
// Loop over the array of elements
elementsArray.forEach(function(elem){
// Assign an event handler
elem.addEventListener("click", function(){
console.log("You clicked me!");
this.style.backgroundColor = "#ff0";
});
});
<div class="test">Something</div>
<div>Something</div>
<div class="test">Something</div>
<div>Something</div>
<div class="test">Something</div>

You need to use .bind(thisArg[, arg1[, arg2[, ...]]]) in order to pass the current index and element (rif. this):
var bouton = document.getElementsByClassName('btn_inscription');
var formulaire = document.getElementsByClassName('formulaire');
var MyFonction = function(idx, event) {
formulaire[idx].style.display = 'block';
this.style.display ='none';
}
for (var i = 0; i < bouton.length; i++) {
bouton[i].addEventListener('click', MyFonction.bind(bouton[i], i));
// ^^^^^^^^^^^^^^^^^^^
}
.formulaire {
display: none;
}
<button class="btn_inscription">1</button>
<button class="btn_inscription">2</button>
<button class="btn_inscription">3</button>
<div class="formulaire">1</div>
<div class="formulaire">2</div>
<div class="formulaire">3</div>

You need to make use of this inside your event handler:
var buttons = document.getElementsByClassName('btn_inscription');
var MyFunction = function() {
this.closest('.formulaire').style.display = 'block';
this.style.display ='none';
}
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', MyFunction.bind(buttons[i]));
}

Related

Accessing child elements in Javascript array via onclick (for loop)

I am looping through an array like this:
<% thoughts.docs.forEach(function(thought, i) { %>
<div class="square-box-container">
<div class="pyp-image-container">
<div class="image-overlay">
By default, element class name 'image-overlay' is hidden with display: 'none'. I am trying to create a function with an onclick event, so when user clicks on div 'square-box-container', the image overlay for that element only changes to display: 'block'.
Currently I have the below code but I think my inner loop in wrong, as when I click on a square box container, the image overlays for ALL the square box containers change to display: 'block', as opposed to that container overlay only. Can anyone advise what I'm doing wrong please?
var containerItems = document.getElementsByClassName("square-box-container");
var overlayItems = document.getElementsByClassName("image-overlay");
for (var i = 0; i < containerItems.length; i ++) {
containerItems[i].onclick = function() {
for (var i = 0; i < overlayItems.length; i ++) {
overlayItems[i].style.display = 'block';
}
}
}
I'm not very familiar with use of child nodes, is that what is required here? Thanks
If you want only the associated element to have its display changed, don't loop inside the click handler - and use let instead of var.
for (let i = 0; i < containerItems.length; i++) {
containerItems[i].onclick = function () {
overlayItems[i].style.display = 'block';
}
}
Another option is to omit the overlayItems collection entirely, and navigate from the clicked element instead.
for (const container of document.getElementsByClassName("square-box-container")) {
container.addEventListener('click', (e) => {
e.currentTarget.querySelector('.image-overlay').style.display = 'block';
});
}

Create multiple elements and delete a single one JavaScript

I'm working on a JavaScript project where a user can click a button to create a text element. However, I also want a feature where I can click a different button and the element that was created most recently will be removed, so In other words, I want to be able to click a button to create an element and click a different button to undo that action.
The problem I was having was that I created the element, then I would remove the element using:
element.parentNode.removeChild(element); , but it would clear all of the elements that were created under the same variable.
var elem = document.createElement("div");
elem.innerText = "Text";
document.body.appendChild(elem);
This code allows an element to be created with a button click. All elemente that would be created are under the "elem" variable. so when I remove the element "elem", all element are cleared.
Is there a simple way to remove on element at a time that were all created procedurally?
Thanks for any help
When you create the elements, give the a class. When you want to remove an element, just get the last element by the className and remove it.
The below snippet demonstrates it -
for(let i = 0; i<5; i++){
var elem = document.createElement("div");
elem.innerText = "Text " + i;
elem.className = "added";
document.body.appendChild(elem);
}
setTimeout(function(){
var allDivs = document.getElementsByClassName("added");
var lastDiv = allDivs.length-1;
document.body.removeChild(allDivs[lastDiv]);
}, 3000);
I would probably use querySelectors to grab the last element:
// optional
// this is not needed it's just a random string added as
// content so we can see that the last one is removed
function uid() {
return Math.random().toString(36).slice(2);
}
document.querySelector('#add')
.addEventListener('click', (e) => {
const elem = document.createElement('div');
elem.textContent = `Text #${uid()}`;
document.querySelector('#container').appendChild(elem);
// optional - if there are elements to remove,
// enable the undo button
document.querySelector('#undo').removeAttribute('disabled');
});
document.querySelector('#undo')
.addEventListener('click', (e) => {
// grab the last child and remove
document.querySelector('#container > div:last-child').remove();
// optional - if there are no more divs we disable the undo button
if (document.querySelectorAll('#container > div').length === 0) {
document.querySelector('#undo').setAttribute('disabled', '');
}
});
<button id="add">Add</button>
<button id="undo" disabled>Undo</button>
<div id="container"></div>

Scope issues inside an Event Listener?

The following code basically shows/hides paragraph tags, I'm having to re-declare the paras variable. Is this because I'm dynamically injecting the button into the DOM, or is it to do with scope? How could I better construct this markup?
// vars
var revealContainer = document.querySelector('.reveal-more');
var paras = revealContainer.querySelectorAll('p');
var status = true;
// return
if (paras && paras.length <= 3) return;
// generate show more link
revealContainer.innerHTML += '<button class="button--text reveal-more__btn">Read more</button>';
var revealBtn = revealContainer.querySelector('.reveal-more__btn');
// click event
revealBtn.addEventListener('click', function () {
var paras = revealContainer.querySelectorAll('p');
// toggle show/hide class
for (var i = 0; i < paras.length; i++) {
var p = paras[i];
p.classList.toggle('is-shown');
}
// check status
if (status) {
this.textContent = 'Read less';
status = false;
} else {
this.textContent = 'Read more';
status = true;
}
});
You can use the live HTMLCollection returned by .getElementsByTagName() instead of the static NodeList returned by .querySelectorAll()
The getElementsByTagName method of Document interface returns an HTMLCollection of elements with the given tag name. The complete document is searched, including the root node. The returned HTMLCollection is live, meaning that it updates itself automatically to stay in sync with the DOM tree without having to call document.getElementsByTagName() again.
var paragraphs = document.getElementById("container").getElementsByTagName("p");
console.log(paragraphs.length);
setInterval(function() {
document.getElementById("container").insertAdjacentHTML("beforeend", "<p>p</p>");
}, 1000);
setInterval(function() {
console.log(paragraphs.length);
}, 2000);
<div id="container"></div>
Below is a really simple Snippet that demonstrates delegated events in pure Javascript, instead of using jQuery.
Here you can see I've attached the eventListener to the div with id elements, this will then listen for click events under this, a simple matches is used just in case you have other elements your not interested in..
document.querySelector("#elements").addEventListener("click", (e) => {
if (!e.target.matches('.element')) return
console.log(`Clicked ${e.target.innerText}`);
});
.element {
border: 1px solid black;
margin: 5px;
}
<div id="elements">
<div class="element">1</div>
<div class="element">2</div>
<div class="element">3</div>
<div>Clicking this does nothing.</div>
</div>

".addEventListener is not a function" why does this error occur?

I’m getting an ".addEventListener is not a function" error. I am stuck on this:
var comment = document.getElementsByClassName("button");
function showComment() {
var place = document.getElementById('textfield');
var commentBox = document.createElement('textarea');
place.appendChild(commentBox);
}
comment.addEventListener('click', showComment, false);
<input type="button" class="button" value="1">
<input type="button" class="button" value="2">
<div id="textfield">
</div>
The problem with your code is that the your script is executed prior to the html element being available. Because of the that var comment is an empty array.
So you should move your script after the html element is available.
Also, getElementsByClassName returns html collection, so if you need to add event Listener to an element, you will need to do something like following
comment[0].addEventListener('click' , showComment , false ) ;
If you want to add event listener to all the elements, then you will need to loop through them
for (var i = 0 ; i < comment.length; i++) {
comment[i].addEventListener('click' , showComment , false ) ;
}
document.getElementsByClassName returns an array of elements. so may be you want to target a specific index of them: var comment = document.getElementsByClassName('button')[0]; should get you what you want.
Update #1:
var comments = document.getElementsByClassName('button');
var numComments = comments.length;
function showComment() {
var place = document.getElementById('textfield');
var commentBox = document.createElement('textarea');
place.appendChild(commentBox);
}
for (var i = 0; i < numComments; i++) {
comments[i].addEventListener('click', showComment, false);
}
Update #2: (with removeEventListener incorporated as well)
var comments = document.getElementsByClassName('button');
var numComments = comments.length;
function showComment(e) {
var place = document.getElementById('textfield');
var commentBox = document.createElement('textarea');
place.appendChild(commentBox);
for (var i = 0; i < numComments; i++) {
comments[i].removeEventListener('click', showComment, false);
}
}
for (var i = 0; i < numComments; i++) {
comments[i].addEventListener('click', showComment, false);
}
var comment = document.getElementsByClassName("button");
function showComment() {
var place = document.getElementById('textfield');
var commentBox = document.createElement('textarea');
place.appendChild(commentBox);
}
for (var i in comment) {
comment[i].onclick = function() {
showComment();
};
}
<input type="button" class="button" value="1">
<input type="button" class="button" value="2">
<div id="textfield"></div>
The first line of your code returns an array and assigns it to the var comment, when what you want is an element assigned to the var comment...
var comment = document.getElementsByClassName("button");
So you are trying to use the method addEventListener() on the array when you need to use the method addEventListener() on the actual element within the array. You need to return an element not an array by accessing the element within the array so the var comment itself is assigned an element not an array.
Change...
var comment = document.getElementsByClassName("button");
to...
var comment = document.getElementsByClassName("button")[0];
Another important thing you need to note with ".addEventListener is not a function" error is that the error might be coming a result of assigning it a wrong object eg consider
let myImages = ['images/pic1.jpg','images/pic2.jpg','images/pic3.jpg','images/pic4.jpg','images/pic5.jpg'];
let i = 0;
while(i < myImages.length){
const newImage = document.createElement('img');
newImage.setAttribute('src',myImages[i]);
thumbBar.appendChild(newImage);
//Code just below will bring the said error
myImages[i].addEventListener('click',fullImage);
//Code just below execute properly
newImage.addEventListener('click',fullImage);
i++;
}
In the code Above I am basically assigning images to a div element in my html dynamically using javascript. I've done this by writing the images in an array and looping them through a while loop and adding all of them to the div element.
I've then added a click event listener for all images.
The code "myImages[i].addEventListener('click',fullImage);" will give you an error of "addEventListener is not a function" because I am chaining an addEventListener to an array object which does not have the addEventListener() function.
However for the code "newImage.addEventListener('click',fullImage);" it executes properly because the newImage object has access the function addEventListener() while the array object does not.
For more clarification follow the link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function
The main reason of this error is this line
document.getElementsByClassName("button")
Cause the getElementsByClassName returns an array-like object of all child elements or a collection of elements.
There are two possible solutions AFAIK -
Treat the variable containing document.getElementsByClassName("button") as an array and be specific when using an event listener.
Example -
comment[0].addEventListener('click' , showComment , false )
Use id for selecting that specific element.
Example-
document.getElementById('button')
Try this one:
var comment = document.querySelector("button");
function showComment() {
var place = document.querySelector('#textfield');
var commentBox = document.createElement('textarea');
place.appendChild(commentBox);
}
comment.addEventListener('click', showComment, false);
Use querySelector instead of className
<script src="main.js" defer></script>
which makes execute your code after the document fully loaded hence the javascript has complete reference

How to get all elements inside "div" that starts with a known text

I have a div element in an HTML document.
I would like to extract all elements inside this div with id attributes starting with a known string (e.g. "q17_").
How can I achieve this using JavaScript ?
If needed, for simplicity, I can assume that all elements inside the div are of type input or select.
var matches = [];
var searchEles = document.getElementById("myDiv").children;
for(var i = 0; i < searchEles.length; i++) {
if(searchEles[i].tagName == 'SELECT' || searchEles.tagName == 'INPUT') {
if(searchEles[i].id.indexOf('q1_') == 0) {
matches.push(searchEles[i]);
}
}
}
Once again, I strongly suggest jQuery for such tasks:
$("#myDiv :input").hide(); // :input matches all input elements, including selects
Option 1: Likely fastest (but not supported by some browsers if used on Document or SVGElement) :
var elements = document.getElementById('parentContainer').children;
Option 2: Likely slowest :
var elements = document.getElementById('parentContainer').getElementsByTagName('*');
Option 3: Requires change to code (wrap a form instead of a div around it) :
// Since what you're doing looks like it should be in a form...
var elements = document.forms['parentContainer'].elements;
var matches = [];
for (var i = 0; i < elements.length; i++)
if (elements[i].value.indexOf('q17_') == 0)
matches.push(elements[i]);
With modern browsers, this is easy without jQuery:
document.getElementById('yourParentDiv').querySelectorAll('[id^="q17_"]');
The querySelectorAll takes a selector (as per CSS selectors) and uses it to search children of the 'yourParentDiv' element recursively. The selector uses ^= which means "starts with".
Note that all browsers released since June 2009 support this.
Presuming every new branch in your tree is a div, I have implemented this solution with 2 functions:
function fillArray(vector1,vector2){
for (var i = 0; i < vector1.length; i++){
if (vector1[i].id.indexOf('q17_') == 0)
vector2.push(vector1[i]);
if(vector1[i].tagName == 'DIV')
fillArray (document.getElementById(vector1[i].id).children,vector2);
}
}
function selectAllElementsInsideDiv(divId){
var matches = new Array();
var searchEles = document.getElementById(divId).children;
fillArray(searchEles,matches);
return matches;
}
Now presuming your div's id is 'myDiv', all you have to do is create an array element and set its value to the function's return:
var ElementsInsideMyDiv = new Array();
ElementsInsideMyDiv = selectAllElementsInsideDiv('myDiv')
I have tested it and it worked for me. I hope it helps you.
var $list = $('#divname input[id^="q17_"]'); // get all input controls with id q17_
// once you have $list you can do whatever you want
var ControlCnt = $list.length;
// Now loop through list of controls
$list.each( function() {
var id = $(this).prop("id"); // get id
var cbx = '';
if ($(this).is(':checkbox') || $(this).is(':radio')) {
// Need to see if this control is checked
}
else {
// Nope, not a checked control - so do something else
}
});
i have tested a sample and i would like to share this sample and i am sure it's quite help full.
I have done all thing in body, first creating an structure there on click of button you will call a
function selectallelement(); on mouse click which will pass the id of that div about which you want to know the childrens.
I have given alerts here on different level so u can test where r u now in the coding .
<body>
<h1>javascript to count the number of children of given child</h1>
<div id="count">
<span>a</span>
<span>s</span>
<span>d</span>
<span>ff</span>
<div>fsds</div>
<p>fffff</p>
</div>
<button type="button" onclick="selectallelement('count')">click</button>
<p>total element no.</p>
<p id="sho">here</p>
<script>
function selectallelement(divid)
{
alert(divid);
var ele = document.getElementById(divid).children;
var match = new Array();
var i = fillArray(ele,match);
alert(i);
document.getElementById('sho').innerHTML = i;
}
function fillArray(e1,a1)
{
alert("we are here");
for(var i =0;i<e1.length;i++)
{
if(e1[i].id.indexOf('count') == 0)
a1.push(e1[i]);
}
return i;
}
</script>
</body>
USE THIS I AM SURE U WILL GET YOUR ANSWER ...THANKS

Categories