How to Update getElementsByClassName after doing something? - javascript

if (document.querySelector(".delete-filter") !== null) {
let dltbtn = document.getElementsByClassName("delete-filter");
let contbtn = document.getElementsByClassName("filter-solid");
for (let i = 0; i < dltbtn.length; i++) {
dltbtn[i].onclick = function() {
contbtn[i].remove();
}
}
}
.filter-solid {
display: inline-block;
border: 1px solid #faa938;
border-radius: 2vw;
font-size: 13px;
padding: 5px 8px;
color: #525666;
margin: 4px 0;
}
.filter-solid button {
border: none;
background: none;
color: #525666;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha512-SfTiTlX6kk+qitfevl/7LibUOeJWlt9rbyDn92a1DqWOw9vWG2MFoays0sgObmWazO5BQPiFucnnEAjpAB+/Sw==" crossorigin="anonymous" referrerpolicy="no-referrer">
<div>
<span class="filter-solid"><span> فقط کالاهای تخفیف دار </span> <button class="delete-filter"><i class="fa fa-times"></i></button></span>
<span class="filter-solid"><span> از <span>200,000</span> تا <span>1,200,000</span> تومان </span> <button class="delete-filter"><i class="fa fa-times"></i></button></span>
<span class="filter-solid"><span> رنگ آبی </span> <button class="delete-filter"><i class="fa fa-times"></i></button></span>
</div>
In above code I want to remove an element after click, but the problem is when I delete elements (from left) the count of dltbtn and contbtn is not updating. I mean when I have 4 elements there is [0, 1, 2, 3] array, so when I delete first element the array should be [0, 1, 2] in order, but it will not be updated. how should I fix this?

If you use .addEventListener() instead of .onclick, and DOM traversal to find the container of the clicked button, you don't need dltbtn, contbtn or an index.
this in the event handler assigned with .addEventListener() is the clicked element -> the button.
With .closest(".filter-solid") we travel the DOM up to the first element that matches the selector ".filter-solid" (in this case .parentNode) would do the same -> the container that should be removed.
document.querySelectorAll(".delete-filter")
.forEach(function(btn) {
btn.addEventListener("click", function() {
const container = this.closest(".filter-solid");
if (container) {
container.remove();
}
});
})
.filter-solid {
display: inline-block;
border: 1px solid #faa938;
padding: 5px 8px;
margin: 4px 0;
}
<div>
<span class="filter-solid"><span> فقط کالاهای تخفیف دار </span> <button class="delete-filter"><i class="fa fa-times">X</i></button></span>
<span class="filter-solid"><span> از <span>200,000</span> تا <span>1,200,000</span> تومان </span> <button class="delete-filter"><i class="fa fa-times">X</i></button></span>
<span class="filter-solid"><span> رنگ آبی </span> <button class="delete-filter"><i class="fa fa-times">X</i></button></span>
</div>

The main issue is that getElementsByClassName() returns a live collection. To quote the MDN page:
Warning: This is a live HTMLCollection. Changes in the DOM will reflect in the array as the changes occur. If an element selected by this array no longer qualifies for the selector, it will automatically be removed. Be aware of this for iteration purposes.
Meaning that contbtn[i].remove() will implicitly remove the current element from the array (because it is removed from the DOM), shifting all elements that come after it.
An easy change would be swapping from getElementsByClassName() to querySelectorAll() which does not not return a live collection.
let dltbtn = document.querySelectorAll(".delete-filter");
let contbtn = document.querySelectorAll(".filter-solid");
With this change elements in the resulting collection won't shift when contbtn[i].remove() is called.
Another good solution in this scenario would be to use event delegation. This means adding an event listener to the wrapping <div> which checks if the .delete-filter is clicked and removes the associated .filter-solid.
This also simplifies adding new elements, since you no longer need to add event listeners on those new elements.
const containers = document.querySelectorAll(".filter-solid-container");
for (const container of containers) {
container.addEventListener("click", function ({ target }) {
// only search within the container by addeding the container selector
const dlt = target.closest(".filter-solid-container, .delete-filter");
// return if not clicked on .delete-filter
// (`dlt` will be set to the container)
if (dlt == container) return;
// I assume that .filter-solid is always present around a .delete-filter
// find and remove it
dlt.closest(".filter-solid").remove();
});
}
// demo: adding a new filter without fumbling with event handlers
const addFilter = document.querySelector("#add-filter");
addFilter.addEventListener("click", function () {
containers[0].innerHTML += `
<span class="filter-solid">
<span>test</span>
<button class="delete-filter"><i class="fa fa-times"></i></button>
</span>
`;
});
.filter-solid {
display: inline-block;
border: 1px solid #faa938;
border-radius: 2vw;
font-size: 13px;
padding: 5px 8px;
color: #525666;
margin: 4px 0;
}
.filter-solid button {
border: none;
background: none;
color: #525666;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha512-SfTiTlX6kk+qitfevl/7LibUOeJWlt9rbyDn92a1DqWOw9vWG2MFoays0sgObmWazO5BQPiFucnnEAjpAB+/Sw==" crossorigin="anonymous" referrerpolicy="no-referrer">
<div class="filter-solid-container">
<span class="filter-solid"><span> فقط کالاهای تخفیف دار </span> <button class="delete-filter"><i class="fa fa-times"></i></button></span>
<span class="filter-solid"><span> از <span>200,000</span> تا <span>1,200,000</span> تومان </span> <button class="delete-filter"><i class="fa fa-times"></i></button></span>
<span class="filter-solid"><span> رنگ آبی </span> <button class="delete-filter"><i class="fa fa-times"></i></button></span>
</div>
<button id="add-filter" type="button">add filter</button>

Related

Toggle <span> text which contains inner HTML element

On the button click, I want to toggle its text from 'View More' to 'View Less'. However, the span element contains another element inside of it (Font Awesome icon). When I toggle text of a span element, the element inside of it disappears. You can see it in the snippet below.
I also tried this solution:
$('.button span').text(($('.button span').text()=='View More<i class="fas fa-angle-down"></i>') ? 'View Less<i class="fas fa-angle-up"></i>' : 'View More<i class="fas fa-angle-down"></i>');
But it inserts the i element as a span text, instead as an element.
NOTE: I can't keep the i element outside of the span element because I have certain animations in CSS.
$('.button').click(function() {
$('.button i').toggleClass('fa-angle-up fa-angle-down');
$('.button span').text(($('.button span').text()=='View More') ? 'View Less' : 'View More');
});
.button {
width: 250px;
padding: 12px 0;
text-transform: uppercase;
cursor: pointer;
}
.button i {
margin-left: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a671c6b423.js" crossorigin="anonymous"></script>
<button type="button" name="button" value="" class="button">
<span>View More<i class="fas fa-angle-down"></i></span>
</button>
$('.button').click(function() {
$('.button span').html(($('.button span').text()=='View More') ? 'View Less<i class="fas fa-angle-up">' : 'View More<i class="fas fa-angle-down">');
});
.button {
width: 250px;
padding: 12px 0;
text-transform: uppercase;
cursor: pointer;
}
.button i {
margin-left: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a671c6b423.js" crossorigin="anonymous"></script>
<button type="button" name="button" value="" class="button">
<span>View More<i class="fas fa-angle-down"></i></span>
</button>
This can be fixed using the html() function of JQuery. That will have the same functionality as the text() function, but adds the option to set html elements.
This link provides you with the JQuery documentation.
what about to wrap the text which you want to change into an own span with an id? then you could change the text without touching the fontawesome icon
I would simply add a data attribute to the texts and toggle only that portion instead of trying to complicate things, like this:
$('.button').click(function() {
$('.button i').toggleClass('fa-angle-up fa-angle-down');
const currentText = $('[data-view-more]').text();
const updatedText = currentText === 'View More' ? 'View Less' : 'View More';
$('[data-view-more]').text(updatedText);
});
.button {
width: 250px;
padding: 12px 0;
text-transform: uppercase;
cursor: pointer;
}
.button i {
margin-left: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a671c6b423.js" crossorigin="anonymous"></script>
<button type="button" name="button" value="" class="button">
<span>
<span data-view-more>View More</span>
<i class="fas fa-angle-down"></i>
</span>
</button>
As you can see I now target data-view-more text when toggling the text and then I target the icon to toggle that.
Just for fun and for the idea: no jquery, use event delegation and a data-attribute to toggle text and icon
document.addEventListener("click", handle);
function handle(evt) {
const bttn = evt.target.closest("[data-view]");
return bttn
? toggleBttn(bttn, bttn.dataset.view === "more")
: true;
}
function toggleBttn(bttn, isClosed) {
let classes = ["fa-angle-down", "fa-angle-up"];
classes = isClosed ? classes : classes.reverse();
bttn.querySelector("i").classList.replace(...classes);
bttn.dataset.view = isClosed ? "less" : "more";
}
.button {
width: 250px;
padding: 12px 0;
text-transform: uppercase;
cursor: pointer;
}
[data-view]:before {
content: 'view 'attr(data-view);
margin-right: 10px;
}
<script src="https://kit.fontawesome.com/a671c6b423.js" crossorigin="anonymous"></script>
<button type="button" data-view="more" class="button">
<i class="fas fa-angle-down"></i>
</button>
When you keep the icon out of the span, it works like you expect it:
<button type="button" name="button" value="" class="button">
<span>View More></span>
<i class="fas fa-angle-down"></i
</button>

How to change class name of <i> element on click which calls two functions?

I am new in javascript. I have created a click to copy button. It should show the check icon after click on the button. The code for "copy function" works well but the code to change icon after click event did not work.
There will be multiple copy buttons and each has unique ID attribute. I want to change the "Copy icon" to "Check icon" after clicked. I think the simple way is to change the class name in
<i class="far fa-copy" id="classTest1"></i>
from (class="far fa-copy") to (class="fas fa-check"), but the problem is the changeClass function does not understand which class of the clicked button to change the class. The code changed the class of all elements.
Also can I combine 2 functions copyToClipboard(elementId) and changeClass() into 1 function?
My website could not run with jQuery and I do not know what happen, I am newbie.
Thank you so much for your help.
function copyToClipboard(elementId) {
var aux = document.createElement("input");
aux.setAttribute("value", document.getElementById(elementId).innerHTML);
document.body.appendChild(aux);
aux.select();
document.execCommand("copy");
document.body.removeChild(aux);
}
function changeClass(elementId) {
var x = document.getElementById("elementId");
x.className = "fas fa-check";
}
.copybutton {
cursor: pointer;
font-size: 14px;
color: #fff;
padding: 12px 10px;
text-decoration: none !important;
background: #333333;
background-color: rgb(51, 51, 51);
border: none;
transition: all .6s cubic-bezier(0.77,0,0.175,1);
}
.copybutton {
color:#ffffff;
}
.copybutton:hover {
background: #c2a693;
transition: all .6s cubic-bezier(0.77,0,0.175,1);
}
.copybutton:focus {
outline: 0;
}
<html>
<head><script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js" integrity="sha256-KzZiKy0DWYsnwMF+X1DvQngQ2/FxF7MF3Ff72XcpuPs=" crossorigin="anonymous"></script>
</head>
<body>
<h2>Finding HTML Elements by Tag Name</h2>
<div id="main">
<p>This example test.</p>
</div>
<div>
<h6 class="copy">Button 1: test 1 <span id="test1" style="display: none;">Test 1 copied</span> <button class="copybutton" onclick="copyToClipboard('test1');changeClass('classTest1')"><i class="far fa-copy" id="classTest1"></i></button></h6>
<h6 class="copy">Button 2: test 2 <span id="test2" style="display: none;">Test 2 copied</span> <button class="copybutton" onclick="copyToClipboard('test2');changeClass('classTest2')"><i class="far fa-copy" id="classTest2"></i></button></h6>
<p>Paste here: <input value="paste here for test copy function"></p>
</div>
</body>
</html>
Actually you can do that in two ways, the first like you did but I have changed the code, you can add as many span element to test, my code is dynamic don't worry
Solution1
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>Clipboard</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
button {
cursor: pointer;
font-size: 14px;
color: #fff;
padding: 12px 10px;
background-color: rgb(51, 51, 51);
border: none;
padding: 5px;
border-radius: 40%;
}
button:hover {
background: #c2a693;
transition: all .6s cubic-bezier(0.77,0,0.175,1);
}
button:focus {
outline: 0;
}
span {
display: none;
}
input {
width: 200px;
padding-left: 10px;
}
</style>
</head>
<body>
<h2>Finding HTML Elements by Tag Name</h2>
<p>This example test.</p>
<div id="container">
<h6 >Button 1: test 1 <span>Test 1 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 2: test 2 <span>Test 2 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 3: test 3 <span>Test 3 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 4: test 4 <span>Test 4 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 5: test 5 <span>Test 5 copied</span><button><i class="fa fa-copy"></i></button></h6>
<p>Paste here: <input placeholder="paste here for test copy function"></p>
</div>
<script>
document.querySelector("#container").onclick = function(e) {
if(e.target.nodeName === "I") {
var tmpInput = document.createElement("input");
document.body.appendChild(tmpInput);
tmpInput.value = e.target.parentElement.previousElementSibling.innerText;
tmpInput.select();
document.execCommand("copy");
document.querySelectorAll("i").forEach(function(element) {
element.className = "fa fa-copy";
});
e.target.className = "fa fa-check";
document.body.removeChild(tmpInput);
}
}
</script>
</body>
</html>
Solution2 edited for 2 seconds timeout and selectors
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>Clipboard</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
button {
cursor: pointer;
font-size: 14px;
color: #fff;
padding: 12px 10px;
background-color: rgb(51, 51, 51);
border: none;
padding: 5px;
border-radius: 40%;
}
button:hover {
background: #c2a693;
transition: all .6s cubic-bezier(0.77,0,0.175,1);
}
button:focus {
outline: 0;
}
span {
display: none;
}
input {
width: 200px;
padding-left: 10px;
}
</style>
</head>
<body>
<h2>Finding HTML Elements by Tag Name</h2>
<p>This example test.</p>
<div id="container">
<h6 >Button 1: test 1 <span>Test 1 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 2: test 2 <span>Test 2 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 3: test 3 <span>Test 3 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 4: test 4 <span>Test 4 copied</span><button><i class="fa fa-copy"></i></button></h6>
<h6 >Button 5: test 5 <span>Test 5 copied</span><button><i class="fa fa-copy"></i></button></h6>
<p>Paste here: <input placeholder="paste here for test copy function"></p>
</div>
<div>
<button><i class="fa fa-copy"></i></button>
<button><i class="fa fa-check"></i></button>
<button><i class="fa fa-address-card"></i></button>
<button><i class="fa fa-warning"></i></button>
<button><i class="fa fa-wifi"></i></button>
</div>
<script>
var copiedText = "", clickedElement, currentTask;
document.querySelector("#container").onclick = function(e) {
if(e.target.nodeName === "I") {
copiedText = e.target.parentElement.previousElementSibling.innerText;
document.execCommand("copy");
document.querySelectorAll("#container i").forEach(function(element) {
element.className = "fa fa-copy";
});
e.target.className = "fa fa-check";
clickedElement = e.target;
}
}
document.body.oncopy = function(e) {
event.clipboardData.setData('text/plain', copiedText);
event.preventDefault();
clearTimeout(currentTask);
currentTask = setTimeout(function() {
clickedElement.className = "fa fa-copy";
}, 2000);
};
</script>
</body>
</html>
As a temporary solution i can offer you this one
function someFunc(test, classTest) {
copyToClipboard(test);
changeClass(classTest);
}
And onclick add this function.
<h6 class="copy">Button 1: test 1 <span id="test1" style="display: none;">Test 1 copied</span> <button class="copybutton" onclick="someFunc('test1', 'classTest1')"><i class="far fa-copy" id="classTest1"></i></button></h6>
If you want to combine several functions for one element use addEventListener('${action}', callbackFunction)
You can learn more about it here https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Also do not use same id for different elements, as you did for spans. Id always should be unique.
Your code can not work. First you have to use unique IDs in your HTML:
<h6 class="copy">Button 1: test 1 <span id="test1" style="display: none;">Test 1 copied</span> <button id="button2" class="copybutton" )"><i class="far fa-copy" id="classTest1"></i></button></h6>
<h6 class="copy">Button 2: test 2 <span id="test2" style="display: none;">Test 2 copied</span> <button id="button2" class="copybutton"><i class="far fa-copy" id="classTest2"></i></button></h6>
Of course you can combine different actions into one function call. For readability of your code and reusability keep different functions seperated and call them inthe clickevent. Example:
/* This ensures that all elements are in DOM = ready */
if (document.readyState!="loading") docReady();
/* Modern browsers */
else document.addEventListener("DOMContentLoaded", docReady);
function docReady() {
// Since both elements are the same except for the ids we do it in a loop
for(var i = 1; i <= 2; i++) {
var clickElement = "button" + i;
document.getElementById(clickElement).addEventListener("click", function(event){ // This is the function which is called when the button is clicked
copyToClipboard("test" + i); // since per button we want to hand over the related id
changeClass("classTest"+i);
});
}
}
function copyToClipboard(elementId) {
var aux = document.createElement("input");
aux.setAttribute("value", document.getElementById(elementId).innerHTML);
document.body.appendChild(aux);
aux.select();
document.execCommand("copy");
document.body.removeChild(aux);
}
function changeClass(elementId) {
var x = document.getElementById("elementId");
x.className = "fas fa-check"; // look into classList.add/ classList.remove
}
I deliberatly extended your code according to your question (removing the double id bug in your html), of course there is room for improvement but hey you wrote working code yourself. So pat yourself on the back and be proud of your work.

How to get the child of a element with event.target

I'm trying to get the child elements of a div to toggle a class 'active'
JS
const dots = document.querySelectorAll('[data-dots]');
dots.forEach(dot => dot.addEventListener('click', handleClick));
function handleClick(e) {
e.target.getElementsByClassName('tb-drop').classList.toggle('active');
console.log(e.target.getElementsByClassName('tb-drop'))
}
HTML
<div class="dots" data-dots>
<i class="fas fa-ellipsis-v dots"></i>
<div class="tb-drop">
<i class="far fa-edit icon-grid"></i>
<i class="fas fa-link icon-grid"></i>
</div>
</div>
So i'm selecting all the divs with the data-dots attribute and then select the child of that div and add the class active. I tried with e.target.children but didnt work.
Thanks, I'm just trying to learn :)
In order to identify the first child, the easiest option is simply to use Element.querySelector() in place of Element.getElementsByClassName():
const dots = document.querySelectorAll('[data-dots]');
dots.forEach(dot => dot.addEventListener('click', handleClick));
function handleClick(e) {
// Element.querySelector() returns the first - if any -
// element matching the supplied CSS selector (element,
// elements):
e.target.querySelector('.tb-drop').classList.add('active');
}
The problem is, of course, that if no matching element is found by Element.querySelector() then it returns null; which is where your script will raise an error. So, with that in mind, it makes sense to check that the element exists before you try to modify it:
const dots = document.querySelectorAll('[data-dots]');
dots.forEach(dot => dot.addEventListener('click', handleClick));
function handleClick(e) {
let el = e.target.querySelector('.tb-drop');
if (el) {
el.classList.add('active');
}
}
It's also worth noting that EventTarget.addEventListener() passes the this element into the function, so rather than using:
e.target.querySelector(...)
it's entirely possible to simply write:
this.querySelector(...)
Unless, of course, handleClick() is rewritten as an Arrow function.
Demo:
const dots = document.querySelectorAll('[data-dots]');
dots.forEach(dot => dot.addEventListener('click', handleClick));
function handleClick(e) {
let el = e.target.querySelector('.tb-drop');
if (el) {
el.classList.add('active');
}
}
div {
display: block;
border: 2px solid #000;
padding: 0.5em;
}
i {
display: inline-block;
height: 1em;
}
::before {
content: attr(class);
}
.active {
color: limegreen;
}
<div class="dots" data-dots>
<i class="fas fa-ellipsis-v dots"></i>
<div class="tb-drop">
<i class="far fa-edit icon-grid"></i>
<i class="fas fa-link icon-grid"></i>
</div>
</div>
Or, if you wish to toggle the 'active' class you could, instead, use toggle() in place of add:
const dots = document.querySelectorAll('[data-dots]');
dots.forEach(dot => dot.addEventListener('click', handleClick));
function handleClick(e) {
let el = e.target.querySelector('.tb-drop');
if (el) {
el.classList.toggle('active');
}
}
div {
display: block;
border: 2px solid #000;
padding: 0.5em;
}
i {
display: inline-block;
height: 1em;
}
::before {
content: attr(class);
}
.active {
color: limegreen;
}
<div class="dots" data-dots>
<i class="fas fa-ellipsis-v dots"></i>
<div class="tb-drop">
<i class="far fa-edit icon-grid"></i>
<i class="fas fa-link icon-grid"></i>
</div>
</div>
References:
Element.querySelector
e.target already is the clicked child of the element that you installed the listener on. You probably want to use e.currentTarget or this instead.
Then you can go using .getElementsByClassName(), .querySelector[All]() or .children from there.
You can also try this code.
var dots = document.querySelectorAll('[data-dots]');
for (var i = 0; i < dots.length; i++) {
dots[i].addEventListener('click', function () {
handleClick(this);
}, false);
}
function handleClick(object) {
var container = object.getElementsByClassName('tb-drop')[0];
if (container != undefined) {
if (container.classList.contains('active')) {
container.classList.remove('active')
}else{
container.classList.add('active')
}
}
}

JS code to show / hide all parent / child divs on a page

I have a simple bit of JS used to show / hide DIVs:
function HideShow(e, itm_id) {
var tbl = document.getElementById(itm_id);
if (tbl.style.display == ""){
e.innerHTML = "<i class='fa fa-plus' aria-hidden='true'></i>";
tbl.style.display = "none"; }
else {
e.innerHTML = "<i class='fa fa-minus' aria-hidden='true'></i>";
tbl.style.display = ""; }
}
This is a working example of the code on Codepen: Show Hide Divs without jQuery
This is an example of one section:
<div id="activities" style="margin-bottom:50px;">
<div style="color: #000; background: #eee; border-bottom:1px solid #ccc; padding:5px;">
<h1 class="heading"><i class='fa fa-minus' aria-hidden='true'></i> Activities <span style="color:#ccc;"></span></h1>
</div>
<div id="parent_activities" style="background: #fff; padding:20px;">
<div id="activities__award-medal" style="background: #fff; padding-left:10px; background:#f1f1f1; border-top:1px solid #fff; font-size:30px;"><i class='fa fa-minus' aria-hidden='true'></i> award-medal <span style="color:#ccc;"></span></div>
<div id="child_award-medal" style="background: #fff; padding:20px;">
<ul class="gallery grid">
<li>
<a href="#">
<img title="military medal - 🎖️" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f396.svg" style="width:64x; height:64px" role="presentation">
</a>
</li>
</ul>
</div>
<div id="activities__event" style="background: #fff; padding-left:10px; background:#f1f1f1; border-top:1px solid #fff; font-size:30px;"><i class='fa fa-minus' aria-hidden='true'></i> event <span style="color:#ccc;"></span></div>
<div id="child_event" style="background: #fff; padding:20px;">
<ul class="gallery grid">
<li>
<a href="#">
<img title="jack-o-lantern - 🎃" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f383.svg" style="width:64x; height:64px" role="presentation">
</a>
</li>
</ul>
</div>
</div>
</div>
The top level example has an id of parent_activities and then there are two child values:
child_award-medal
child_event
I'd like to work out how to add two links:
A link to toggle the HideShow function for the parents so that all divs with an ID starting with parent_ are shown / hidden
A link to toggle the HideShow function for the children so that all divs with an ID starting with child_ are shown / hidden
I'm not sure how I'd go about that though.
Any advice much appreciated. Thanks
Note: this isn't a fully complete solution. The intention is to assist you in the parts that are giving you pause.
Try not to embed JavaScript in your HTML body; it's unnecessary markup and makes it difficult to track down and debug errors. I did not change your existing calls, but demonstrate how it can be done by using addEventListener with the newer code
You can target your elements using document.querySelectorAll and looking for the prefix you're interested in (e.g., parent_, child_). Which prefixes to use have been added to the links in the data-selector attributes
because the toggling action is not going to another page, these should be buttons or spans
to hide elements, you can use the Bootstrap display classes, as I have used d-none which stands for display none. The Bootstrap library provides these to make it especially easier for responsive layouts
many of your inline-CSS should be in classes, this is to both reduce your markup and make it more organized
// So forEach can be used on 'querySelectorAll' and 'getElementsByClassName' collections
HTMLCollection.prototype.forEach = NodeList.prototype.forEach = Array.prototype.forEach;
function HideShow(e, itm_id) {
var tbl = document.getElementById(itm_id);
if (tbl.style.display == "") {
e.innerHTML = "<i class='fa fa-plus' aria-hidden='true'></i>";
tbl.style.display = "none";
} else {
e.innerHTML = "<i class='fa fa-minus' aria-hidden='true'></i>";
tbl.style.display = "";
}
}
// -----------------------------------------------------------
// NEW Code
// New toggle links
let toggles = document.getElementsByClassName('toggler');
// Attach click event
toggles.forEach(link => link.addEventListener('click', fnToggleElement))
// Event handler definition
function fnToggleElement() {
let elements = document.querySelectorAll(`[id^="${this.dataset.selector}"]`)
let className = 'd-none'
elements.forEach(el => {
let fas = el.parentElement.closest('.item,.sub-container,.menu-container').querySelectorAll('.fa')
if (el.classList.contains(className)) {
el.classList.remove(className)
fas.forEach(fa => {
fa.classList.remove('fa-plus')
fa.classList.add('fa-minus')
})
} else {
el.classList.add(className)
fas.forEach(fa => {
fa.classList.remove('fa-minus')
fa.classList.add('fa-plus')
})
}
})
}
.menu-container {
margin-bottom: 50px;
}
.sub-container {
padding: 20px;
}
.heading {
color: #000;
background: #eee;
border-bottom: 1px solid #ccc;
padding: 5px;
}
.indent {
background: #fff;
padding: 20px;
}
.icon {
width: 64px;
height: 64px;
}
.item {
background: #fff;
padding-left: 10px;
background: #f1f1f1;
border-top: 1px solid #fff;
font-size: 30px;
}
.toggler {
cursor: pointer;
display: inline-block;
}
.gallery {
width: 100%;
*width: 99.94877049180327%;
margin: 0;
padding: 0;
}
.gallery.grid li {
margin: 2px 5px;
}
.gallery.grid li {
margin: 2px 5px;
display: block;
}
.gallery.grid li:hover {
background: #ccc;
}
.gallery.grid li {
display: inline-block;
border-top: 1px solid #eee;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
border-left: 1px solid #eee;
padding: 6px;
position: relative;
-moz-box-sizing: border-box;
border-radius: 3px 3px 3px 3px;
background: #fff;
}
.gallery a {
display: block;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<div class="container-fluid">
<div class="row">
<div class="col-sm"><span class="toggler btn-link" data-selector="parent_">Toggle Parents</span></div>
<div class="col-sm"><span class="toggler btn-link" data-selector="child_">Toggle Children</span></div>
</div>
</div>
<div class="container-fluid">
<div id="activities" class="menu-container">
<h1 class="heading">
<a href="javascript:;" onclick="HideShow(this,'parent_activities')">
<i class='fa fa-minus' aria-hidden='true'></i>
</a> Activities
<span style="color:#ccc;"></span>
</h1>
<div id="parent_activities" class="sub-container">
<div id="activities__award-medal" class="item">
<a href="javascript:;" onclick="HideShow(this,'child_award-medal')">
<i class='fa fa-minus' aria-hidden='true'></i>
</a> award-medal
<span style="color:#ccc;"></span>
</div>
<div id="child_award-medal" class="indent">
<ul class="gallery grid">
<li>
<a href="# ">
<img title="military medal - 🎖️" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f396.svg " class="icon" role="presentation">
</a>
</li>
</ul>
</div>
<div id="activities__event " class="item">
<a href="javascript:; " onclick="HideShow(this, 'child_event') ">
<i class='fa fa-minus' aria-hidden='true'></i>
</a> event
<span style="color:#ccc; "></span>
</div>
<div id="child_event " class="indent">
<ul class="gallery grid ">
<li>
<a href="# ">
<img title="jack-o-lantern - 🎃" src="https://cdn.jsdelivr.net/emojione/assets/svg/1f383.svg" class="icon" role="presentation">
</a>
</li>
</ul>
</div>
</div>
</div>
<div id="animals-nature" class="menu-container">
<h1 class="heading"><i class='fa fa-minus' aria-hidden='true'></i> Animals & Nature <span style="color:#ccc;"></span></h1>
<div id="parent_animals-nature" class="sub-container">
<div id="animals-nature__animal-amphibian " class="item ">
<a href="javascript:;" onclick="HideShow(this, 'child_animal-amphibian')">
<i class='fa fa-minus' aria-hidden='true'></i>
</a> animal-amphibian
<span style="color:#ccc;"></span>
</div>
<div id="child_animal-amphibian" class="indent">
<ul class="gallery grid">
<li>
<a href="# ">
<img title="frog face - 🐸 " src="https://cdn.jsdelivr.net/emojione/assets/svg/1f438.svg " style="width:64x; height:64px " role="presentation ">
</a>
</li>
</ul>
</div>
<div id="animals-nature__animal-bird " class="item">
<a href="javascript:;" onclick="HideShow(this, 'child_animal-bird')">
<i class='fa fa-minus' aria-hidden='true'></i>
</a> animal-bird
<span style="color:#ccc;"></span>
</div>
<div id="child_animal-bird" class="indent">
<ul class="gallery grid">
<li>
<a href="# ">
<img title="turkey - 🦃 " src="https://cdn.jsdelivr.net/emojione/assets/svg/1f983.svg " style="width:64x; height:64px " role="presentation ">
</a>
</li>
</ul>
</div>
</div>
</div>
Try the following selector and apply:
document.querySelectorAll('[id^="child_"]')
See the below snippet for an example:
function toggleIdStartingWith( prefix = 'parent_' ){
// Select all IDs starting with prefix and turn this NodeList into an array
// so we can loop through it easily later.
var all = [...document.querySelectorAll(`[id^="${prefix}"]`)];
// Determine whether we want to turn them on or off by
// checking the first element. You might want to also check
// if any elements are found at all before doing this.
var hidden = all[ 0 ].style.display === 'none';
// Apply the display style to all.
all.forEach(element => {
element.style.display = hidden ? '' : 'none';
});
// Return the inverted hidden value, which is what we applied.
// Useful if you want to toggle stuff, and then see what the result
// was in the code that called the function.
return !hidden;
}
// For testing purposes I am hooking two buttons up for testing this.
document.getElementById('hideshow_parents').addEventListener( 'click', event => {
event.preventDefault()
event.target.textContent = toggleIdStartingWith( 'parent_' )
? 'Show all Parents'
: 'Hide all Parents'
})
document.getElementById('hideshow_children').addEventListener( 'click', event => {
event.preventDefault()
event.target.textContent = toggleIdStartingWith( 'child_' )
? 'Show all Children'
: 'Hide all Children'
})
<div id="parent_1">Parent</div>
<div id="child_1">Child</div>
<div id="parent_2">Parent</div>
<div id="child_2">Child</div>
<div id="parent_3">Parent</div>
<div id="child_3">Child</div>
<div id="parent_4">Parent</div>
<div id="child_4">Child</div>
<div id="parent_5">Parent</div>
<div id="child_5">Child</div>
<button id="hideshow_parents">Hide/Show Parents</button>
<button id="hideshow_children">Hide/Show Children</button>
As you asked in the comment, switching the classes depending on the toggle state is easy too. I personally think you shouldn't mix html and interactivity, so I am going to use addEventListener in my example:
function toggleIdStartingWith( prefix = 'parent_' ){
var all = [...document.querySelectorAll(`[id^="${prefix}"]`)];
var hidden = all[ 0 ].style.display === 'none';
all.forEach(element => {
element.style.display = hidden ? '' : 'none';
});
return !hidden;
}
document.querySelector('h1').addEventListener( 'click', event => {
event.preventDefault()
if( toggleIdStartingWith( 'parent_' ) ){
event.target.textContent = 'Show';
event.target.classList.remove( 'fa-minus' )
event.target.classList.add( 'fa-plus' )
} else {
event.target.textContent = 'Hide';
event.target.classList.add( 'fa-minus' )
event.target.classList.remove( 'fa-plus' )
}
})
.fa-minus:before { content: '-'; }
.fa-plus:before { content: '+'; }
<div id="parent_1">Parent</div>
<div id="parent_2">Parent</div>
<div id="parent_3">Parent</div>
<div id="parent_4">Parent</div>
<div id="parent_5">Parent</div>
<h1 class="fa-minus">Hide</h1>
If you are insistent on getting it as an onclick in your html, just wrap it in a function:
function toggle( target, prefix ){
if( toggleIdStartingWith( prefix ) ){
target.textContent = 'Show';
target.classList.remove( 'fa-minus' )
target.classList.add( 'fa-plus' )
} else {
target.textContent = 'Hide';
target.classList.add( 'fa-minus' )
target.classList.remove( 'fa-plus' )
}
}
And call it as such:
<h1 onclick="toggle( this, 'parent_); return false;'"></h1>
Also, just so you know, it might be good to return false if you are going to use onclick handlers in HTML to prevent the default events from occuring. Then you can just leave your link set to # instead of the ugly javascript:;.
You should use querySelectorAll() to select "IDs starting with...". This can be done like document.querySelectorAll('[id^="start_"]') and then you iterate through the elements applying the style to hide or show.
Check out this fiddle: https://jsfiddle.net/1c38dezk/
You should use querySelectorAll() to select "IDs starting with...".
Have a nice day

click function does not working with ng-if

If i use like following,I got what i need.. Here is the code.
<style>
.moreMenu {
position: absolute;
right: 0px;
z-index: 10;
padding: 10px;
color: #666;
border-radius: 0 0 0 4px;
background: #FFF;
display: none;
border-top: 1px solid #DDD;
}
</style>
<i class="fa fa-ellipsis-v tbButton otherOptions rightSide" aria-hidden="true"></i>
<div class="moreMenu">
heloo
</div>
<script>
$(".otherOptions").click(function () {
$(".moreMenu").slideToggle("fast");
});
</script>
But if i use ng-if condition,
<i class="fa fa-ellipsis-v tbButton otherOptions rightSide" aria-hidden="true" ng-if="member==''"></i>
click function is not working.
I solved the issue..
<i class="fa fa-ellipsis-v tbButton otherOptions rightSide" aria-hidden="true" ng-if=members==''" ng-click="mymore()"></i>
// controller
$scope.mymore = function(){
$(".moreMenu").slideToggle("fast");
}
Thanks all for the informations...
It would be better to use ng-click but if you need to use jQuery's click event, you can add a filtered click event to the body:
$('body').on('click', '.otherOptions', function() {
$('.moreMenu').slideToggle('fast');
});
this will add an event handler on the body but will only trigger if the event bubbles up from the otherOptions element. This is an approach to handling dynamic content with jQuery events
#athi is your answer is a proper answer. But as per the question I have answered here use ng-show instead of ng-if.
angular.module("a",[]).controller("ac",function($scope){
});
.otherOptions
{
color: #666;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="a" ng-controller="ac">
<input class="otherOptions" type="button" value="hello" ng-click="mymore()" ng-show=true>
<div class="moreMenu">
heloodfg
</div>
</div>
<script>
$(".otherOptions").click(function () {
$(".moreMenu").slideToggle("fast");
});
</script>

Categories