How to select specific elements with JS that are dynamically created? - javascript

<div class="gallery-container">
<?php while (have_rows('gallery')): ?>
[...]
<div class="toggle-container">
<button class="toggle-button active" onclick="gridView()">Grid</button>
<button class="toggle-button" onclick="listView()">List</button>
</div>
<div class="gallery-items grid-items">
[...Gallery Items...]
</div>
<?php endwhile; ?>
</div>
What would be the best way to select specific elements on a page when the elements are created with a while loop shown above. It's an ever-growing list and elements can also be removed.
In this example I am generating a page full of small galleries together with the toggle buttons for the Grid/List view next to each gallery.
I am trying to make all of those buttons work with just the gallery they are generated together with.
I know how to select them based on their index manually, but I don't know how I could tweak the code to be able to make it work with every small gallery separately.
This is what I came up with to make it work with the first gallery:
<script>
const button = document.getElementsByClassName('toggle-button');
const element = document.getElementsByClassName('gallery-items');
function listView() {
if ( element[0].classList.contains('grid-items') ){
element[0].classList.remove("grid-items");
}
button[0].classList.toggle('active');
button[1].classList.toggle('active');
}
function gridView() {
if ( !element[0].classList.contains('grid-items') ){
element[0].classList.add("grid-items");
}
button[0].classList.toggle('active');
button[1].classList.toggle('active');
}
</script>

You might consider using event delegation instead: add a click listener to .gallery-container. If the clicked target is a .toggle-button, run the appropriate logic, selecting the relevant surrounding elements on click:
document.querySelector('.gallery-container').addEventListener('click', ({ target }) => {
if (!target.matches('.toggle-button')) {
return;
}
const toggleContainer = target.parentElement;
const btns = toggleContainer.children;
if (target === btns[0]) {
btns[0].classList.add('active');
btns[1].classList.remove('active');
} else {
btns[0].classList.remove('active');
btns[1].classList.add('active');
}
const galleryItems = toggleContainer.nextElementSibling;
if (target === btns[0]) {
galleryItems.classList.add('grid-items');
} else {
galleryItems.classList.remove('grid-items');
}
});
.active {
background-color: yellow;
}
.grid-items {
background-color: red;
}
<div class="gallery-container">
<div class="toggle-container">
<button class="toggle-button active">Grid</button>
<button class="toggle-button">List</button>
</div>
<div class="gallery-items grid-items">
[...Gallery Items...]
</div>
<div class="toggle-container">
<button class="toggle-button active">Grid</button>
<button class="toggle-button">List</button>
</div>
<div class="gallery-items grid-items">
[...Gallery Items 2...]
</div>
</div>
Note that there's no need to explicitly test if a classList.contains a particular class before adding it (though, there's no harm in doing so, it's just unnecessary).

Related

JS variables in jQuery

I have jQuery code and a need to repeat it 4x times with changing index.
So I used for loop from JS and inside get jQuery code.
Am I using the right way to pass variables from JS to jQuery?
It should select elements .info-btn of parent elements: #info-box-pr-1, #info-box-pr-2, ...
jQuery(document).ready(function() {
for (let i = 1; i < 5; i++) {
jQuery("#info-box-pr-" + i + ".less-info-box .info-btn").click(function() {
jQuery("#info-box-pr-" + i + ".more-info-box").css("display", "block");
});
jQuery("#info-box-pr-" + i + ".more-info-box .info-btn").click(function() {
jQuery("#info-box-pr-" + i + ".more-info-box").css("display", "none");
});
}
});
jQuery is a framework that relies on Javascript, so you're not passing variables between them. You're simply concatenating the i value to a string, which is fine.
That being said, I would suggest avoiding incremental id attributes, as it leads to more code complexity than necessary.
For example, you could remove the loop and id and make the code infinitely extensible by using common class names along with DOM traversal methods (eg. closest(), next(), prev()) to relate the .info-btn to the .more-info-box.
jQuery($ => {
$('.less-info-box .info-btn').on('click', e => $(e.target).closest('.less-info-box').hide().next().show());
$('.more-info-box .info-btn').on('click', e => $(e.target).closest('.more-info-box').hide().prev().show());
});
.more-info-box { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="less-info-box">
Less info
<button class="info-btn">More</button>
</div>
<div class="more-info-box">
More information...
<button class="info-btn">Less</button>
</div>
<div class="less-info-box">
Less info
<button class="info-btn">More</button>
</div>
<div class="more-info-box">
More information...
<button class="info-btn">Less</button>
</div>
<div class="less-info-box">
Less info
<button class="info-btn">More</button>
</div>
<div class="more-info-box">
More information...
<button class="info-btn">Less</button>
</div>
You don't really need jQuery for this. Use Event Delegation to handle things on the document level. For example:
document.addEventListener(`click`, handle);
function handle(evt) {
if (evt.target.id.startsWith('info-box')) {
// hide all div#more-...
document.querySelectorAll(`.more`).forEach(m => m.classList.add(`hidden`));
// display the div with id #more-[id of the clicked button]
document.querySelector(`#more-${evt.target.id}`).classList.remove(`hidden`);
}
}
.hidden {
display: none;
}
<button id="info-box-pr-1">info 1</button>
<button id="info-box-pr-2">info 2</button>
<button id="info-box-pr-3">info 3</button>
<div id="more-info-box-pr-1" class="more hidden">more infobox 1</div>
<div id="more-info-box-pr-2" class="more hidden">more infobox 2</div>
<div id="more-info-box-pr-3" class="more hidden">more infobox 3</div>

Javascript Modal with dynamic XML Content

I have an XML DB with movies. Each movies comes with a picture. On click on the picture modal opens and in the modal you can see the information about the movie. With the following code I am able to iterate over all my pictures, which are buttons.
btn.forEach(element => {
element.addEventListener("click", () => {
modal.style.display = "block";
} );
} );
Problem is, this opens the same modal all the time, meaning, the content is not dynamically changing, depending on the movie I click. I tried the following
btn.forEach(element => {
element.addEventListener("click", () => {
modal.forEach(item => {
item.style.display = "block";
});
});
});
But this changes the content to the last on the data base, showing again same modal all the time and also breaking the close span for all modals. How do I show a correct modal for each movie?
I would recommend not to use arrow functions here because of context or so to say binding 'this' the best way is to use a plain old function and some DOM traversing
The thing is you havent supplied code for btn so I don't know how are you selcting all buttons but I would do it like so:
Array.from(document.getElementsByClassName('btn')).map(btn => btn.addEventListener('click', function(){
const theContainer = this.closest('.container')
theContainer.getElementsByClassName('modal')[0].classList.add('show')
theContainer.getElementsByClassName('close')[0].onclick = function(){
this.closest('.modal').classList.toggle('show')
}
}))
.modal {
display: none;
}
.show {
display: block;
border: 1px solid red;
}
<div class='container'>
<div class='modal'>
<p> modal </p>
<button class='close'>X</button>
</div>
<button class='btn'>0</button>
</div>
<div class='container'>
<div class='modal'>
<p> modal 1</p>
<button class='close'>X</button>
</div>
<button class='btn'>1</button>
</div>
<div class='container'>
<div class='modal'>
<p> modal 2</p>
<button class='close'>X</button>
</div>
<button class='btn'>2</button>
</div>
<div class='container'>
<div class='modal'>
<p> modal 3</p>
<button class='close'>X</button>
</div>
<button class='btn'>3</button>
</div>

find out which button was pressed in the foreach loop

I return to the user a list of his notes, surely that there may be an indefinite number of them.
I create buttons when the text is longer than 80 characters.
By clicking on this button, I want to catch a click among other buttons and find out all the information about his parent div. How can i do this?
<div class="d-flex flex-wrap">
#foreach (var item in ViewBag.UserTodoList)
{
<div class="card" style="width: 32%; margin-left: 1%; margin-top:1%">
<div class="card-body">
<h5 class="card-title">#item.Name</h5>
#if (item.Body.Length > 80)
{
<p class="card-text">#item.Body.Substring(0, 80)...</p>
<button class="btn btn-info" id="1234">Прочитать</button>
}
else
{
<p class="card-text">#item.Body</p>
}
<a asp-controller="ToDo" asp-action="DeleteTodos" asp-route-todoId="#item.Id" class="btn btn-primary">Удалить</a>
</div>
</div>
}
</div>
Something like this:
const buttons = Array.from(document.querySelectorAll('.card .btn'));
function handleClick() {
const parent = this.parentNode; // here is parent
console.log(parent) // lets log it to console
}
buttons.forEach((button) => { button.addEventListener('click', handleClick);})
BTW, you should add unique class to your buttons. It will be easier to get them by querySelectorAll or getElementsByClassName
I am not expert in this (new one to it too) but how i would do it is add that button some class like ErrorClass and then inside script i would do
$('.ErrorButton').click(function() {
var parent = this.parent(); // With this you get parent div container
});

How to show container on click with Localstorage?

I'm trying to show container on click with localstorage. I mean that when you click the button, CSS classess:is-hidden and is-visible will be saved and remembered by browser.
This is my code:
var comments = document.getElementById("js-comments");
if (comments) {
comments.addEventListener("click", function() {
localStorage.setItem('comments', 'true');
comments.classList.add("is-hidden");
var container = document.getElementById("js-comments__inner");
container.classList.add("is-visible");
if (localStorage.getItem('comments') == 'true') {
container.classList.add("is-visible");
}
});
}
HTML markup:
<div class="comments">
<button class="comments__button" id="js-comments">Load comments</button>
<div class="comments__inner" id="js-comments__inner">
<h3 class="h4">Comments</h3>
</div>
</div>
Idea is: when you click on the button and the comments__inner elements then they receive the CSS clasess.

Get id of main div using JavaScript

How can I get the div id for a button and identify whether it's within one of two possible ids? For example, we have a call-to-action button that could be inside a div with the id="new" or id="current". Here are a few examples:
<div id="new">
Download
</div>
or
<div id="current">
Download
</div>
It's possible the id could be in a parent or parent's parent div, such as this:
<div id="new">
<div class="something">
Download
</div>
</div>
or this:
<div id="new">
<div class="row">
<div class="col-md-6">
Download
</div
</div>
</div>
We'd like our landing page developers to be able to develop the pages without having to ever touch the JavaScript for this functionality. We're ultimately trying to pass along this value in a URL string, such as this:
fileref.setAttribute("src", "https://oururl.html?cStatus=" + cStatus);
Make this slight modification: onclick="cStatus(this)" and then:
function cStatus(elem) {
var els = [];
while (elem) {
els.unshift(elem);
elem = elem.parentNode;
if (elem.id == "new") {
// has new
break;
} else if (elem.id == "current") {
// has current
break;
}
}
}
In the onclick callback you can get the parent element using $(this).parent() and then check its id.

Categories

Resources