First I think that using document.querySelector
seems much user friendly than using document.getElementsByClassName
. Instead of let coll = document.getElementsByClassName('collection'); coll[0].xxx
, we can just use document.querySeletor('collection').xxx
.
So code can refactor a bit to
let coll = document.querySelector('.collection');
let dis = document.querySelector('.display');
let drop = document.querySelector('.drop');
document.getElementById("menu-btn1").addEventListener("click", function() {
if(coll.style.display == 'none') {
coll.style.display = 'block';
dis.style.display = 'none';
drop.style.display = 'none';
} else {
coll.style.display = 'none';
}
});
Next, I will place the DOM retrieval code into the event handler, to prevent unnecessary namespace pollution. Though maybe in your working code, you may use the coll
for other handling, then it is okay to place there. In addition, I don't think one will change the variable of a DOM to other value, so I would use a constant
instead.
document.getElementById("menu-btn1").addEventListener("click", function() {
const coll = document.querySelector('.collection');
const dis = document.querySelector('.display');
const drop = document.querySelector('.drop');
if(coll.style.display == 'none') {
coll.style.display = 'block';
dis.style.display = 'none';
drop.style.display = 'none';
} else {
coll.style.display = 'none';
}
});
However these changes does not resolve the fact that display: none
is actually scattered in both HTML and Javascript. I think a better idea is just to place the code in one place. That is remove the styling in HTML.
To do so, I think there are possibly 2 approaches, one is the use of RxJS
, another way is to use of data driven approach, e.g. React
, Angular
, Vue
, ...
Data Driven Approach: Use of VueJS
Let's first have a look at How we can implement it in Data Driven Approach.
I will use Vue for demonstration, as it is the simplest to setup among the three.
new Vue({
el: "#app",
template: `
<div class="ui">
<div class="menu">
<span id="text"><span @click="show = !show">collection</span></span>
</div>
<div class="collection" :style="styles">
Collection Inside
</div>
</div>
`,
data: function() {
return {
show: false
}
},
computed: {
styles: function() {
return {
"display": this.show ? "block": "none"
};
},
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
As you can see, the HTML code is now placed in Javascript as a template, and we make use of :style
and @click
in the template, instead of normal HTML. These are Vue specific
syntax and is used in Vue for easy interaction between the Vue component Javascript and the template.
Event Driven Approach: RxJS
Another approach would be RxJS, that just work without a framework, so makes it more easy to adopt in a legacy web application.
rxjs.merge(
rxjs.of(1), // so that it immediately triggers
rxjs.fromEvent(document.querySelector("#menu-btn1"), 'click'),
)
.pipe(
rxjs.operators.scan((accum, value, index) => {
return index % 2 == 1;
}, false)
).subscribe(show => {
const coll = document.querySelector(".collection");
const value = show ? "block" : "none";
coll.style.display = value;
}
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.6.3/rxjs.umd.min.js"></script>
<div class="menu">
<span id="text"><span id="menu-btn1">collection</span></span>
</div>
<div class="collection">
Collection Inside
</div>
Here is all the solution I think of, and it is left to you to decide which way is the cleanest code. To me, I just like the way RxJS
is constructed, and do not need to maintain an extra data for storage.
display: none
in your code. \$\endgroup\$ – tsh Mar 2 at 6:21