dropdown menu is created in shadowDOM
almost perfect,
but the problem is how to hide the dropdown menu when click on any where else in window
class NavComponent extends HTMLElement {
constructor() {
super();
let template = document.currentScript.ownerDocument.querySelector('template');
let clone = document.importNode(template.content, true);
let root = this.attachShadow({ mode: 'open' });
root.appendChild(clone);
}
connectedCallback() {
let ddc = this.shadowRoot.querySelectorAll('.dropdowncontainer')
let dd = this.shadowRoot.querySelectorAll('.dropdown');
let ddc_length = ddc.length
for (let index = 0; index < ddc_length; index++) {
ddc[index].addEventListener('click', e => {
dd[index].classList.toggle('show');
});
}
/** have to update the code ............ */
window.onclick = function (event) {
} /** END - have to update the code ............ */
}
}
customElements.define('app-nav', NavComponent)
please refer this demo for complete code
The best solution, as #guitarino suggested is to define a dropdown menu custom element.
When the menu is clicked, call a (first) event handler that will show/hide the menu, and also add/remove a (second) dropdown event handler on window.
At its turn, the (second) dropdown event handler will call the first event handler only if the action is outside the custom element itself.
connectedCallback()
{
//mousedown anywhere
this.mouse_down = ev => !this.contains( ev.target ) && toggle_menu()
//toggle menu and window listener
var toggle_menu = () =>
{
if ( this.classList.toggle( 'show' ) )
window.addEventListener( 'mousedown', this.mouse_down )
else
window.removeEventListener( 'mousedown', this.mouse_down )
}
//click on menu
this.addEventListener( 'click', toggle_menu )
}
It works with or without Shadow DOM:
customElements.define( 'drop-menu', class extends HTMLElement
{
constructor ()
{
super()
this.attachShadow( { mode: 'open'} )
.innerHTML = '<slot></slot>'
}
connectedCallback()
{
//mousedown anywhere
this.mouse_down = ev => !this.contains( ev.target ) && toggle_menu()
//toggle menu and window listener
var toggle_menu = () =>
{
if ( this.classList.toggle( 'show' ) )
window.addEventListener( 'mousedown', this.mouse_down )
else
window.removeEventListener( 'mousedown', this.mouse_down )
}
//click on menu
this.addEventListener( 'click', toggle_menu )
}
disconnectedCallback ()
{
this.removeEventListener( 'mousedown', this.mouse_down )
}
} )
drop-menu {
position: relative ;
cursor: pointer ;
display: inline-block ;
}
drop-menu > output {
border: 1px solid #ccc ;
padding: 2px 5px ;
}
drop-menu > ul {
box-sizing: content-box ;
position: absolute ;
top: 2px ; left: 5px ;
width: 200px;
list-style: none ;
border: 1px solid #ccc ;
padding: 0 ;
opacity: 0 ;
transition: all 0.2s ease-in-out ;
background: white ;
visibility: hidden ;
z-index: 2 ;
}
drop-menu.show > ul {
opacity: 1 ;
visibility: visible ;
}
drop-menu > ul > li {
overflow: hidden ;
transition: font 0.2s ease-in-out ;
padding: 2px 5px ;
background-color: #e7e7e7;
}
drop-menu:hover {
cursor: pointer;
background-color: #f2f2f2;
}
drop-menu ul li:hover {
background-color: #e0e0e0;
}
drop-menu ul li span {
float: right;
color: #f9f9f9;
background-color: #f03861;
padding: 2px 5px;
border-radius: 3px;
text-align: center;
font-size: .8rem;
}
drop-menu ul li:hover span {
background-color: #ee204e;
}
<drop-menu><output>Services</output>
<ul>
<li>Graphic desing</li>
<li>web design</li>
<li>app design</li>
<li>theme</li>
</ul>
</drop-menu>
<drop-menu><output>tutorial</output>
<ul>
<li>css <span>12 available</span></li>
<li>php <span>10 available</span></li>
<li>javascript <span>40 available</span></li>
<li>html <span>20 available</span></li>
</ul>
</drop-menu>
An unrelated suggestion: you should probably separate .dropdown into its own <app-nav-dropdown> component and assign the 'click' event listeners in its 'constructor' or 'connectedCallback'.
The best idea for your problem is to
ddc[index].addEventListener('click', e => {
if(dd[index].classList.contains('show')) {
dd[index].classList.remove('show');
window.removeEventListener('click', handleDropdownUnfocus, true);
}
else {
dd[index].classList.add('show');
window.addEventListener('click', handleDropdownUnfocus, true);
}
});
Note: I use addEventListener with true, such that the event happens at capture, so that it's will not happen immediately after the .dropdown click handler.
And your handleDropdownUnfocus will look like
function handleDropdownUnfocus(e) {
Array.from(document.querySelectorAll('app-nav')).forEach(function(appNav) {
Array.from(appNav.shadowRoot.querySelectorAll('.dropdown')).forEach(function(dd) {
dd.classList.remove('show');
});
});
window.removeEventListener('click', handleDropdownUnfocus, true);
}
There's a problem with this solution though, that if you click on the menu item again after opening it, it will call both .dropdown and window handlers, and the net result will be that the dropdown will remain open. To fix that, you would normally add a check in handleDropdownUnfocus:
if(e.target.closest('.dropdown')) return;
However, it will not work. Even when you click on .dropdown, your e.target will be app-nav element, due to Shadow DOM. Which makes it more difficult to do the toggling. I don't know how you'd address this issue, maybe you can come up with something fancy, or stop using Shadow DOM.
Also, your code has some red flags... For example, you use let keyword in your for-loop, which is fine. The support for let is very limited still, so you're more likely going to transpile. The transpiler will just change every let to var. However, if you use var in your loop, assigning the handlers in a loop like that will not work anymore, because index for every handler will refer to the last dropdown (because index will now be global within the function's context and not local for every loop instance).
Related
I have a menu that open a sub-menu section onclick (let's name the container: "sub-menu").
I would like "sub-menu" to disapear if the user click outside of it / on the rest of the page.
It seems to be solved on How do I detect a click outside an element?
But I can't get how to use the code snipet from the second most popular answer:
export function hideOnClickOutside(selector) {
const outsideClickListener = (event) => {
const $target = $(event.target);
if (!$target.closest(selector).length && $(selector).is(':visible')) {
$(selector).hide();
removeClickListener();
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
Could you please guide me on how to use it?
I edited, and included a basic example. -> I want sub menu to also close when clicking on the "white" space. But not on the parent "main menu" element.
document.getElementById("main-menu").addEventListener("click", function() {bouttonexpand('sub-menu-class')});
function bouttonexpand(id) {
var elemeacacher = document.getElementsByClassName(id);
if (elemeacacher[0].style.display != "none"){
for(var y=0;y<elemeacacher.length;y++)
elemeacacher[y].style.display = "none";
}
else {
for(var y=0;y<elemeacacher.length;y++)
elemeacacher[y].style.display = "block";
}
}
#main-menu {
display:inline-block;
height:20px;
width:100px;
background: blue;
padding: 5%;
}
#sub-menu {
display:inline-block;
height:50px;
width:50px;
background: red;
display: none;
}
<div><div id="main-menu">Main menu</div></div>
<div><div id="sub-menu" class="sub-menu-class">Sub menu</div></div>
Thanks
By using jQuery, you can bind to the document click event and hides the div container when the clicked element isn’t the container itself or descendant of the div element.
var container = $("#sub-menu");
if (!container.is(event.target) && !container.has(event.target).length) {
container.hide();
}
If you want to hide that container without being tested the container itself or descendant of the div element just remove the condition and simply use container.hide();.
Also, rather than setting display: none; on sub-menu in the CSS, set it manually so that you can toggle the sub-menu from the very first click.
Have a look at the snippet below:
var x = document.getElementById("sub-menu");
x.style.display = "none";
$(document).click(function (evt) {
if ($(evt.target).is('#main-menu')) { // control click event if it's main-menu
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
else {
var container = $("#sub-menu");
if (!container.is(event.target) && !container.has(event.target).length) { // if you don't want that remove the condition and write container.hide(); only
container.hide();
}
}
});
#main-menu {
display: inline-block;
height: 20px;
width: 100px;
background: blue;
padding: 5%;
}
#sub-menu {
display: inline-block;
height: 50px;
width: 50px;
background: red;
}
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<div id="main-menu">Main menu</div>
<div id="sub-menu" class="sub-menu-class">Sub menu</div>
I am trying to show a description when hovering over an option in a select list, however, I am having trouble getting the code to recognize when hovering.
Relevant code:
Select chunk of form:
<select name="optionList" id="optionList" onclick="rankFeatures(false)" size="5"></select>
<select name="ranks" id="ranks" size="5"></select>
Manipulating selects (arrays defined earlier):
function rankFeatures(create) {
var $optionList = $("#optionList");
var $ranks = $("#ranks");
if(create == true) {
for(i=0; i<5; i++){
$optionList.append(features[i]);
};
}
else {
var index = $optionList.val();
$('#optionList option:selected').remove();
$ranks.append(features[index]);
};
}
This all works. It all falls apart when I try to deal with hovering over options:
$(document).ready(
function (event) {
$('select').hover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
})
})
I found that code while searching through Stack Exchange, yet I am having no luck getting it to work. The alert occurs when I click on an option. If I don't move the mouse and close the alert by hitting enter, it goes away. If I close out with the mouse a second alert window pops up. Just moving the mouse around the select occasionally results in an alert box popping up.
I have tried targeting the options directly, but have had little success with that. How do I get the alert to pop up if I hover over an option?
You can use the mouseenter event.
And you do not have to use all this code to check if the element is an option.
Just use the .on() syntax to delegate to the select element.
$(document).ready(function(event) {
$('select').on('mouseenter','option',function(e) {
alert('yeah');
// this refers to the option so you can do this.value if you need..
});
});
Demo at http://jsfiddle.net/AjfE8/
try with mouseover. Its working for me. Hover also working only when the focus comes out from the optionlist(like mouseout).
function (event) {
$('select').mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
})
})
You don't need to rap in in a function, I could never get it to work this way. When taking it out works perfect. Also used mouseover because hover is ran when leaving the target.
$('option').mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
console.log('yeah!');
};
})
Fiddle to see it working. Changed it to console so you don't get spammed with alerts. http://jsfiddle.net/HMDqb/
That you want is to detect hover event on option element, not on select:
$(document).ready(
function (event) {
$('#optionList option').hover(function(e) {
console.log(e.target);
});
})
I have the same issue, but none of the solutions are working.
$("select").on('mouseenter','option',function(e) {
$("#show-me").show();
});
$("select").on('mouseleave','option',function(e) {
$("#show-me").hide();
});
$("option").mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
});
Here my jsfiddle https://jsfiddle.net/ajg99wsm/
I would recommend to go for a customized variant if you like to ease
capture hover events
change hover color
same behavior for "drop down" and "all items" view
plus you can have
resizeable list
individual switching between single selection and multiple selection mode
more individual css-ing
multiple lines for option items
Just have a look to the sample attached.
$(document).ready(function() {
$('.custopt').addClass('liunsel');
$(".custopt, .custcont").on("mouseover", function(e) {
if ($(this).attr("id") == "crnk") {
$("#ranks").css("display", "block")
} else {
$(this).addClass("lihover");
}
})
$(".custopt, .custcont").on("mouseout", function(e) {
if ($(this).attr("id") == "crnk") {
$("#ranks").css("display", "none")
} else {
$(this).removeClass("lihover");
}
})
$(".custopt").on("click", function(e) {
$(".custopt").removeClass("lihover");
if ($("#btsm").val() == "ssm") {
//single select mode
$(".custopt").removeClass("lisel");
$(".custopt").addClass("liunsel");
$(this).removeClass("liunsel");
$(this).addClass("lisel");
} else if ($("#btsm").val() == "msm") {
//multiple select mode
if ($(this).is(".lisel")) {
$(this).addClass("liunsel");
$(this).removeClass("lisel");
} else {
$(this).addClass("lisel");
$(this).removeClass("liunsel");
}
}
updCustHead();
});
$(".custbtn").on("click", function() {
if ($(this).val() == "ssm") {
$(this).val("msm");
$(this).text("switch to single-select mode")
} else {
$(this).val("ssm");
$(this).text("switch to multi-select mode")
$(".custopt").removeClass("lisel");
$(".custopt").addClass("liunsel");
}
updCustHead();
});
function updCustHead() {
if ($("#btsm").val() == "ssm") {
if ($(".lisel").length <= 0) {
$("#hrnk").text("current selected option");
} else {
$("#hrnk").text($(".lisel").text());
}
} else {
var numopt = +$(".lisel").length,
allopt = $(".custopt").length;
$("#hrnk").text(numopt + " of " + allopt + " selected option" + (allopt > 1 || numopt === 0 ? 's' : ''));
}
}
});
body {
text-align: center;
}
.lisel {
background-color: yellow;
}
.liunsel {
background-color: lightgray;
}
.lihover {
background-color: coral;
}
.custopt {
margin: .2em 0 .2em 0;
padding: .1em .3em .1em .3em;
text-align: left;
font-size: .7em;
border-radius: .4em;
}
.custlist,
.custhead {
width: 100%;
text-align: left;
padding: .1em;
border: LightSeaGreen solid .2em;
border-radius: .4em;
height: 4em;
overflow-y: auto;
resize: vertical;
user-select: none;
}
.custlist {
display: none;
cursor: pointer;
}
.custhead {
resize: none;
height: 2.2em;
font-size: .7em;
padding: .1em .4em .1em .4em;
margin-bottom: -.2em;
width: 95%;
}
.custcont {
width: 7em;
padding: .5em 1em .6em .5em;
/* border: blue solid .2em; */
margin: 1em auto 1em auto;
}
.custbtn {
font-size: .7em;
width: 105%;
}
h3 {
margin: 1em 0 .5em .3em;
font-weight: bold;
font-size: 1em;
}
ul {
margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>
customized selectable, hoverable resizeable dropdown with multi-line, single-selection and multiple-selection support
</h3>
<div id="crnk" class="custcont">
<div>
<button id="btsm" class="custbtn" value="ssm">switch to multi-select mode</button>
</div>
<div id="hrnk" class="custhead">
current selected option
</div>
<ul id="ranks" class="custlist">
<li class="custopt">option one</li>
<li class="custopt">option two</li>
<li class="custopt">another third long option</li>
<li class="custopt">another fourth long option</li>
</ul>
</div>
I am currently making a dropdown component in pure JavaScript. When the user click on the dropdown toggle, the content gain focus and its content is being displayed. When the user click outside of it, then the content loses its focus and it gets hidden.
So far, it works great. However, I am encountering two problems.
The first one is that when an element inside the dropdown is clicked (eg: anchor tags), the dropdown loses focus, which it shouldn't.
The second one is that when the dropdown toggle is clicked while the dropdown content is being displayed, the dropdown should close instead of closing then re-opening due to the click registered on the dropdown toggle.
HTML:
<div class="dropdown">
Dropdown toggle
<div class="dropdown-content" tabindex="0">
<ul>
<li>
My profile
</li>
<li>
Log out
</li>
</ul>
</div>
</div>
CSS:
.dropdown {
position: relative;
}
.dropdown.is-open .dropdown-content {
display: block;
}
.dropdown-content {
display: none;
position: absolute;
top: 100%;
width: 100%;
padding: 0.5rem 0;
background: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
JS:
class Dropdown {
constructor(element) {
this.element = element;
this.toggle = this.element.querySelector('.dropdown-toggle');
this.content = this.element.querySelector('.dropdown-content');
this.bindings();
}
bindings() {
this.toggle.addEventListener('click', this.open.bind(this));
this.content.addEventListener('keydown', this.handleKeyDown.bind(this));
this.content.addEventListener('focusout', this.close.bind(this));
}
open(e) {
e.preventDefault();
this.element.classList.add('is-open');
this.content.focus();
}
close(e) {
this.element.classList.remove('is-open');
}
handleKeyDown(e) {
if (e.keyCode === 27) {
this.close(e);
}
}
}
document.querySelectorAll('.dropdown').forEach(dropdown => new Dropdown(dropdown));
I have been trying to get my head around on how I would solve these issues without any luck. Any idea on how to solve these issues?
After some testing I found a solution that works. Try the code below
class Dropdown {
constructor(element) {
this.element = element;
this.toggle = this.element.querySelector('.dropdown-toggle');
this.content = this.element.querySelector('.dropdown-content');
this.bindings();
}
bindings() {
this.toggle.addEventListener('click', this.open.bind(this));
this.content.addEventListener('keydown', this.handleKeyDown.bind(this));
this.element.addEventListener('focusout', this.onFocusOut.bind(this),true);
}
onFocusOut(e) {
if (!this.element.contains(e.relatedTarget)) {
this.close(e)
}
}
open(e) {
e.preventDefault();
this.element.classList.toggle('is-open');
this.content.focus();
}
close(e) {
e.preventDefault();
this.element.classList.remove('is-open');
}
handleKeyDown(e) {
if (e.keyCode === 27) {
this.close(e);
}
}
}
I'm trying to have a bgcolor change for an element on mouseover, mouseout, and onclick. The problem is Javascript overwrites my onclick with mouseout, so I can't have both. So is there any way to have mouseover reset after mouseout?
function init() {
document.getElementById('default').onmouseover = function() {
tabHoverOn('default', 'grey')
};
document.getElementById('default').onmouseout = function() {
tabHoverOff('default', 'yellow')
};
document.getElementById('section2').onmouseover = function() {
tabHoverOn('section2', 'grey')
};
document.getElementById('section2').onmouseout = function() {
tabHoverOff('section2', 'yellow')
};
document.getElementById('section3').onmouseover = function() {
tabHoverOn('section3', 'grey')
};
document.getElementById('section3').onmouseout = function() {
tabHoverOff('section3', 'yellow')
};
}
function tabHoverOn(id, bgcolor) {
document.getElementById(id).style.backgroundColor = bgcolor;
}
function tabHoverOff(id, bgcolor) {
document.getElementById(id).style.backgroundColor = bgcolor;
}
var current = document.getElementById('default');
function tab1Highlight(id) {
if (current != null) {
current.className = "";
}
id.className = "tab1highlight";
current = id;
}
function tab2highlight(id) {
if (current != null) {
current.className = "";
}
id.className = "tab2highlight";
current = id;
}
function tab3highlight(id) {
if (current != null) {
current.className = "";
}
id.className = "tab3highlight";
current = id;
}
window.onload = init();
body {
width: 900px;
margin: 10px auto;
}
nav {
display: block;
width: 80%;
margin: 0 auto;
}
nav > ul {
list-style: none;
}
nav > ul > li {
display: inline-block;
margin: 0 3px;
width: 150px;
}
nav > ul > li > a {
width: 100%;
background-color: #ffff66;
border: 1px solid #9b9b9b;
border-radius: 12px 8px 0 0;
padding: 8px 15px;
text-decoration: none;
font-weight: bold;
font-family: arial, sans-serif;
}
main {
display: block;
width: 80%;
margin: 0 auto;
border: 1px solid #9b9b9b;
padding: 10px;
}
main > h1 {
font-size: 1.5em;
}
.tab1highlight {
background-color: #339966;
color: white;
}
.tab2highlight {
background-color: #ff6666;
color: white;
}
.tab3highlight {
background-color: #6600ff;
color: white;
}
main img {
border: 5px solid #eeefff;
width: 80%;
margin-top: 20px;
}
<body>
<nav>
<ul>
<li>Section 1</li>
<li>Section 2</li>
<li>Section 3</li>
</ul>
</nav>
<main>
<h1>Exercise: Navigation Tab #5</h1>
<ul>
<li>
Combine the navigation tab exercises #1, #3, and #4 in one file, including <br>
<ul>
<li>temporarily change the background color of a tab when the cursor is hovering on it.</li>
<li>set the foreground and background color of the tab being clicked.</li>
<li>change the background color of the main element based on the selected tab.</li>
</ul>
<p>
To test, click on a tab and then move your mouse around. For example, the third tab is clicked, the tab background color is switched to blue. Then hover the mouse over the third tab, the background color of the tab should be switch to light green and then back to blue after the mouse moves out.
</p>
<img src="menu_tab5.jpg">
</li>
</ul>
</main>
It's generally a good idea to keep CSS out of JavaScript completely if you can help it. A better strategy for solving the hover problem is to use the CSS pseudo selector :hover rather than coding the color changes in JavaScript. If you give all your tabs the same class, you only have to write the CSS once:
.tab {
background-color: yellow;
}
.tab:hover {
background-color: grey;
}
Once you've done that, you can also relegate the click styling to CSS by creating an event handler that adds and removes a special class each time a tab is clicked.
In the CSS file:
.tab.clicked {
background-color: blue;
}
And then in JavaScript, something like:
var tabs = document.getElementsByClassName('tab');
for (i = 0; i < tabs.length; i ++) {
tabs[i].onclick = function (ev) {
for (i = 0; i < tabs.length; i ++) {
tabs[i].classList.remove('clicked');
}
ev.currentTarget.classList.add('clicked');
};
}
I've created a JSFiddle to illustrate.
Try updating a Boolean variable.
var Ele = document.getElementById('default');
var clicked = false;
Ele.onclick = function(){
clicked = true;
// add additional functionality here
}
Ele.onmouseover = function(){
clicked = false;
// add additional functionality here
}
Ele.onmouseout = function(){
if(!clicked){
// add additional functionality here
}
}
To get a better idea what i'm doing look here for my previous code that i try to make a little better >>Codepen
I want to have an array that i fill up with all the id's that i try to animate and with one function toggle the classes .open .closed on every id in the array.
so on an click add .open to #Hamburger, #Navigation, #Black-filter. and one second click remove .open and add .closed for those id's.
because i'm still learning javascript i want it to work in vanilla javascript so i understand the basics before im going on with jquery.
var hamburger = document.getElementById('Hamburger');
var navigation = document.getElementById('Navigation');
var blackFilter = document.getElementById('Black-filter');
var isOpen = true; // true or false
var animation = [h, s, b]; // #H #S #B
var open = "open"; // .open
var closed = "closed"; // .closed
function trigger() {
if (isOpen === true) {
animation.classList.add(open); // add .open to all id's
animation.classList.remove(closed); // remove .closed from all id's
} else {
animation.classList.add(closed);
animation.classList.remove(open);
}
isOpen = !isOpen; // toggles true to false
}
hamburger.addEventListener('click', trigger, false); // onclick toggle class
blackFilter.addEventListener('click', trigger, false); // onclick toggle class
body {
width: 100%;
}
#Hamburger {
height: 100px;
background: red;
width: 100px;
}
#Hamburger.open {
opacity: 0.5;
}
#Hamburger.closed {
opacity: 1;
}
#Navigation {
height: 100px;
background: blue;
width: 100px;
}
#Navigation.open {
opacity: 0.5;
}
#Navigation.closed {
opacity: 1;
}
#Black-filter {
height: 100px;
background: green;
width: 100px;
}
#Black-filter.open {
opacity: 0.5;
}
#Black-filter.closed {
opacity: 1;
}
<body>
<div id="Hamburger"></div>
<div id="Navigation"></div>
<div id="Black-filter"></div>
</body>
What you are looking for is:
var isOpen = true;
var hamburger = document.getElementById('Hamburger');
var navigation = document.getElementById('Navigation');
var blackFilter = document.getElementById('Black-filter');
var animatable = [hamburger, navigation, blackFilter];
var openClass = "open"; // .open
var closedClass = "closed"; // .closed
function trigger() {
if (isOpen) {
animatable.forEach(function (element) {
element.classList.add(openClass);
element.classList.remove(closedClass);
});
} else {
animatable.forEach(function (element) {
element.classList.add(closedClass);
element.classList.remove(openClass);
});
}
isOpen = !isOpen;
}
hamburger.addEventListener('click', trigger, false);
blackFilter.addEventListener('click', trigger, false);
Demo
There are a few things that need improvement.
First of all you are naming you variables rather poorly. Which is actually already one of your problems, first you say that
var b = document.getElementById('B');
and then later
var b = "closed";
So this needs to be fixed, use variable names that are descriptive so you will know what you are talking about when.
Last but not least you are trying to change the elements of that array a, not the array itself. So you need to access the elements by themselves, set their classes and then you are good to go e.g.:
for( var index in a ) {
if ( open === true ) {
a[index].classList.add(b);
a[index].classList.remove(c);
} else {
a[index].classList.add(c);
a[index].classList.remove(b);
}
open = !open;
Firstly ou don't need "open" AND "close" classes, only one would clearly simplify your code (and there is the "default" state).
Then, add a class for all your buttons, the easily manipulate them in JS and CSS (here the class ".btn");
// Directly get on array (a NodeList more precisely)
var buttons = document.getElementsByClassName('btn');
function toggleClass() {
// Loop to add or remove (toggle) the the '.open' class
for (var i=0; i<buttons.length; i++) {
buttons[i].classList.toggle('open');
}
}
// Loop to add event listener to all buttons
for (var i=0; i<buttons.length; i++) {
buttons[i].addEventListener('click', toggleClass, false);
}
.btn {
height: 100px;
width: 100px;
}
.btn.open {
opacity: 0.5;
}
#Hamburger { background: red; }
#Navigation { background: blue; }
#Black-filter { background: green; }
<div id="Hamburger" class="btn"></div>
<div id="Navigation" class="btn"></div>
<div id="Black-filter" class="btn"></div>
This is already way simpler. But you should have a parent element holding the opened/closes state, so you wouldn't loop in an array.
// Only need to manipulate one DOM node
var menu = document.getElementById('menu');
function toggleClass() {
menu.classList.toggle('open');
}
menu.addEventListener('click', toggleClass, false);
body {
width: 100%;
}
.btn {
height: 100px;
width: 100px;
}
.menu.open > .btn {
opacity: 0.5;
}
#Hamburger { background: red; }
#Navigation { background: blue; }
#Black-filter { background: green; }
<div class="menu" id="menu">
<div id="Hamburger" class="btn"></div>
<div id="Navigation" class="btn"></div>
<div id="Black-filter" class="btn"
</div>
Your event listener gets the event as the 1st argument. Use it to decide what to do:
function trigger(event) {// use event.target ... }