Same function on two separate elements with the same class [duplicate] - javascript

This question already has answers here:
Why does querySelector only select the first element and how can I fix this?
(4 answers)
Closed 1 year ago.
I have this function:
var togView = document.querySelector('.toggle-view');
var list = document.querySelector('.pt-list');
togView.onclick = function() {
const isAdded = togView.classList.toggle('list');
list.classList.toggle('list', isAdded);
};
Which I'm using to toggle a class called .list on and off on this little element called toggle-view and on a sibling element called pt-list
<div class="toggle-view">
<span class="thumb-view">Thumbnail View</span> | <span class="list-view">List View</span>
</div>
<div class="pt-list"></div>
This worked fine until I added another <div class="toggle-view"> element. Now only the first iteration of the toggle-view element works.
How could I get this function to work on both <div class="toggle-view"> elements independently without having to assign them an ID and duplicate the function?
I found a similar question on here from a few years ago and from what I understand, I need to use this?, but I can't figure out how to apply that to what's happening in my function.
Any guidance would be appreciated!

You can use "document.querySelectorAll('.toggle-view')" and querySelectorAll will return a list of html elements with class name of ".toggle-view". Then use foreach loop through this list and add click listener to trigger click event independently.
See the snippet below, the result might be vary, but logic is there:
var togView = document.querySelectorAll('.toggle-view');
var list = document.querySelector('.pt-list');
togView.forEach((button) => {
button.addEventListener('click', () => {
const isAdded = button.classList.toggle('list');
list.classList.toggle('list', isAdded);
//Test for showing the className onto html element, independently
button.innerHTML = button.classList.value
});
});
<div class="toggle-view">
<span class="thumb-view">Thumbnail View</span> | <span class="list-view">List View</span>
</div>
<div class="toggle-view">
<span class="thumb-view">Thumbnail View</span> | <span class="list-view">List View</span>
</div>
<div class="pt-list"></div>

Related

How to close template modals in handlebars?

I created an app that receives articles from news API. Each article is displayed in a card, which has a button "Open Modal".
This button opens a modal with the unique information that pertains to each respective article.
However, I am unable to close the modal once it's opened. I suspect it's because the modal is stuck in this state: modals.forEach((modal, index) => {modal.classList.toggle('open', index === openIndex);
Here is my current code:
{{!-- #each article --}}
<div class="row">
{{#each articles}}
<div class="col-12-sm col-6-md col-3-lg">
<div class="card m-2">
<div class="card-body">
<h5 class="card-title">{{title}}</h5>
<p class="card-text">{{description}}</p>
</div>
<img class="card-image" src="{{urlToImage}}" alt="Card image cap">
<button data-open-modal="{{#index}}">Open Modal</button>
</div>
</div>
{{/each}}
</div>
</div>
{{#each articles}}
<!-- The Modal -->
<div class="modal closed" id="Modal_{{#index}}">
<!-- Modal content -->
<div class="modal-content">
<span id="spm" class="close" >×</span>
<h2>{{title}}</h2>
<img src="{{urlToImage}}" alt="">
<p>{{content}}</p>
</div>
</div>
{{/each}}
<script>
//Store all modals and modal buttons in variables
const openModalButtons = document.querySelectorAll('[data-open-modal]');
const modals = document.querySelectorAll('.modal');
//Loop through all modal buttons and assign handler to each
openModalButtons.forEach(openModalButton => {
openModalButton.addEventListener('click', (event) => {
//Get index value from number clicked
const openIndex = Number(event.target.dataset.openModal); //Access dataset attribute to read and write
//Loop over each modal.
//Set modal class to open if index is equal to wanted index
modals.forEach((modal, index) => {
modal.classList.toggle('open', index === openIndex);
modal.classList.toggle('closed', index !== openIndex);
});
});
});
</script>
And here is what I tried adding to my script: (It gave no error but did nothing)
const span = document.querySelectorAll('.close');
let spanArr = Array.prototype.slice.call(span);
spanArr.forEach(spanArr => {
spanArr.addEventListener('click', (event) => {
const closeIndex = Number(event.target.dataset.closeModal);
spanArr[closeIndex].forEach(span => {
span.onclick = function() {
modal.style.display = "none";
}
});
});
});
I also tried adding event listeners to the spans, but I was unable to make it work. I am a beginner and this is my first time using handlebars, so thank you for any insight!
There are some issues here that perhaps are due to absent explanations on my part in https://stackoverflow.com/a/73738690/3397771.
First, the reason that const openIndex = Number(event.target.dataset.openModal); works is because there is a data-attribute called data-open-modal defined on each "open" button. It is that data-attribute that we are referencing with dataset.openModal and the value we will get back is the value on the right-hand-side of the equal sign in the attribute, the {{#index}} part.
However, the data-attribute approach is probably excessively complicated for our purposes here. We could, alternatively, use the index obtained in the forEach loop we use to iteratively add our click event listeners.
Next, there is no need for the spanArr[closeIndex].forEach(... loop inside our click handler. spanArr - despite its name - is not an arr(ay); it is a single span element.
The updated code becomes:
const span = document.querySelectorAll('.close');
span.forEach((spanArr, index) => {
spanArr.addEventListener('click', () => {
modals[index].style.display = "none";
});
});
Note: I have left the names of the variables as I found them, but they could and should be improved. For example, span does not communicate what purpose of these elements is or, for that matter, that it is a collection. closeButtons would be a better name. In fact, elements that behave like buttons should use the <button> element, not <span>, so as to be semantically correct and accessible.
I have created a new fiddle.

Why does the classList.contain() keeps returning false?

Why does the classList.contain() keeps returning false? I want to create a toggle effect. when on click, only the descendent of a certain class will have the toggle effect. However, the containing method keeps returning false.
One weird part when I'm playing around with the containing method is when I take out the '.classList' it returns True perfectly. But I know if I want to find descendent classes of their parents I need '.classList'. Why is this?
Here is my HTML.
<ul class = "horizontalpic">
<div class = 'fakebutton'>
<div class = 'starsolid'>
<i class="fas fa-star fa-s" style = color:black></i>
</div>
</div>
</ul>
JS
const starhover = () => {
document.addEventListener('DOMContentLoaded', e => {
document.querySelector('ul.horizontalpic').addEventListener('click', e => {
console.log(e.target);
console.log(e.target.classList.contains('starsolid'));
});
});starhover();
console.log your e.target.classList and you'll see that it's horizontalpic, not starsolid.
The reason for that is because when you click on that element then you're clicking on the parent element, not the child. You've added document.querySelector('ul.horizontalpic').addEventListener('click' to that element, starsolid likely won't be triggered on any click.
If you want to fix it then change your click event to the correct element.
.contains will check any descending child element, so if you call it on e.target, which in this case is horizontalpic, it will loop through fakebutton and starsolid and find it.
Code Pen Solution
your console.log(e.target); is pointing to <i class="fas fa-star fa-s" style = color:black></i> which does not conatin the class 'starsolid'. Therfore, it always console logs False.
try this:
keep everything same only change this row on your code.
console.log(e.target.classList.contains('starsolid') || e.target.parentElement.classList.contains('starsolid'));
Answer checks if the clicked element contains the className, if the parent contains it or if any of the children and children of the children contains it:
const functionmy = () => {
const className = 'starsolid'
document.querySelector('ul.horizontalpic').addEventListener('click', e => {
console.log(e.target.classList.contains(className) || e.target.parentNode.classList.contains(className) || Object.values(e.target.getElementsByTagName('*')).map(e => (e.classList)).some(arr => (Object.values(arr).includes(className))));
})
}
const starhover = () => {
document.addEventListener('DOMContentLoaded', functionmy);
};
starhover();
<ul class="horizontalpic">
<div class='fakebutton'>
<div class='starsolid'>
<h1 class="fas fa-star fa-s" style=color:black>hello</h1>
</div>
</div>
</ul>

Pure JS: trying to remove Div tag that remove button is in [duplicate]

This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 2 years ago.
I tried to check out other answers on Stackoverflow and apply it to my own code but I couldn't get it to work.
I'm trying to implement the setup function that registers a click event handler and implements the following logic: when the button of class remove is clicked, its parent element should be removed from the list. It should be in pure javascript.
This is the original code:
function setup() {
//write your code here
}
document.body.innerTML = `
<div class="image">
<img src="https://" alt="First">
<button class="remove">X</button>
</div>
<div class="image">
<img src="https://" alt="Second">
<button class="remove">X</button>
</div>`;
setup();
document.getElementsByClassName("remove")[0].click();
console.log(document.body.innerHTML);
Here's my attempt
function setup() {
let buttondel = document.getElementsByClassName("remove");
buttondel.addEventListener('click',function(e){
e.currentTarget.parentNode.remove();
})
}
but I'm getting this error "buttondel.addEventListener is not a function". Here's my code at Codepen: https://codepen.io/kikibres/pen/PoZLbZa?editors=1011
You can make a "querySelectorAll" to get all buttons with the class remove, and then after that you can use forEach, or even a normal For loop to add the event listener of this.parentNode.remove().
Here is a codepen to help you understand:
https://codepen.io/marcoamorim95/pen/JjGZpzR
arrayBtns = document.querySelectorAll('.remove');
arrayBtns.forEach(btn => {
btn.addEventListener('click', function() {
this.parentNode.remove();
})
})

Problem with Dom-event to an array of buttons

I am trying to create a visual element where you can add and remove 2 input fields and a p element, while I found a way to do it, While removing them not in chronological order the last div wont be removed and prints me "cant remove of undefied"
I tried doing it in a few ways, through if function, throgh array methods etc... always the same problem
so the Html code goes this way
<main id="mainBlock">
<div class="divBlock">
<input class="name" type="text">
<input class="workingHours" type="text">
<p class="money"></p>
<button class="deleteButton">delete</button>
</div>
<button id="addButton">add</button>
</main>
and the js:
let addButton = document.getElementById('addButton');
let allDivs = document.getElementsByClassName('divBloc');
addButton.onclick = function(){
let deleteButtons = document.querySelectorAll('button.deleteButton');
let allDeleteButtonsArr = Array.from(allDeleteButtons)
allDeleteButtonsArr.forEach(item => {
item.onclick = function(){
let indexNumber = allDeleteButtonsArr.indexOf(item);
allDivs[indexNumber].remove();
};
});
I think i should explain while the onclick function is related to the create button at first. For the purpose of giving you easier time to read I delete all the part where I create all the new p div and input elements when you click on it. because each time you click on add element there is a new index number I thought it will be better to include it inside the addButton onclick fucntion.
Thanks in advance :)
Since you're dynamically appending nodes, and then you wish to remove them, adding/removing event handlers to the delete button might be very annoying.
A better way is to use event delegation by adding the event listener to the container #mainBlock, and when it's called check if the the delete button was called, and if so remove it's parent.
const item = `
<div class="divBlock">
<input class="name" type="text">
<input class="workingHours" type="text">
<p class="money"></p>
<button class="deleteButton">delete</button>
</div>
`;
const container = document.querySelector('#mainBlock');
const addButton = document.querySelector('#addButton');
addButton.addEventListener('click', () => {
addButton.insertAdjacentHTML('beforebegin', item);
});
container.addEventListener('click', e => {
if(!e.target.matches('.deleteButton')) return;
e.target.parentNode.remove();
});
<main id="mainBlock">
<button id="addButton">add</button>
</main>

jQuery matching a single class against predefined list where element has multiple classes

I am trying to figure out if there is a way around doing .hasClass() multiple times to see if the active element I am working with has one of currently four specific classes, I am also trying to figure out the most optimized way to do this while the element(s) that are acting as the trigger (or the active element) has multiple classes in it mostly for styling purposes.
Example of the HTML:
<div class="form_row">
<div class="primary_row">1</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub">1a</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub subexists">1a</div>
<div class="secondary_row">2</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub subexists">1a</div>
<div class="secondary_row subexists">2</div>
<div class="secondary_row_sub">2a</div>
</div>
I am in the progress of currently building it up, so this is still a rough draft, but its safe to assume more classes will exist on various elements per the need. The Four main classes I am worried about are primary_row, primary_row_sub, secondary_row, secondary_row_sub. I am building a click handler like:
$('.form_row > div').click(function()
{
//code
});
in this click handler I want to be able to detect if the element clicked is one of the four mentioned above. Where if it is, I want to do something based on which. So determining which class is of the element clicked, rather than building four click handlers one for each type. I am hoping I can keep it optimized and contained to a single handler. Any ideas?
One option:
var classMap = {"one": function () { alert("one");},
"two": function () { alert("two");},
"three": function () { alert("three");}
}
, classes = "";
$('div').click(function (e) {
classes = this.className.split(" ");
for (key in classMap) {
if ($.inArray(key, classes) !== -1) {
classMap[key]();
}
}
});
http://jsfiddle.net/wp9X7/5/
if ($(this).is(".primary_row")) {
...
} elseif ($(this).is(".primary_row_sub")) {
...
} and so on

Categories