I have a forEach loop which displays menus for different restaurants when the restaurant icon is clicked. The restaurant icons and the number of restaurants/menus is unknown and each is created with a dynamic ID, e.g. menu-0, menu-1, menu-2 etc.
When one restaurant icon is clicked, all the others should be hidden. Currently I have the below Javascript to show/hide the menus; while this is working, I'm working for a way to make it more dynamic/efficient, rather than a static list, given the number of menus is unknown.
let menu0 = document.getElementById('menu-0');
let menuIcon1 = document.getElementById('menu-icon-1');
let menu1 = document.getElementById('menu-1');
let menuIcon2 = document.getElementById('menu-icon-2');
let menu2 = document.getElementById('menu-2');
let menuIcon3 = document.getElementById('menu-icon-3');
let menu3 = document.getElementById('menu-3');
let menuIcon4 = document.getElementById('menu-icon-4');
let menu4 = document.getElementById('menu-4');
$('#menu-icon-0').on('click', function(e) {
if (menu0.style.display = 'none') {
menu0.style.display = 'block';
menuIcon0.style.border = '5px solid #2541B2';
menuIcon1.style.border = 'none';
menu1.style.display = 'none';
menuIcon2.style.border = 'none';
menu2.style.display = 'none';
menuIcon3.style.border = 'none';
menu3.style.display = 'none';
menuIcon4.style.border = 'none';
menu4.style.display = 'none';
}
})
$('#menu-icon-1').on('click', function(e) {
if (menu1.style.display = 'none') {
menu1.style.display = 'block';
menuIcon1.style.border = '5px solid #2541B2';
menuIcon0.style.border = 'none';
menu0.style.display = 'none';
menuIcon2.style.border = 'none';
menu2.style.display = 'none';
menuIcon3.style.border = 'none';
menu3.style.display = 'none';
menuIcon4.style.border = 'none';
menu4.style.display = 'none';
}
})```
from my experience you have to add custom class to your dynamic elements , then you can set the click on the class name for example, and inside the click you search for all elements that have this class, make for loop and compare the id of element with the clicked view id if not equals you should hide the boarder.
you are using jquery right? so all this can be boiled down to
$('#menu-icon-0').on('click', function(e) {
$('#menu-icon-0').toggle()
}
Related
I have my program set up so that it autogenerates HTMl and places it into the webpage. Each introduction of new HTMl is given a new ID following along which increment it is at e.g <div id = "genQuestion0"> <div id = "genQuestion1">etc.
The inital div's have all been set through CSS as no display
[id^="genQuestion"]{
text-align: center;
display: none;
}
The first question id = genQuestion0 has been set that upon generation its display is set to block.
How can I make it so that when a button is pressed the previous questions display is changed to none and the next one is set to block.
This is what I have currently for my function but it does not work.
var countclicks = 0;
const nextQuestion = () =>{
var elementName = `genQuestion${countclicks}`;
var elementNameNeg = `genQuestion${(countclicks - 1)}`;
elementName.style.display = "block";
elementNameNeg.style.display = "none";
countClicks ++;
}
It's because your is just string and not DOM element.
You can access that element by using document.getElementById(elementNameNeg).style.display = 'block';
So change to this?
var countclicks = 0;
const nextQuestion = () =>{
var elementName = `genQuestion${countclicks}`;
var elementNameNeg = `genQuestion${(countclicks - 1)}`;
document.getElementById(elementName).style.display = "block";
document.getElementById(elementNameNeg).style.display = "none";
countClicks ++;
}
I have a foreach loop going through my posts and each loop produces an html card with info inside from each post.
I want to show and hide items within the card on only that one card not every card that has been loaded. But I will need something to differentiate between the individual cards so the javascript doesn't hide all the info on all of the cards.
I wanted to know if anyone knows how I can achieve this with a javascript function to search only in elements within that element and not search the whole page.
My current show hide method
function show_hide_button_one() {
var a = document.getElementById("grid-item-1");
var b = document.getElementById("grid-item-2");
a.style.display = "block";
b.style.display = "none";
}
}
function show_hide_button_two() {
var x = document.getElementById("grid-item-1");
var y = document.getElementById("grid-item-2");
y.style.display = "block";
x.style.display = "none";
}
}
But with that method it shows and hides everything with the relative class
and not just for the card in use.
Its for lot's of posts. So I need a smarter route than my own method xD
pls help.
You have a couple of different options. You can use HTML data attributes to give each card unique identifiers beyond just ID or class name, or you can just make each card's ID's unique based on the post info. (Be careful not to give more than one card the same ID, as that will mess up your code!)
Here is a good artice on HTML data attributes:
https://www.sitepoint.com/how-why-use-html5-custom-data-attributes/
PS - Also, do this instead:
function show_hide_button(c1, c2) {
var a = document.getElementById(c1);
var b = document.getElementById(c2);
a.style.display = a.style.display === "block" ? "none" : "block";
b.style.display = b.style.display === "block" ? "none" : "block";
}
}
You must be having same id for all card. Have separate id for each card/button (whatever you are trying to hide). You will achieve what you desire.
Try doing this
//HTML
<div id="card1>
...
<button id="card1id1"></button>
<button id="card1id2"></button>
</div>
<div id="card2>
...
<button id="card2id1"></button>
<button id="card2id2"></button>
</div>
// JS
function show_hide_button_one(id1,id2) {
var a = document.getElementById(id1);
var b = document.getElementById(id2);
a.style.display = "block";
b.style.display = "none";
}
}
function show_hide_button_two(id1,id2) {
var x = document.getElementById(id1);
var y = document.getElementById(id2);
y.style.display = "block";
x.style.display = "none";
}
}
// calling
show_hide_button_one("card1id1", "card1id1")
show_hide_button_two("card1id1", "card1id1")
Yesterday I asked a question about improving efficiency in my code. Today I have another question in the same spirit of trying to write less lines of code to accomplish repetitive tasks.
I have the following code:
function myIntroductionText() {
introPos.style.display = 'block';
posOne.style.display = 'none';
posTwo.style.display = 'none';
posThree.style.display = 'none';
posFour.style.display = 'none';
posFive.style.display = 'none';
posSix.style.display = 'none';
posSeven.style.display = 'none';
posEight.style.display = 'none';
posNine.style.display = 'none';
posTen.style.display = 'none';
posEleven.style.display = 'none';
backButton.style.visibility = 'hidden';
}
function myPositionOne() {
introPos.style.display = 'none';
posOne.style.display = 'block';
posTwo.style.display = 'none';
posThree.style.display = 'none';
posFour.style.display = 'none';
posFive.style.display = 'none';
posSix.style.display = 'none';
posSeven.style.display = 'none';
posEight.style.display = 'none';
posNine.style.display = 'none';
posTen.style.display = 'none';
posEleven.style.display = 'none';
backButton.style.visibility = 'visible';
}
function myPositionTwo() {
introPos.style.display = 'none';
posOne.style.display = 'none';
posTwo.style.display = 'block';
posThree.style.display = 'none';
posFour.style.display = 'none';
posFive.style.display = 'none';
posSix.style.display = 'none';
posSeven.style.display = 'none';
posEight.style.display = 'none';
posNine.style.display = 'none';
posTen.style.display = 'none';
posEleven.style.display = 'none';
}
The HTML looks something like this:
<p class="textContent" id="introductionText">Introduction Text Goes Here</p>
<p class="textContent" id="position1">content1</p>
<p class="textContent" id="position2">content2</p>
<p class="textContent" id="position3">content3</p>
Each position (i.e. introPos, posOne, posTwo) also has a corresponding function that looks essentially the same as the function above, except it changes the display based on which position it is in.
I'm thinking that I could use a loop and/or an if/else statement to make this task more efficient. I tried by using getElementsByClassName('textContent'), which (I think) produced an array containing all of the elements with that class. According to the console.log is contains [p#introductionText.textContent, p#position1.textContent, so on and so on...]. So, I wrote the following code to try to loop through it:
var blanks = document.getElementsByClassName("textContent") // this creates the array that I mentioned
for (item in blanks) {
if (blanks[0] === introductionText.textContent) {
blanks[0].style.display = 'block';
} else {
blanks[item].style.display = 'block';
}
}
I tried using p#introductionText.textContent but that returned an error. I'm very new to JavaScript so I fully recognize that I could be doing something very silly here, but any help would be appreciated.
EDIT:
The error message says Uncaught SyntaxError: Unexpected tocken ILLEGAL
I should also add that my goal is to have only one position be visible at each time. I have a "Back" and "Next" button that allows users to go from posOne to posTwo, to posThree, and so on. So, in addition to making posTwo visible, I also need to make posOne and/or posThree not visible.
Thanks!
The first thing is moving all those Javascript style expressions to CSS:
#introPos,
#posOne,
#posTwo,
#posThree,
#posFour,
#posFive,
#posSix,
#posSeven,
#posEight,
#posNine,
#posTen,
#posEleven {
display: none;
}
Or even shorter
#introductionText>.textContent {
display: none;
}
This would enable you to shorten each function considerably:
function myPositionOne() {
posOne.style.display = 'block';
backButton.style.visibility = 'visible';
}
Instead of setting each style via JS again and again, you'd simply set those that change.
The next step would be to rewrite all those functions into one that accepts a parameter which element you are targeting:
function myPosition(pos) {
var parent = document.getElementById("text-container");
var children = parent.getElementsByClassName("textContent");
var element;
// first hide all <p class="textContent"> children
for (var i = 0; i < children.length; i++) {
children[i].style.display = 'none';
if (i == pos) {
element = children[i];
}
}
// then show the right one
if (element) {
element.style.display = 'block';
}
// show or hide the back button depending on which child we are dealing with
if (pos > 0) {
document.getElementById("backButton").style.visibility = 'visible';
} else {
document.getElementById("backButton").style.visibility = 'hidden';
}
if (pos >= children.length-1) {
document.getElementById("nextButton").style.visibility = 'hidden';
} else {
document.getElementById("nextButton").style.visibility = 'visible';
}
}
This sets only the child number #pos visible and adjusts the visibility of the back button (assuming the back button has the ID "backButton").
Maybe this:
All paragraphs also have the class "textContent". Make this display none and display the correct paragraph via given paragraph-id:
function myFunction(classDisplay) {
var elems = document.getElementsByClassName('textContent');
for (var i=0;i<elems.length;i+=1){
elems[i].style.display = 'none';
}
document.getElementById(classDisplay).style.display = "block";
}
The following will hide all but position 2:
myFunction("position2");
I don't know about the back-button, this is always be visible?
EDIT: I've tested this and corrected the code.
If you use JQuery, you can also use the following instead of the for loop:
$('.textContent').css('display','none');
In newer versions of JavaScript you can use:
Array.from(document.getElementsByClassName('myclass')).forEach((item) => {
item.style.backgroundColor = "blue";
})
I want clicking on an "expando" to toggle between its states: expanded and collapsed.
I'm still pretty new to DOM/JS, so my style here is probably awful; If you have any style guidelines let me know, but for right now I want to get the code working. I've tried a few different ways, like setting the expand or collapse behavior in dom's onclick (and changing it in the expand and collapse functions), but if I do that, then for some reason clicking doesn't trigger a collapse, but it will trigger an expand.
The problem with the code below is that I can expand an expando, but when I click on it, it also triggers the collapse, so it expands and then immediately collapses back.
var expandos = document.getElementsByTagName("expando");
var uid = 0;
for(var i=0; i<expandos.length; ++i) {
var dom = expandos[i];
dom.id = "expando_"+uid++;
var iframe = document.createElement("iframe");
iframe.src = dom.innerHTML;
iframe.name = dom.id +".big";
iframe.id = iframe.name;
iframe.scrolling = "no";
iframe.style.display = "inline";
iframe.onclick = collapse(dom);
var p = document.createElement("p");
var text = document.createTextNode(dom.innerHTML);
p.id = dom.id+".small";
p.style.display = "inline";
p.appendChild(text);
p.onclick = expand(dom);
dom.innerHTML = "";
/* We have to clear the innerHTML to prevent the original text from
showing up in addition to the text added by p.
*/
dom.appendChild(iframe);
dom.appendChild(p);
/* We have to append iframe and p **after** we clear innerHTML
because otherwise clearing innerHTML will clear the appended
children.
*/
function expand(dom) {
return function() {
alert("Expanding "+dom.id);
var iframe = document.getElementById(dom.id+".big");
var p = document.getElementById(dom.id+".small");
p.style.display = "none";
iframe.style.display = "initial";
dom.onclick = collapse(dom);
}
}
function collapse(dom) {
return function() {
alert("Collapsing "+dom.id);
var iframe = document.getElementById(dom.id+".big");
var p = document.getElementById(dom.id+".small");
p.style.display = "initial";
iframe.style.display = "none";
dom.onclick = expand(dom);
}
}
collapse(dom)();
}
The sample HTML I'm testing on:
<body>
<expando>The quick brown</expando> fox jumps over <expando>the lazy dog</expando>.
<script src="loadExpandos.js"></script>
</body>
In the same directory, I have files named "The quick brown" and "the lazy dog", and they expand properly.
A quick fix for to get the basic functionality you want is to combine your expand and collapse into a single function and have an if/else block that checks the state. Not 100% on what caused your original issue, but I'd guess it has something to do with your onClick events not being cleared.
function clickHandler(dom) {
return function() {
var iframe = document.getElementById(dom.id+".big");
var p = document.getElementById(dom.id+".small");
if(p.style.display === "initial"){
p.style.display = "none";
iframe.style.display = "initial";
} else {
p.style.display = "initial";
iframe.style.display = "none";
}
I have javascript code to toggle hide and show information and works for one showMoreText section of text, but want 16 different toggle options for all the different links. Included is the image that works for the "Montana's Glacier" underneath Why is it Important? It does toggle, but I created another javascript page (toggle2.js) specifically for the "Ice now covers..." link, but it will not appear.
Code for first toggle option in toggle.js
window.onload = function(){
document.getElementById('toggle').onclick = showMore;
}
function showMore(){
var div = document.getElementById('showMoreText');
var display = div.style.display;
display == "none" ? div.style.display = "block" : div.style.display = "none";
}
Code for second toggle option in toggle2.js
window.onload = function(){
document.getElementById('toggle2').onclick = showMore;
}
function showMore(){
var div = document.getElementById('showMoreText2');
var display = div.style.display;
display == "none" ? div.style.display = "block" : div.style.display = "none";
}
Code within HTML for toggle1
<div id="showMoreText" style="display: none"> (missing paranthesis)
Code within HTML for toggle2
<div id="showMoreText" style="display: none"> (missing paranthesis)
What is wrong? I want to be able to toggle to many different toggles (1-16) but when I click on toggle1, and want to click on toggle 2, it does not change. what variable is barring me from it work properly?
Thank you!
Further Optimised based on Aleks G answer:
Your script:
window.onload = function(){
for(var i=0; i<3; i++){ //here is set to 3
document.getElementById('toggle'+i).onclick = function(i){
return function(){
showMore('showMoreText'+i);
}
}(i);
}
}
function showMore(id){
var div = document.getElementById(id),
display = div.style.display;
display == "none" ? div.style.display = "block" : div.style.display = "none";
}
HTML:
<div id="toggle1" class="toggle"></div>
<div id="toggle2" class="toggle"></div>
<div id="toggle3" class="toggle"></div>
CSS:
div.toggle{
display: none;
}
Actually, you don't even need the id in div. You can just loop through the class toggle and find all the divs.
Why do you need to have all toggles separately? Why not just do this?
window.onload = function(){
document.getElementById('toggle').onclick = showMore1;
document.getElementById('toggle2').onclick = showMore2;
document.getElementById('toggle3').onclick = showMore3;
...
}
function showMore1() { showMore('showMoreText'); }
function showMore2() { showMore('showMoreText2'); }
function showMore3() { showMore('showMoreText3'); }
...
function showMore(id){
var div = document.getElementById(id);
var display = div.style.display;
display == "none" ? div.style.display = "block" : div.style.display = "none";
}
This is quite crude, feel free to optimise further.
Not a complete answer, but just to go with the above, the window.onload function can be simplified into a single object, and the code recylcled.
window.onload = function() {
var tList={'toggle1': 'showMore1','toggle2': 'showMore2','toggle3': 'showMore3'};
for(var i in tList){document.getElementById(i).onclick = tList[i]};
}