JavaScript - click responding after one extra click - javascript

I am trying to create a drop-down list using CSS and JavaScript.
document.querySelector('.dropdown-btn').addEventListener("click", function() {
let list = document.getElementById("project-list");
if (list.style.display === "none") {
list.style.display = "inline-block";
} else {
list.style.display = "none";
}
});
#project-list {
list-style-type: none;
display: none;
position: relative;
}
<ul>
<li class="projects">Projects
<button class="dropdown-btn">Show<i class="fas fa-angle-double-down"></i></button>
<br>
<ul id="project-list">
<li>Hello</li>
<li>World</li>
</ul>
</li>
</ul>
You can try it here. It responds after one extra click, but I am unable to figure out the reason. I am learning the front-end. How can I fix it?

The problem is that list.style.display is an empty string the first time you click the button. You need to check for that, as well:
document.querySelector('.dropdown-btn').addEventListener("click", function() {
let list = document.getElementById("project-list");
if (!list.style.display || list.style.display === "none") {
list.style.display = "inline-block";
} else {
list.style.display = "none";
}
});
#project-list {
list-style-type: none;
display: none;
position: relative;
}
<ul>
<li class="projects">Projects
<button class="dropdown-btn">Show<i class="fas fa-angle-double-down"></i></button>
<br>
<ul id="project-list">
<li>Hello</li>
<li>World</li>
</ul>
</li>
</ul>

It appears to respond after the second click, because the list.style.display is an empty string when you first click it.
Surprised? The CSS rule applies to the element, but it doesn't apply to the JavaScript object.
To get the actual style of the element (including CSS rules and inline styles), you can use getComputedStyle().
For example:
document.querySelector('.dropdown-btn').addEventListener("click", function() {
let list = document.getElementById("project-list");
let style = getComputedStyle(list)
if (style.display === "none") {
list.style.display = "inline-block";
} else {
list.style.display = "none";
}
});

Related

Is there a shorter more concise way to hide & show div with Javascript?

I am creating a dashboard with approximately 20 divs starting with "display: none;".
When the .onClick() in the sidebar will be used, it will show a specific div and keep hidden all the others.
I have used the classic solution of creating a function for each div, however, is extremely lengthy and the code looks like a mess.
Is there a better cleaner way to achieve this with Javascript?
Here is my code:
function presale() {
var x = document.getElementById("presale");
var y = document.getElementById("claim");
var z = document.getElementById("stake");
if (x.style.display === "grid") {
x.style.display = "none";
} else {
x.style.display = "grid";
y.style.display = "none";
z.style.display = "none";
}
}
function claim() {
var x = document.getElementById("presale");
var y = document.getElementById("claim");
var z = document.getElementById("stake");
if (y.style.display === "grid") {
y.style.display = "none";
} else {
x.style.display = "none";
y.style.display = "grid";
z.style.display = "none";
}
}
function stake() {
var x = document.getElementById("presale");
var y = document.getElementById("claim");
var z = document.getElementById("stake");
if (z.style.display === "grid") {
z.style.display = "none";
} else {
x.style.display = "none";
y.style.display = "none";
z.style.display = "grid";
}
}
*,
html {
color: #fff;
background-color: black;
}
#presale,
#claim,
#stake
/* Here I have many other divs like below */
{
display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<link rel="stylesheet" href="MOD.CSS">
<script src="main2.js"></script>
<title>Base Template</title>
</head>
<body>
<div>
<ul>
<!-- Here I have other 20 options like the above -->
<li onclick="presale()">Presale</li>
<li onclick="claim()">Claim</li>
<li onclick="stake()">Stake</li>
<!-- Here I have other 20 options like the above -->
</ul>
<div id="presale">
<h1>Presale</h1>
</div>
<div id="claim">
<h1>Claim</h1>
</div>
<div id="stake">
<h1>Stake</h1>
</div>
</div>
</body>
</html>
Is there a better way to do this without the need to create a function and repeat the same thing over and over for each div?
There is no need for JS at all. You can simply use an anchor and use #id as hyper reference. Then you can display the element through CSS by using the :target-selector:
*,
html {
color: #fff;
background-color: black;
}
.d-none
/* Here I have many other divs like below */
{
display: none;
}
div:target {
display: grid;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<link rel="stylesheet" href="MOD.CSS">
<script src="main2.js"></script>
<title>Base Template</title>
</head>
<body>
<div>
<ul>
<!-- Here I have other 20 options like the above -->
<li>Presale</li>
<li>Claim</li>
<li>Stake</li>
<!-- Here I have other 20 options like the above -->
</ul>
<div id="presale" class="d-none">
<h1>Presale</h1>
</div>
<div id="claim" class="d-none">
<h1>Claim</h1>
</div>
<div id="stake" class="d-none">
<h1>Stake</h1>
</div>
</div>
</body>
</html>
Here you see a vanilla Javascript solution.
content divs are by default hidden.
If you click an element, the corresponding data-id get the class show.
window.onload = function () {
document.querySelectorAll('#nav li').forEach((elements) => {
elements.addEventListener('click', (el) => {
document.querySelectorAll('.content').forEach((item) => {
// hide all
item.classList.remove('show');
});
// show one
document.getElementById(el.target.getAttribute('data-id')).classList.add('show');
});
});
};
.content {
display: none;
}
.show {
display: block;
}
<ul id="nav">
<li data-id="presale">Presale</li>
<li data-id="claim">Claim</li>
<li data-id="stake">Stake</li>
</ul>
<div id="presale" class="content">
<h1>Presale</h1>
</div>
<div id="claim" class="content">
<h1>Claim</h1>
</div>
<div id="stake" class="content">
<h1>Stake</h1>
</div>
Something like this using data attributes and classlist toggles should also work.
I would consider minimizing your code (and CSS) by using generic CSS selectors to hide/show the individual sections. This also makes scalability and maintainability easier for the next guy.
This has the added benefit of your styling being controlled 100% using CSS and not arbitrary inline styles set by the javascript.
Adding another section is also easy as can be:
Add a new section with some id (eg. awesome-section)
Add a nav entry with the attribute data-toggle-section with the id as the value <li data-toggle-section="awesome-section">Awesome Section</li>
Profit
You're also not restricted to using just the nav elements themselves as the event listener is bound using the [data-toggle-section] selector which means that basically anything can show or hide a section as long as it has that attribute with the correct value.
const buttons = Array.from(document.querySelectorAll("[data-toggle-section]"));
const sections = buttons.map(element => {
return document.getElementById(element.dataset.toggleSection)
});
buttons.forEach(element => {
element.addEventListener('click', event => {
const selected = element.dataset.toggleSection;
sections.forEach(section => {
if(section.id === selected) {
section.classList.toggle('shown');
} else {
section.classList.remove('shown');
}
})
});
});
*,
html {
color: #fff;
background-color: black;
}
.option-section {
display: none;
}
.option-section.shown {
display: grid;
}
<div>
<ul>
<!-- Here I have other 20 options like the above -->
<li data-toggle-section="presale">Presale</li>
<li data-toggle-section="claim">Claim</li>
<li data-toggle-section="stake">Stake</li>
<!-- Here I have other 20 options like the above -->
</ul>
<div id="presale" class="option-section">
<h1>Presale</h1>
</div>
<div id="claim" class="option-section">
<h1>Claim</h1>
</div>
<div id="stake" class="option-section">
<h1>Stake</h1>
</div>
</div>
You could simply assign the same class (e.g. my_div) to every showable div, then pass the id to your function (that will show that and hide all the others).
function show_hide(id) {
document.querySelectorAll('.my_div').forEach(my_div => {
my_div.style.display = my_div.getAttribute('id') == id ? 'block' : 'none';
});
}
.my_div {
display: none;
}
<div>
<ul>
<li onclick="show_hide('presale')">Presale</li>
<li onclick="show_hide('claim')">Claim</li>
<li onclick="show_hide('stake')">Stake</li>
</ul>
<div class="my_div" id="presale">
<h1>Presale</h1>
</div>
<div class="my_div" id="claim">
<h1>Claim</h1>
</div>
<div class="my_div" id="stake">
<h1>Stake</h1>
</div>
</div>
Here's my attempt. It's sensibly the same as #ztom's answer but I tryed avoiding a foreach.
document.querySelectorAll("li").forEach(e => e.addEventListener("click", () => {
let shown = document.querySelector(".action:not(.d-none)")
if(shown){
shown.classList.add("d-none")
if(e.dataset.id != shown.id){
document.getElementById(e.dataset.id).classList.remove("d-none")
}
}else{
document.getElementById(e.dataset.id).classList.remove("d-none")
}
}))
.action{
display:grid;
}
.d-none{
display:none;
}
<ul>
<li data-id="presale">Presale</li>
<li data-id="claim">Claim</li>
<li data-id="stake">Stake</li>
</ul>
<div class="action d-none" id="presale">Presale</div>
<div class="action d-none" id="claim">Claim</div>
<div class="action d-none" id="stake">Stake</div>
When it comes to use the same logic on multiple elements, use classes instead of id's and your solution is shortened by default.
With jQuery, it's basically a 2-liner:
in CSS, create a class .hidden with display:none;
Your div and li elements should be grouped, using a class too.
Then you can simply refer to this classes and add the show/hide logic by:
$('h1:contains('+$(this).text()+')').parent().toggleClass("hidden");
$('h1:not(:contains('+$(this).text()+'))').parent().addClass("hidden");
$('document').ready(function(){
$('.toggle').on('click',function(){
$('h1:contains('+$(this).text()+')').parent().toggleClass("hidden");
$('h1:not(:contains('+$(this).text()+'))').parent().addClass("hidden");
});
});
*,
html {
color: #fff;
background-color: black;
}
.hidden
{
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="MOD.CSS">
<script src="main2.js"></script>
<title>Base Template</title>
</head>
<body>
<div>
<ul>
<!-- Here I have other 20 options like the above -->
<li class="toggle">Presale</li>
<li class="toggle">Claim</li>
<li class="toggle">Stake</li>
<!-- Here I have other 20 options like the above -->
</ul>
<div class="hidden">
<h1>Presale</h1>
</div>
<div class="hidden">
<h1>Claim</h1>
</div>
<div class="hidden">
<h1>Stake</h1>
</div>
</div>
</body>
</html>
This is a question for Code Review section https://codereview.stackexchange.com/
However, you can try smth like this:
const elems = ["presale", "claim", "stake"];
function toggle(elem) {
elems.map(i => {
let el = document.getElementById(i);
el.style.display = "none";
});
let active_el = document.getElementById(elem);
active_el.style.display = "grid";
}
and in html add the elem name as a param, so, replace this
<li onclick="presale()">Presale</li>
<li onclick="claim()">Claim</li>
<li onclick="stake()">Stake</li>
with this
<li onclick="toggle('presale')">Presale</li>
<li onclick="toggle('claim')">Claim</li>
<li onclick="toggle('stake')">Stake</li>
If you attach data attributes to both the list items and the "panels" you can use one function to match them up, and use a CSS class to determine whether it should be active or not.
// Cache the elements, the panels container, and the list element
// separately adding one event listener to the list. We're using
// event delegation for this - one listener captures all
// the events from its child elements
const allElements = document.querySelectorAll('.list li, .panels .panel');
const panels = document.querySelector('.panels');
const list = document.querySelector('ul');
list.addEventListener('click', handlePanel);
// When the listener is triggered
function handlePanel(e) {
// Check if it's a list item
if (e.target.matches('li')) {
// Destructure its id from the dataset
const { id } = e.target.dataset;
// Remove all the active classes from the elements
allElements.forEach(el => el.classList.remove('active'));
// And then add an active class to the list item,
// and the panel where their ids match
const selector = `[data-id="${id}"]`;
const item = list.querySelector(`li${selector}`);
const panel = panels.querySelector(`.panel${selector}`);
item.classList.add('active');
panel.classList.add('active');
}
}
.panel { display: none; }
.panel h1 { font-size: 1.2em; color: darkblue; }
ul { list-style-type: none; margin-left: 0; padding: 0; }
li { padding: 0.3em; border: 1px solid white; }
li:hover { background-color: thistle; cursor: pointer; }
li.active { border: 1px solid #454545; background-color: lightyellow; }
.panel.active { display: block; }
<ul class="list">
<li data-id="presale">Presale</li>
<li data-id="claim">Claim</li>
<li data-id="stake">Stake</li>
</ul>
<div class="panels">
<div data-id="presale" class="panel">
<h1>Presale</h1>
</div>
<div data-id="claim" class="panel">
<h1>Claim</h1>
</div>
<div data-id="stake" class="panel">
<h1>Stake</h1>
</div>
</div>
Additional documentation
classList
Destructuring assignment
Event delegation
matches
querySelector / querySelectorAll
Template/string literals

Javascript hide/show on click not working

I'm trying to make a drop-down list using Html and Javascript, but it is not working for some reason, despite watching every video on Youtube. Here's the code I'm using:
HTML:
<div class="dropdown">
<button id="dropdownbtn" onclick="dropdown()">Courses &#9662</button>
<div id="dropdown-items">
<li class="li">button!!</li>
<li class="li">button!!</li>
<li class="li">button!!</li>
<li class="li">button!!</li>
</div>
</div>
JavaScript:
var flag = 0;
function dropdown() {
if (flag==0) {
document.getElementById("dropdown-items").style.display = "none";
flag=1;
}
if (flag==1) {
document.getElementById("dropdown-items").style.display = "block";
flag=0;
}
}
I set the display to none in css also. Thank you for your help.
it's actually working but you have to place else if because in if condition you change the flag with 1 and also second condition becomes true
var flag = 0;
function dropdown() {
if (flag==0) {
document.getElementById("dropdown-items").style.display = "none";
flag=1;
}
else if (flag==1) {
document.getElementById("dropdown-items").style.display = "block";
flag=0;
}
}
An easier solution, use ternary operators:
document.getElementById("dropdown-items").style.display = flag === 0 ? 'none' : ' block'
Why not use toggleclass here?
<div class="dropdown">
<button id="dropdownbtn" onclick="dropdown()">Courses &#9662</button>
<div id="dropdown-items">
<li class="li">button!!</li>
<li class="li">button!!</li>
<li class="li">button!!</li>
<li class="li">button!!</li>
</div>
</div>
add css like
#dropdown-items {
display: none;
}
.toggleShow {
display: block;
}
In your script
<script>
function dropdown() {
var element = document.getElementById("dropdown-items");
element.classList.toggle("toggleShow");
}
</script>

How to fix issue with "previous button" using JavaScript

I am "emulating" a Gravity Form. I am using a previous button for my form. I know the best way to solve the problem is by 'telling' the function the id of the current div (display: block) but I don't know how.
In the first part of the code, I show or hide divs based on the selected option of the tag select, now, in the second one is where I configure the "previous button".
<script>
function yesnoCheck(that) {
if (that.value == "2") {
document.getElementById("b").style.display = "block";
document.getElementById("a").style.display = "none";
<?php $new="b" ?>
} else {
document.getElementById("a").style.display = "none";
document.getElementById("d").style.display = "block";
}
}
</script>
<script>
function yesnoCheck2(that) {
if (that.value != " ") {
document.getElementById("c").style.display = "block";
document.getElementById("a").style.display = "none";
document.getElementById("b").style.display = "none";
<?php $new="c" ?>
} else {
document.getElementById("a").style.display = "none";
}
}
</script>
<script>
function yesnoCheck3(that) {
if (that.value != " ") {
document.getElementById("d").style.display = "block";
document.getElementById("c").style.display = "none";
<?php $new="f" ?>
} else {
document.getElementById("c").style.display = "none";
}
}
</script>
<script>
function yesnoCheck4(that) {
if (that.value.length == 8) {
document.getElementById("tform").style.display = "block";
} else {
document.getElementById("tform").style.display = "none";
}
}
</script>
It isn't entirely clear what you're trying to do, but what I think you're trying to do is navigate through a set of sections in a form as if they were different pages.
One really easy way to do this is to use the :target CSS selector, which allows you to select elements that match the ID of the current page's anchor fragment. For example, if I had a section with the ID of main, and the URL was something like https://example.com/#main, I could use section:target to show that section.
Here's a full example for you. HTML:
<section id="one">
<h1>Section 1</h1>
<p>
This is section one. Content goes here.
</p>
Next
</section>
<section id="two">
<h1>Section 2</h1>
<p>
This is section two. More content goes here.
</p>
Previous
Next
</section>
<section id="three">
<h1>Section 3</h1>
<p>
This is the last section.
</p>
Previous
</section>
CSS:
.button {
background: #333;
color: #fff;
text-decoration: none;
padding: 0.5em 1em;
border-radius: 0.3em;
}
section {
display: none;
}
section:target {
display: block;
}
Finally, some JavaScript to initialize things:
// Default to the first section
if (!location.hash.substr(1)) {
location.hash = 'one';
}
The buttons are just links to the next section, by way of anchor fragment.
This example is up on JSFiddle here: https://jsfiddle.net/91pnc3gf/

Javascript onclick needs 2 clicks

Each div is shown only after 2 clicks at the start.After 2 initial clicks on each div, each div showhide works with just 1 click. Javascript and html
function showhide() {
var div = document.getElementsByClassName('search_form')[0];
if (div.style.display == "none") {
div.style.display = "block";
} else {
div.style.display = "none";
}
}
function showhide2() {
var div = document.getElementsByClassName('login')[0];
if (div.style.display == "none") {
div.style.display = "block";
} else {
div.style.display = "none";
}
}
function showhide3() {
var div = document.getElementsByClassName('carrello')[0];
if (div.style.display == "none") {
div.style.display = "block";
} else {
div.style.display = "none";
}
}
.search_form {
display: none;
float: right;
}
.login {
display: none;
float: right;
}
.carrello {
display: none;
float: right;
}
<div class="login-carrello">
<img src="img.png" onClick="showhide();" onmouseover="this.src='img.png'" onmouseout="this.src='gg.png'" width="50px" height="50px"> &nbsp &nbsp
<img src="img.png" onClick="showhide2()" onmouseover="this.src='img.png'" onmouseout="this.src='img.png'" width="50px" height="50px">
<img src="imgt.png" onClick="showhide3()" onmouseover="this.src='img.png'" onmouseout="this.src='img.png'" width="50px" height="50px">
</div>
are both in a single PHP page.Thanks in advance.
The problem is in JavaScript code. Since display property was initially set in css, div.style.display won't give you none. So, you have to change your code a little bit. Like this:
if(div.style.display != "block")
div.style.display = "block";
else
div.style.display = "none";
Once you set the display property using JavaScript code, you can read it using JavaScript.
Because the display property is not actually set (although it is applied through CSS), it's initial value is empty (and thus not equal to 'none' ).
If checked in the reverse order, it would work, but perhaps safer is to use an extra class (with the display property) you toggle instead.
A minimized example:
function showhide(cn) {
var div = document.getElementsByClassName(cn)[0];
div.classList.toggle('show');
}
.login-carrello >img{
width:50px;
height: 50px;
}
.search_form,.login, .carrello {
float: right;
display: none;
}
.show{
display:block;
}
<div class="login-carrello">
<img src="/wp-content/themes/kyro/img/search.png" onClick="showhide('search_form')">
<img src="img.png" onClick="showhide('login')">
<img src="imgt.png" onClick="showhide('carrello')">
</div>
<div class="search_form">search_form</div>
<div class="login">login</div>
<div class="carrello">carrello</div>
The start setting for .search_form,.login, .carrello is display:none, but adding .show overrides that. (I've also taken the liberty of parameterizing the classname to show/hide so only a single function is needed. With late binding it could be automated further, but this stays pretty close to the original)
Not sure if you're looking for a double click, or just two seperate clicks. However if a double click would satisfy your functionality requirement, you could try something like the following:
<img src="img.png" ondblclick="showhide2()" onmouseover="this.src='img.png'" mouseout="this.src='img.png'" width="50px" height="50px">

Making JavaScript menu close when clicking outside

I'm basically trying to make open menu close when user click anywhere. im new and i dont know what is problem. so far i am using this code, here is my html, css and javaScript. i think problem is in my JavaScript.
thanks for help.
HTML
<div class="menu-button" style="width:23px; cursor: pointer;" onClick="show_menu()"><span style="color:#b0acac;">▼</span></div>
<div id="dropdown_menu" class="hidden_menu">
<ul id="container">
<li>Settings<br></li>
<li>Log Out</li>
</ul>
</div>
CSS
.menu-button{
position: relative;
left:1556px;
top:-43px;
}
.hidden_menu{
display:none
}
#dropdown_menu ul li {
text-decoration:none;
display: inline;
cursor: pointer;
position: relative;
left:-20px;
top:2px;
}
#dropdown_menu ul{
width:80px;
height:90px;
background-color:#efefef;
position:relative;
top:-64px;
left:1460px;
border-color:#ff0000;
border-width:2px;
}
JavaScript
<script>
function show_menu(){
var menu = document.getElementById('dropdown_menu');
if(menu.style.display == 'block'){
menu.style.display = 'none';
}else {
menu.style.display = 'block';
}
}
function hide_menu(){
var menu = document.getElementById('dropdown_menu');
if(menu.style.display == 'none'){
menu.style.display = 'block';
}
}
var cl = document.getElementById("body");
cl.addEventListener("click", hide_menu);
</script>
This can help you.
HTML Code
<div class="menu-button" style="width:23px; cursor: pointer;" ><span id="button" style="color:#b0acac;">Show Menu</span></div>
<div id="dropdown_menu" class="hidden_menu">
<ul id="container">
<li>Settings<br></li>
<li>Log Out</li>
</ul>
</div>
Css
.hidden_menu { display:none;}
JavaScript Code
var dropMenu = document.getElementById('dropdown_menu'),
menuButton = document.getElementById('button'),
dropUL = document.getElementById('container').childNodes;
function hide_menu(evt){
evt = evt || window.event; // get window.event if evt is falsy (IE)
var targetElement = evt.target || evt.srcElement; // get srcElement if target is falsy (IE)
if(targetElement === menuButton || targetElement.nodeName.toLowerCase() === 'li' ){
dropMenu.style.display = 'block'
} else {
dropMenu.style.display = 'none'
}
}
// For legacy broser(IE8 and IE7) support
function addEvent(el, type, fn){
if(typeof addEventListener !== 'undefined'){
el.addEventListener(type, fn, false)
} else {
el.attachEvent('on'+type, fn);
}
}
addEvent(document, 'click', hide_menu);
Demo
If you want plain js:
document.addEventListener('click', function(e){
console.log(e.target.id)
if(e.target.id!=="dropdown_menu")
console.log("document Clicked");
});
It is same as above function but written in javascript
Try
function hide_menu(e){
var menu = document.getElementById('dropdown_menu');
if(e.target != menu) {
menu.style.display = 'none';
}
}
document.body.addEventListener("click", hide_menu);
$('body').click(function(){
if($(this).attr('id')!='dropdown_menu' && !$(this).hasClass('menu-button'))
// code to close the menu
});
Making sure that the click is not on the div for menu

Categories

Resources