Every time when a button is clicked, a checkmark will be displayed. If the clicked button is not active, the checkmark is removed. Also when the button is clicked the "load dynamic content"-button will be displayed. There is a problem when dynamic content is loaded. After loading, checkmarks appear.But removing a checkmark does not work. I use a nested event listener for dynamic content. When I check console log after loading, button class elements has not changed. I cannot figure out how to solve this. Any advice?
const checkMarks = document.querySelectorAll('.ck');
// Nested event delegation
document.querySelector('.parent').addEventListener('click', function(e){
if(e.target.classList.contains('btn-link')){
// Hide checkmark if not active button
checkMarks.forEach(function(check, index){
check.classList.remove('show');
});
// Display checkmark
e.target.closest('div').querySelector('.ck').classList.add('show');
// Display load dynamic button
document.querySelector('.load-content').classList.add('show');
}
});
document.querySelector('.load-content').addEventListener('click', function(){
document.querySelector('.parent').innerHTML = '<div><button class="btn-link btn4">Button 4</button><span class="ck">checked</span></div><div><button class="btn-link btn5">Button 5</button><span class="ck">checked</span></div><div><button class="btn-link btn6">Button 6</button><span class="ck">checked</span></div>'
});
.parent{
padding: 30px;
}
.parent .footer{
margin-top: 30px;
margin-bottom: 30px;
display: flex;
}
.btn-link{
display: block;
width: 100px;
padding: 15px;
margin-bottom: 15px;
}
.load-content{
display: none;
padding: 15px;
}
.load-content.show{
display: block;
}
.ck{
display: none;
}
.ck.show{
display: block;
}
<div class="parent">
<div><button class="btn-link btn1">Button 1</button><span class="ck">checked</span></div>
<div><button class="btn-link btn2">Button 2</button><span class="ck">checked</span></div>
<div><button class="btn-link btn3">Button 3</button><span class="ck">checked</span></div>
</div>
<div class="load"><button class="load-content">Load dynamic content</button></div><div>
jsfiddle
According to MDN: The Element method querySelectorAll() returns a static (not live) NodeList representing a list of elements matching the specified group of selectors.
So I changed that to getElementsByClassName, also the loop process, from forEach to for loop, here you go.
const checkMarks = document.getElementsByClassName('ck'); // Changed that
// Nested event delegation
document.querySelector('.parent').addEventListener('click', function(e){
if(e.target.classList.contains('btn-link')){
// Hide checkmark if not active button
for(let i = 0; i < checkMarks.length ; i++){ // Also that changed, as forEach doesn't work for getElementsByClassName.
checkMarks[i].classList.remove('show');
};
// Display checkmark
e.target.closest('div').querySelector('.ck').classList.add('show');
// Display load dynamic button
document.querySelector('.load-content').classList.add('show');
}
});
document.querySelector('.load-content').addEventListener('click', function(){
document.querySelector('.parent').innerHTML = '<div><button class="btn-link btn4">Button 4</button><span class="ck">checked</span></div><div><button class="btn-link btn5">Button 5</button><span class="ck">checked</span></div><div><button class="btn-link btn6">Button 6</button><span class="ck">checked</span></div>'
});
.parent{
padding: 30px;
}
.parent .footer{
margin-top: 30px;
margin-bottom: 30px;
display: flex;
}
.btn-link{
display: block;
width: 100px;
padding: 15px;
margin-bottom: 15px;
}
.load-content{
display: none;
padding: 15px;
}
.load-content.show{
display: block;
}
.ck{
display: none;
}
.ck.show{
display: block;
}
<div class="parent">
<div><button class="btn-link btn1">Button 1</button><span class="ck">checked</span></div>
<div><button class="btn-link btn2">Button 2</button><span class="ck">checked</span></div>
<div><button class="btn-link btn3">Button 3</button><span class="ck">checked</span></div>
</div>
<div class="load"><button class="load-content">Load dynamic content</button></div><div>
The click handler on .load-content removes/replaces nodes present in your code and create new nodes. But these new nodes do not get picked up because this code:
const checkMarks = document.querySelectorAll('.ck');
Runs only one time and picks the checkmarks present at the page load. Try moving this expression inside .parent click handler to see if that helps.
Ideally you should be removing the click handler on .parent inside your .load-content click handler. Those handlers are still there and this presents memory leaks. You should be able to fix this by converting your anonymous click handler of .parent into a named function. Then you can simply use removeEventListener and addEventListener to remove and re-add the handlers.
Related
For example, if I have an element that is not wrapped within the same parent container as another div I am hovering, will it be possible to change this external element style?
<div class="externalElement"></div>
<container>
<div class="tohover"></div> </container>
How can I update
"externalElement" when I hover "tohover" div class? I've tried with jQuery but to no avail..
Here is a vanilla JavaScript answer.
Hover on the blue box to make the red box green.
onmouseover on MDN:
The onmouseover property of the GlobalEventHandlers mixin is an event handler that processes mouseover events.
The mouseover event fires when the user moves the mouse over a particular element.
onmouseout on MDN:
The onmouseout property of the GlobalEventHandlers mixin is an event handler that processes mouseout events.
The mouseout event fires when the mouse leaves an element. For example, when the mouse moves off of an image in the web page, the mouseout event is raised for that image element.
const $externalElement = document.querySelector('.externalElement')
const $tohover = document.querySelector('.tohover')
$tohover.onmouseover = () => {
$externalElement.style.backgroundColor = 'lightgreen'
}
$tohover.onmouseout = () => {
$externalElement.style.backgroundColor = 'indianred'
}
main {
display: flex;
}
.externalElement {
height: 100px;
width: 100px;
margin-left: 20px;
background: indianred;
border-radius: 5px;
transition: all 200ms;
}
.tohover {
cursor: pointer;
height: 100px;
width: 100px;
background: steelblue;
border-radius: 5px;
color: white;
font-family: sans-serif;
display: flex;
justify-content: center;
align-items: center;
}
<main>
<container>
<div class="tohover">Hover me!</div>
</container>
<div class="externalElement"></div>
</main>
By using the mouseover and mouseout functionalities, you can achieve this as below;
$('.tohover').on('mouseover', function() {
$('.externalElement').addClass('hovered')
})
$('.tohover').on('mouseout', function() {
$('.externalElement').removeClass('hovered')
})
.tohover {
border: 1px solid #ddd;
padding: 10px;
}
.externalElement {
color: red;
transition: all 0.3s ease;
}
.externalElement.hovered {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="externalElement">Here</div>
<div>
<div class="tohover">Hover me!</div>
</div>
I used the mouseover and mouseout so that it will be easy to understand. Otherwise,it can be achieved using a single function.
So there are 2 different options the first is to use the sibling selector and just go pretty specific, what I wouldn't recommend because of selector sepcifity. The second option would be using jQuery as shown in the exmaple below.
The hover function accepts 2 parameters, one is what will happen on enter, the other is what happens on leave.
I also added a solution without jquery and plain js.
$('.hover').hover(
() => $('.far').addClass('hovered'),
() => $('.far').removeClass('hovered')
)
const target = document.querySelector('.far-nonjquery');
const hover = document.querySelector('.hover')
hover.addEventListener('mouseenter', e => target.classList.add('hovered'));
hover.addEventListener('mouseleave', e => target.classList.remove('hovered'));
.hover:hover + .next {
font-size: 2rem;
}
.hovered {
color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="hover">Hover</div>
<div class="next">Next</div>
<div>
<div>
<p>
<span class="far">Far away</span>
</p>
</div>
</div>
<div>
<div>
<p>
<span class="far-nonjquery">Far away non jquery</span>
</p>
</div>
</div>
I'm trying to code a responsive button which can be placed multiple times in the same line, showing its content always below the line containing the button.
In the snippet there is a working code, but it has a small flaw: since the pseudo-class focus is used, once the button is opened it's enough to click anywhere on the screen to close it.
The usual behaviour for a button is that to close it you have to click on it, so is it possibile to get this behaviour also for this one?
I used other pseudo-classes but without success, I guess only a javascript can do the job.
.container {
position: relative;
margin: 2em;
}
.details {
display: none;
}
.collapsible:focus {
margin-bottom: 1.5em;
pointer-events: none;
}
.collapsible:focus + .details
{
display: block;
margin-top: -1.15em;
position: absolute;
left: 0;
right: 0;
background: yellow;
}
<div class=container>
You can <button class="collapsible">place</button><span class=details>yes</span> more than <button class="collapsible">one</button><span class=details>nice</span> per line, they are responsive and the content is always shown just <button class="collapsible">below</button><span class=details>cool</span> the line containing the button.
But once opened, you can close it with a click <button class="collapsible">everywhere</button><span class=details>not good</span> on the screen
</div>
Javascript for further customization
<script type="text/javascript">
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.parentElement.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
</script>
Implementation of the idea was a bit more complicated so I'll just answer.
This uses an old trick whereby a label, associated with a hidden checkbox, is used as the click target. Since clicking on a label checks or unchecks the checkbox, and there is a pseudo-class for the checked state of the checkbox, we can use that to persist the state of our styles. Credit to TylerH for his answer to the similar question Can I have an onclick effect in CSS?.
I've implemented it here by using a partial attribute selector, so in this example any checkboxes have to have an ID that begins with "demo". The checkboxes do have to have an ID for the for attribute of the label to hook onto.
.container {
position: relative;
margin: 2em;
}
.collapsible:focus {
margin-bottom: 1.5em;
pointer-events: none;
}
label {
display: inline-block;
background: lightgrey;
}
[id^="demo"] {
display: none;
}
/* details that are next to labels that are next to unchecked checkboxes are hidden */
[id^="demo"]:not(:checked)+label+.details {
display: none;
}
/* details that are next to labels that are next to checked checkboxes are displayed */
[id^="demo"]:checked+label+.details {
display: block;
position: absolute;
left: 0;
right: 0;
background: yellow;
}
/* labels that are next to unchecked checkboxes have a different color
so you can track which ones are "on" */
[id^="demo"]:checked+label {
background: blue;
color: white;
}
<div class=container>
You can <input type="checkbox" id="demo01" /><label for="demo01" >place</label><span class=details>yes</span> more than <input type="checkbox" id="demo02" /><label for="demo02">one</label><span class=details>nice</span> per line, they are responsive and the content is always shown just <input type="checkbox" id="demo03" /><label for="demo03">below</label><span class=details>cool</span> the line containing the button. But once opened, you can close
it with a click <input type="checkbox" id="demo04" /><label for="demo04">everywhere</label><span class=details>not good</span> on the screen
</div>
I have looked everywhere possible as I am trying to develop a drop down button, but instead of options then the buttons display unordered list items, but when a user click off the button the the button does not close, but in order to close the button, then the button needs to be click again.
Down below you will find the way the button is when not clicked and the way the button appears when it has been clicked.
If you also go to the following website you will see an example of the button in action by click "See our list of websites"
Button on a website for example
Any help would be greatly appreciated and thanks in advance.
Here you go, key functions I used;
.click() for the a tag or the link, the function inside the .click() will be called.
.slideToggle() the ul after the click, this would hide or show the target element depending on its state.
Then add positon:absolute to the ul so that it wouldn't affect inline elements.
$(document).ready(function() {
$(".toggle-button").click(function() {
$(this).parent().find("ul").slideToggle(function() {
// Animation complete.
});
});
})
.links-unordered {
display: inline-block;
position: relative;
}
.links-unordered {
margin-top: 20px;
min-height: 30px;
}
.links-unordered .toggle-button {
text-decoration: none;
padding: 12px 16px 12px 16px;
transition: 0.2s;
border: 1px solid black;
}
.links-unordered .toggle-button:hover,
.links-unordered .toggle-button:active,
.links-unordered .toggle-button:focus,
.links-unordered .toggle-button:visited {
text-decoration: none;
color: black;
}
.links-unordered .toggle-button:hover {
border-width: 2px;
}
.links-unordered ul {
position: absolute;
top: 10px;
margin-top: 25px;
padding-inline-start: 20px;
}
.links-unordered ul li {
line-height: 25px;
padding-left: 15px;
}
.links-unordered a {
text-decoration: none;
color: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="links-unordered">
<a class="toggle-button" href="#">SEE OUR LIST OF WEBSITES</a>
<ul style="display:none;">
<li>cdn.sc.rockstargames.com</li>
<li>lifeinvader.com</li>
<li>rockstargames.com</li>
<li>socialclub.rockstargames.com</li>
</ul>
</div>
<div class="links-unordered">
<a class="toggle-button" href="#">SEE OUR LIST OF WEBSITES</a>
<ul style="display:none;">
<li>cdn.sc.rockstargames.com</li>
<li>lifeinvader.com</li>
<li>rockstargames.com</li>
<li>socialclub.rockstargames.com</li>
</ul>
</div>
I'm not sure if I understand what you want. But here's a sample of what you asked, a button that when you click, show a list. And when you click on an item, the list goes out and you have the item. Hope this helps you. It's a simple code, but if you have questions, go ahead and ask!!
function closeList(e) {
var site = e.target.innerText;
alert(site + ' clicked!!');
document.querySelector('#dvSites').style.display = 'none';
}
function showList() {
var dvSites = document.querySelector('#dvSites');
if (dvSites.style.display === '')
return; // already visible
dvSites.style.display = '';
}
// Add eventListener to close the div
var lis = document.querySelector('#dvSites').querySelectorAll('li');
for(var i = 0; i < lis.length; i++) {
lis[i].addEventListener('click', closeList);
}
// Add eventListener to open the div
document.querySelector('#btnShow').addEventListener('click', showList);
<button id="btnShow">Show sites!!</button>
<div id="dvSites" style="display: none">
<ul>
<li>stackoverflow.com</li>
<li>www.google.com</li>
<li>www.sipmann.com</li> <!-- :) -->
</ul>
</div>
i am trying to implement accordion.
My accordion should expand on mouse hover on the "accordian head".
And also mouse click on "accordian head" should show/hide the accordion-body.
I got the show/hide working through CSS on hover.
But when i club mouse click event , the functionality is not working
here is the sample
http://jsfiddle.net/yf4W8/157/
.accordion-body{display:none;}.accordion:hover div{display:block;}
you need to change
myDivElement.style.display = none;
myDivElement.style.display = block;
to
myDivElement.style.display = "none"; //double quotes are missing
myDivElement.style.display = "block"; //double quotes are missing
Demo
I have created a working demo, please check the link below not it is working on mouse click. Replace your JavaScript code with this and remove the css properties.
function expandAccordionBody(){
var myDivElement = document.getElementById("accbody" );
var cStyle=window.getComputedStyle(myDivElement, null);
if(cStyle.display=='block'){
myDivElement.style.display='none';
}else{
myDivElement.style.display='block';
}
}
Demo
I took a liberty to change your code a bit. This code works.
Hope that this is what you meant to do....
Instead of using pure Javascript I used jQuery event click and hover.
here is the link for working code
click here for DEMO
HTML code;
<div class="accordion">
<div class="headA">
Head
</div>
<div id="accbody" class="accordion-body">
Body
</div>
</div>
CSS code;
.accordion {
border: 1px solid #444;
margin-left: 60px;
width: 30%;
}
.accordion:hover div {
display: block;
}
.accordion-body a {
background-color: green;
display: block;
color: white;
padding: 25px;
text-align: center;
}
.headA a {
text-align: center;
display: block;
background-color: yellow;
padding: 25px;
}
jQuery code;
$(document).ready(function() {
// on page load hide accordion body
var accordionBody = $('#accbody');
accordionBody.hide();
// first make on click event happening
// when user clicks on "Head" accordion "Body will show up"
$('.headA').click(function() {
if (accordionBody.is(':hidden')) {
accordionBody.slideDown(400);
} else {
accordionBody.slideUp(400);
}
});
$('.headA').hover(function() {
if (accordionBody.is(':hidden')) {
accordionBody.slideDown(400);
} else {
accordionBody.slideUp(400); // turn this off if you want only to slide down and not back up
}
});
});
===UPDATE===
If I remove the style="display: none; from the template and apply the below method as recommended below, the empty container fires when you click on any other list item. What else can be done?
I have an ul list that is dynamically created at run time using jQuery and JSON (Using inline HTML is a template). I need the background CSS style to change when a user clicks on a list item (#navItem). I've tried everything under the moon that I can think of from inline class to .appentTo(), etc. What I have below works fine for hard-coded elements but Nothing seems to work with dynamically loaded content. Whats even more confusing is that the classes in the elements within the li tag initiate...???
Any help would be appreciated. Below are my code snippets. Thnx.
HTML:
<div id="navScrollContainer" class="navContentPosition">
<ul id="navContent">
// Display as 'None' to prevent a empty containter from showing -->
<li id="navItem" class="ulFx" style="display: none;">//<-THIS NEEDS TO CHANGE ONCLICK!!
<a class="navA">
<h1 class="navH1">.</h1>
<h2 class="navH2">.</h2>
<p class="navP">.</p>
<hr class="navHR" />
</li>
</ul>
</div>
<script type="text/javascript">
$('#navScrollContainer').on('click', '.ulFx', function() {
$(this).addClass("liFx");
});
</script>
This is the Function that injects the data into the DOM as a list:
function loadNav(url, container, appendE) {
$.getJSON(url, function(data) {
$.each(data.items, function() {
var newItem = $('#' + container).clone();
// Now fill in the fields with the data
newItem.addClass('ulFx');
newItem.find("h1").text(this.label);
newItem.find("h2").text(this.title);
newItem.find("p").text(this.description);
newItem.find("a").attr("href", this.gotoURL);
newItem.children().appendTo('#' + appendE);
});
$('#navHeaderTitle').text(data.listTitle);
iniScroll('scrollNav', 'navScrollContainer');
var target = data.targetKey;
// transition("#" + pageName, "show");
});
};
The CSS that need to happen (only on that item) when the user clicks on a Item:
#-webkit-keyframes
liBG {from {
background-color: transparent
}
50% { background-color: rgba(51,102,255,0.15); }
to {
background-color: transparent
}
}
.liFx {
-webkit-animation-name: liBG;
-webkit-animation-duration: 1s;
-webkit-animation-fill-mode: forwards;
}
The Class atributes given to the li items:
.navH1 {
font-size: 18px;
color: #FFA500;
text-decoration: underline;
font-weight: bold;
margin-top: 8px;
margin-bottom: 8px;
margin-left: 15px;
}
.navH2 {
font-size: 16px;
color: #999999;
font-weight: bold;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 25px;
font-weight: bold;
}
.navP {
color: #888;
font-size: 14px;
text-align: justify;
margin-top: 5px;
margin-bottom: 16px;
margin-left: 25px;
margin-right: 10px;
}
.navA {
text-decoration: none;
}
.navHR {
border: none;
background-color: #336;
height: 1px;
}
This will watch for dynamic elements:
$(".liFx").live("click", function() {
$(this).addClass("liBG");
});
Without seeing your click handler, I can only speculate. However, generally when the problem is related to dynamic content and having them respond to stimulus, the problem lies in how you are attaching the handler.
If you use .click(), or .trigger('click'), the handler will be applied directly to the elements you are calling these functions on. That means that if the elements do not currently exist, they will not receive a handler.
The way to get around this, is to attach the event listener to a parent element that will always exist and then watch for the event propagating up from the dynamic child element. You could do this manually, by looking at the event.target, but jQuery, as usual, makes this easy for us.
The modern jQuery way of doing this is using .on() (documentation):
$('#someparent').on('click', '#childselector', function() {
// my handler code
});
jQuery then attaches a handler on #someparent, and when it sees a click that was targeted at #childselector, it fires.
If you want to apply a class to a child of #navContent, and #navContent will always exist, do this:
$('#navContent').on('click', 'li', function() {
$(this).addClass("liFx");
});
If #navContent is dynamic too, simply go higher in the DOM tree.
As a side note, I notice that the li has an id of navItem. This sounds an awful lot like a class, rather than an ID. If you are going to have more than one navItem, they cannot all have the same ID. This is what classes are for:
<li class="navItem liFx" style="display: none;">
I am not sure where is the problem, but you are trying to do something as such:
$("#navlink").on('click', function() {
$("#yourselector").css("backgroundColor", "#ddd"); //change the color
});
I added another div and an addClass() method to my function along with Jeff B's answer above. If the class is hard coded into the tag, it doesnt function.
<ul id="navContent">
<li id="navItem" style="display: none;">
<div>//ADDED THIS TAG TO EXCEPT THE CLASS
<a>
<h1>.</h1>
<h2>.</h2>
<p>.</p>
<hr/>
</a>
</div>
</li>
</ul>
In my js file:
$.each(data.items, function() {
var newItem = $('#' + container).clone();
// Now fill in the fields with the data
newItem.find("div").addClass("ulFx");//ADDED THIS METHOD
newItem.find("h1").text(this.label);
newItem.find("h2").text(this.title);
newItem.find("p").text(this.description);
newItem.find("a").attr("href", this.gotoURL);
newItem.children().appendTo('#' + appendE);
});