This question already has answers here:
How do I detect a click outside an element?
(91 answers)
Closed 2 years ago.
I am trying to make a "form" only visible after clicking another "button" with the following conditions:
The "form" should only be visible if you click the "button".
If you click either "outside" the form or click again the button: the form will become hidden again.
/* JAVASCRIPT */
const outer = document.getElementById('outer');
const inner = document.getElementById('inner')
outer.onclick = function() {
if (inner.style.visibility == 'visible') {
document.getElementById('inner').style.visibility = 'hidden';
} else {
document.getElementById('inner').style.visibility = 'visible';
}
}
const ignoreClickOnMeElement = document.getElementById('inner');
document.addEventListener('click', function(event) {
const isClickInsideElement = ignoreClickOnMeElement.contains(event.target);
if (!isClickInsideElement) {
// when uncommenting the line below everything stop working, rather then just closing if I click outside this div
/* document.getElementById('inner').style.visibility='hidden';*/
}
});
/* CSS */
* {
border: thin solid black;
}
<!-- HTML -->
<body>
<div class="outer" id="outer">
<button>Visible</button>
</div>
<div class="inner" id="inner" style="visibility: hidden;">
<form id="form">
<input type="number">
<input type="number">
</form>
</div>
</body>
If you use a delegated event listener bound to a higher node, such as the document body in this case you can determine which elements have been clicked based upon the event target.
let form=document.getElementById('form');
let bttn=document.querySelector('div.outer button');
document.body.addEventListener('click',e=>{
e.preventDefault();
e.stopPropagation();
if( ( e.target==bttn && form.parentNode.style.visibility!=='visible' ) || e.target==form || e.target.parentNode==form ){
form.parentNode.style.visibility='visible'
}else{
form.parentNode.style.visibility='hidden'
}
})
* {
border: thin solid black;
}
form{
height:100px;width:80%;background:pink
}
.inner{
height:200px;width:100%;
}
.outer{
width:100%;height:100px;
}
<div class="outer" id="outer">
<button>Visible</button>
</div>
<div class="inner" id="inner" style="visibility: hidden;">
<form id="form">
<input type="number">
<input type="number">
</form>
</div>
Related
This question already has answers here:
Multiple show/hide divs with separate toggle
(5 answers)
Closed 8 months ago.
I am trying to show and hide different div's on click on different buttons. For example, when I click button "1", it shows the block with id="block-1". When I click on button "4", the block with id="block-4" shows and previous block #block-1 hides. I tried using different ID's because I don't know any other solution to show blocks with different content inside. Unfortunately, my current code doesn't work properly: it toggles the right class to show the div, but I can't hide the previous div or change the block once the button with number is clicked. On the default state, when the page is loaded, the first block (#block-1) should always be visible. Here's the link to codepen with the result: https://codepen.io/tomavl/pen/vYRLJVY
<div class="filter">
<button class="filter-btn active" id="1">1</button>
<button class="filter-btn" id="2">2</button>
<button class="filter-btn" id="3">3</button>
<button class="filter-btn" id="4">4</button>
</div>
<div class="block">
<div class="block-1 block-card active" id="block-1">Block 1</div>
<div class="block-2 block-card" id="block-2">Block 2</div>
<div class="block-3 block-card" id="block-3">Block 3</div>
<div class="block-4 block-card" id="block-4">Block 4</div>
</div>
background-color: red;
color: white;
}
.block-card {
display: none;
}
.block-card.active {
display: block;
}
var filterBtn = document.querySelectorAll(".filter-btn");
for (var i = 0; i < filterBtn.length; i++) {
filterBtn[i].onclick = function () {
if (this.classList) {
for (var j = 0; j < filterBtn.length; j++) {
filterBtn[j].classList.remove("active");
}
this.classList.add("active");
} else {
this.active += " " + active;
}
};
}
$("#2").on("click", function (e) {
e.stopImmediatePropagation();
if ($(this).hasClass("active")) {
$(".block-2").addClass("active");
} else {
$(".block-2").removeClass("active");
}
});
$("#3").on("click", function (e) {
e.stopImmediatePropagation();
if ($(this).hasClass("active")) {
$(".block-3").addClass("active");
} else {
$(".block-3").removeClass("active");
}
});
$("#4").on("click", function (e) {
e.stopImmediatePropagation();
if ($(this).hasClass("active")) {
$(".block-4").addClass("active");
} else {
$(".block-4").removeClass("active");
}
});
You can achieve what you need with much less code by using common classes to group content by behaviour. You can use data attributes where required to store custom metadata in an element.
In the following example all buttons use the same event handler. The differences come simply from the data attribute on the button used to change the selector. The code just removes the active class from all relevant elements before applying it to the target.
let $blocks = $('.block-card');
$('.filter-btn').on('click', e => {
let $btn = $(e.target).addClass('active');
$btn.siblings().removeClass('active');
let selector = $btn.data('target');
$blocks.removeClass('active').filter(selector).addClass('active');
});
body {
background-color: red;
color: white;
}
.block-card {
display: none;
}
.block-card.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="filter">
<button class="filter-btn active" data-target="#block-1">1</button>
<button class="filter-btn" data-target="#block-2">2</button>
<button class="filter-btn" data-target="#block-3">3</button>
<button class="filter-btn" data-target="#block-4">4</button>
</div>
<div class="block">
<div class="block-card active" id="block-1">Block 1</div>
<div class="block-card" id="block-2">Block 2</div>
<div class="block-card" id="block-3">Block 3</div>
<div class="block-card" id="block-4">Block 4</div>
</div>
I have multiple divs on the page with the class 'item' – I'd like to include a button within the div that when clicked will toggle append/remove the class 'zoom' on the 'item' div…
<div class="item">
<button class="zoomer"></button>
</div>
I've found plenty of code examples that target an id element, but struggling to find a solution that works with multiples of the same class element on one page.
Many thanks in advance!
You can use querySelectorAll to get all of the buttons and then you can use forEach so you can target the element's item parent.
// Get all the buttons
let zoomer_button = document.querySelectorAll('.zoomer');
// Loop through the buttons.
// Arrow function allows to pass the element
zoomer_button.forEach(button => {
// Add an event listener for a click on the button.
button.addEventListener('click', function(e) {
// the e is the event, and then you check what the target is, which is the button.
// then you can toggle a 'zoom' class on the parent 'item'
e.target.parentNode.classList.toggle('zoom');
});
});
.item.zoom {
background-color: blue;
}
<div class="item">
<button class="zoomer">button</button>
</div>
<div class="item">
<button class="zoomer">button</button>
</div>
<div class="item">
<button class="zoomer">button</button>
</div>
<div class="item">
<button class="zoomer">button</button>
</div>
<div class="item">
<button class="zoomer">button</button>
</div>
If it's nested a layer deeper, you can use parentNode twice.
// Get all the buttons
let zoomer_button = document.querySelectorAll('.zoomer');
// Loop through the buttons.
// Arrow function allows to pass the element
zoomer_button.forEach(button => {
// Add an event listener for a click on the button.
button.addEventListener('click', function(e) {
// the e is the event, and then you check what the target is, which is the button.
// then you can toggle a 'zoom' class on the parent 'item'
e.target.parentNode.parentNode.classList.toggle('zoom');
});
});
.item.zoom {
background-color: blue;
}
<div class="item">
<div class="media">
<button class="zoomer">button</button>
</div>
</div>
<div class="item">
<div class="media">
<button class="zoomer">button</button>
</div>
</div>
<div class="item">
<div class="media">
<button class="zoomer">button</button>
</div>
</div>
<div class="item">
<div class="media">
<button class="zoomer">button</button>
</div>
</div>
You can use querySelectorAll and access each element with e.target
document.querySelectorAll('.item > .zoomer')
.forEach(elem => elem.addEventListener('click', e => {
e.target.classList.toggle('someClass')
}))
.someClass{
background:limegreen;
}
<div class="item">
<button class="zoomer">1</button>
</div>
<div class="item">
<button class="zoomer">2</button>
</div>
<div class="item">
<button class="zoomer">3</button>
</div>
<div class="item">
<button class="zoomer">4</button>
</div>
In the example below, are 7 <button>s that do various stuff -- details are commented in example.
// Render 7 <menu>/<button> combos
[...new Array(7)].forEach((item, index) => {
document.querySelector('main').insertAdjacentHTML('beforeend', `
<menu class="item${index}">
<button class="btn${index}">${index}</button>
</menu>`);
});
/*~~~~~~~~~~~~~~~~~~~~~~~.btn0*/
// Click <button> remove it's parent (which also removes the <button>)
document.querySelector('.btn0').onclick = function(e) {
this.parentElement.remove();
}
/*~~~~~~~~~~~~~~~~~~~~~~~.btn1*/
// Click <button> -- <button> is removed but it's contents is left behind
document.querySelector('.btn1').onclick = unWrap;
function unWrap(e) {
const clicked = e.target;
const parent = clicked.parentElement;
while (clicked.firstChild) {
parent.insertBefore(clicked.firstChild, clicked);
}
clicked.remove();
}
/*~~~~~~~~~~~~~~~~~~~~~.btn4-6*/
// Collect all tags with a class that starts with "btn"
const btns = document.querySelectorAll("[class^='btn']");
// Adding .target class to the last 2 <button>s
btns.forEach((btn, idx) => {
if (idx > 4) btn.classList.add('target')
});
/*~~~~~~~~~~~~~~~~~~~~~~~.btn2*/
// Target third <button> by index
/*
When <button> clicked, it's parent gets .hide class which is:
visibility:hidden which would normally hide the <button> as well, but
.btn2 has visibility explicitly set to visible
*/
btns[2].onclick = e => e.target.closest('menu').classList.toggle('hide');
/*~~~~~~~~~~~~~~~~~~~~~~~.btn3*/
/*
Everytime the <button> is clicked, a copy of itself is made and the
clones also have this ability as well
*/
btns[3].addEventListener('click', copySelf);
function copySelf(e) {
let dupe = e.target.cloneNode(true);
e.target.parentElement.append(dupe);
dupe.onclick = copySelf;
}
/*~~~~~~~~~~~~~~~~~~~~~.btn4-6*/
/*
The click event is bound to the parent/ancestor tag <section>
Any click to any <button> will trigger the event handler.
.btn4, .btn5, and .btn6 all react in a specific manner because
the event handler, delegateClick(e) is using flow control statements and
specific criteria.
*/
document.querySelector('main').onclick = delegateClick;
let armed = false;
function delegateClick(e) {
const clicked = e.target;
if (clicked.matches('button') && !armed) {
clicked.classList.add('armed');
armed = true;
return;
}
if (clicked.matches('.armed.target') && armed) {
clicked.parentElement.style.cssText = `font-size: 5rem; margin: 0`
clicked.replaceWith(`💥`);
return;
}
if (clicked.matches('.target') && armed) {
clicked.classList.add('armed');
return;
}
if (clicked.matches('.armed') && armed) {
clicked.classList.remove('armed');
armed = false;
}
}
menu {
outline: dashed red 1px;
}
.hide {
visibility: hidden;
}
.btn2 {
visibility: visible
}
.armed {
animation: warning 1s linear infinite;
}
#keyframes warning {
50% {
opacity: 0;
}
}
.target.armed {
background: red;
color: white;
}
button {
font: inherit;
cursor: pointer;
}
<main></main>
To summarise the code, I have buttons that display different tabs when pressed. Within the tabs, there are more buttons that change the color of some div elements and only one tab can be opened at a time. All this works as it should for the most part.
All buttons had been using focus but I wanted to replace it with javascript so that the selection will be retained when clicking on different elements. No tabs should be visible if the current opened tab button is pressed like it does when the code first runs.
I have had a few issues trying to get this to work properly. At the moment, the color buttons remain clicked. When tab toggles, the tab button loses selection and the tab div doesn't close when I click on the current selected tab's button.
https://jsfiddle.net/gkde169x/4/
<button class="tabButton" onclick="toggle_tab('tabOne');">Tab One</button>
<button class="tabButton" onclick="toggle_tab('tabTwo');">Tab Two</button>
<div id="tabOne" class="clickedTab" style="display: none;">
<br><br>
<div id="paletteOne">
<button class="paletteButton" style="background-color: blue"></button>
<button class="paletteButton" style="background-color: red;"></button>
<button class="paletteButton" style="background-color: yellow;"></button>
<button class="paletteButton" style="background-color: Green;"></button>
<button class="paletteButton" style="background-color: Orange;"></button>
<button class="paletteButton" style="background-color: white;"></button>
</div>
</div>
<div id="tabTwo" class="clickedTab" style="display: none;">
<br><br>
<div id="paletteTwo">
<button class="paletteButton" style="background-color: blue"></button>
<button class="paletteButton" style="background-color: red;"></button>
<button class="paletteButton" style="background-color: yellow;"></button>
<button class="paletteButton" style="background-color: Green;"></button>
<button class="paletteButton" style="background-color: Orange;"></button>
<button class="paletteButton" style="background-color: white;"></button>
</div>
</div>
<div id="change1"></div>
<div id="change2"></div>
<script type="text/javascript">
const divOne = document.getElementById('change1');
const divTwo = document.getElementById('change2');
document.querySelectorAll('#paletteOne button').forEach(function (el) {
el.addEventListener('click', function () {
divOne.style.backgroundColor = el.style.backgroundColor;
el.className = "paletteSelect";
});
});
document.querySelectorAll('#paletteTwo button').forEach(function (el) {
el.addEventListener('click', function () {
divTwo.style.backgroundColor = el.style.backgroundColor;
el.className = "paletteSelect";
});
});
function toggle_tab(id) {
const target = document.getElementById(id);
if (!target) {
return;
}
// Hide unselected tabs
const tabs = document.querySelectorAll('.clickedTab');
for (const tab of tabs) {
tab.style.display = 'none';
}
// Show current tab
target.style.display = 'block';
}
What's the best way to accommodate this in my code?
to unclick the color button I would do something like this, (with each click check for clicked buttons and unclick)
const pal = document.getElementById('paletteOne')
pal.addEventListener('click', function(e) {
document.querySelectorAll('#paletteOne button').forEach(function(el) {
el.className = "paletteButton"});
if(e.target.className==="paletteButton"){
divOne.style.backgroundColor = e.target.style.backgroundColor;
e.target.className = "paletteSelect";
}
});
to hide selected tab when clicked on
const tabs = document.querySelectorAll('.clickedTab');
for (const tab of tabs) {
if(tab!== target || target.style.display === 'block'){
tab.style.display = 'none';
}else{
target.style.display = 'block';}
}
obviously these things can be done differently, I'm just working off your code...
In your javascript
function toggle_tab(id) {
const target = document.getElementById(id);
if (!target) {
return;
}
const tabShown = document.querySelectorAll('.show')
tabShown.forEach((tab) => {
if(target != tab) tab.classList.remove('show')
})
target.classList.toggle('show');
}
Also in your CSS use classes. (You can create one class and give it to both of them since they have so many styles in common and use tabTwo and tabOne classes only for differences.)
.tabContainer {/*here use this class, give this to both tabs*/
position: absolute;
margin-top: 38px;
height: 100px;
width: 100px;
padding-left: 50px;
padding-bottom: 50px;
border-style: solid;
border-color: black;
background: white;
display:none;/*here*/
}
.tabTwo {/*here use class*/
margin-left: 20px;
}
.show{
display:block;
}
This question already has answers here:
How to retrieve the display property of a DOM element?
(4 answers)
Closed 6 years ago.
I do not understand this behavior.
If I set style "display:none" inline works properly when I click on ChangeDiv1 button.
If I set style "display:none" in header tag when I click on ChangeDiv2 button document.getElementById("conten2").style.display is empty.
If I do not set style "display:none" when I click on ChangeDiv3 button document.getElementById("conten3").style.display is empty.
How can I solve this?
function cambiar1() {
var miElemento1 = document.getElementById("conten1").style.display;
if (miElemento1 == "block") document.getElementById("conten1").style.display = "none";
if (miElemento1 == "none") document.getElementById("conten1").style.display = "block";
document.getElementById('estadoconten1').innerHTML = 'State Div1:' + document.getElementById("conten1").style.display;
}
function cambiar2() {
var miElemento2 = document.getElementById("conten2").style.display;
if (miElemento2 == "block") document.getElementById("conten2").style.display = "none";
if (miElemento2 == "none") document.getElementById("conten2").style.display = "block";
document.getElementById('estadoconten2').innerHTML = 'State Div2:' + document.getElementById("conten2").style.display;
}
function cambiar3() {
var miElemento3 = document.getElementById("conten3").style.display;
if (miElemento3 == "block") document.getElementById("conten3").style.display = "none";
if (miElemento3 == "none") document.getElementById("conten3").style.display = "block";
document.getElementById('estadoconten3').innerHTML = 'State Div3:' + document.getElementById("conten3").style.display;
}
#estadoconten1 {border:solid 1px red}
#conten2 {display:none; border:solid 1px green}
#conten3 {border:solid 1px blue}
<h2>
MODIFY "LOAD TYPE" AT JAVASCRIPT MENU AND SET "IN HEAD" OPTION
</h2>
<button type="button" onclick="cambiar1()">ChangeDiv1</button>
<button type="button" onclick="cambiar2()">ChangeDiv2</button>
<button type="button" onclick="cambiar3()">ChangeDiv3</button>
<div id='estadoconten1'></div>
<div id='estadoconten2'></div>
<div id='estadoconten3'></div>
<br>
<div id='conten1' style='display:none'>
I am Div 1
</div>
<br>
<div id='conten2'>
I am Div 2
</div>
<br>
<div id='conten3'>
I am Div 3
</div>
Example jsfiddle
You check if an elememt has display: block or none. However there are many others like initial, inline,outline ... . thats why your js doesnt work.
Explicitly set it to your css:
#conten1,#conten2,#conten3{
display:block;
}
Or change your js
if(element.style.display=="none"){
//element is hidden
//display
}else{
//elememt is not hidden
//hide
}
You can toggle anything other than "none" to "none". You can toggle "none" to "block".
function cambiar(idx) {
var el = document.getElementById("conten" + idx);
var miElemento = el.style.display;
if (miElemento === "none") {
el.style.display = "block";
} else {
el.style.display = "none";
}
document.getElementById('estadoconten' + idx).innerHTML = 'State Div' + idx + ':' + el.style.display;
}
#estadoconten1 {
border: solid 1px red
}
#conten2 {
display: none;
border: solid 1px green
}
#conten3 {
border: solid 1px blue
}
<button type="button" onclick="cambiar(1)">ChangeDiv1</button>
<button type="button" onclick="cambiar(2)">ChangeDiv2</button>
<button type="button" onclick="cambiar(3)">ChangeDiv3</button>
<div id='estadoconten1'></div>
<div id='estadoconten2'></div>
<div id='estadoconten3'></div>
<br>
<div id='conten1' style='display:none'>
I am Div 1
</div>
<br>
<div id='conten2'>
I am Div 2
</div>
<br>
<div id='conten3'>
I am Div 3
</div>
display is not part of the element unless is has specifically been set.
see this answer.
document.getElementById(...).style.display is blank
I am trying to learn jquery keypress to add class system.
I have tryed the following code but it doesn't worked. I have tryed with an ID here. When started the #ttt1 then the the #rb1 background color should change but nothing happened.
What i am doing wrong or what i need to do here? Anyone can tell me ?
This id DEMO from codemep.io
$(document).ready(function() {
var ID = $(this).attr("id");
$("#ttt" + ID).on('keypress', function() {
if ($(this).val().length > 20) {
$("#rb" + ID).addClass("ad");
} else {
$("#rb" + ID).removeClass("ad");
}
});
});
HTML
<div class="container">
<div class="tWrp">
<textarea class="test" id="ttt1" placeholder="Write"></textarea>
</div>
<div class="br" id="rb1">Button</div>
</div>
<div class="container">
<div class="tWrp">
<textarea class="test" id="ttt2" placeholder="Write"></textarea>
</div>
<div class="br" id="rb2">Button</div>
</div>
You are defining a variable ID inside a function which occurs on $(document).ready(). Inside that function the value this will point to the document. What you need to do is to define the variable inside the keypress event handler function.
Use class for selection and then use $(this).attr("id") inside the handler function. Also you can use $(this).closest('div').next() to get the next element in the parent.
DEMO
$(document).ready(function() {
//here value for this is the document object and the id is not useful.
$(".test").on('keyup', function() {
//but here value for this is textarea where keypress event happened.
var ID = this.id;
if (this.value.length > 20) {
$(this).closest('div').next().addClass("ad");
} else {
$(this).closest('div').next().removeClass("ad");
}
});
});
.container {
margin:0px auto;
width:100%;
max-width:500px;
position:relative;
margin-top:100px;
}
.test {
outline:none;
border:1px solid red;
width:100%;
min-height:100px;
}
.br {
background-color:blue;
width:100px;
height:40px;
}
.ad {
background-color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="container">
<div class="tWrp">
<textarea class="test" id="ttt1" placeholder="Write"></textarea></div>
<div class="br" id="rb1">Button</div>
</div>
<div class="container">
<div class="tWrp">
<textarea class="test" id="ttt2" placeholder="Write"></textarea></div>
<div class="br" id="rb2">Button</div>
</div>