Toggle icon flips no matter what section is opened - javascript

I am new to all of this so please excuse any misuse of terms.
I have added a toggle to my wordpress site to hide long sections of text and it seems to work just fine.
I wanted to add an arrow that flips depending on whether the section is open or not. My problem is the arrow flips back and forth no matter what section is toggled and I don't know how to fix that.
JS:
function toggle(id) {
var element = document.getElementById(id);
var text = document.getElementById("arrow");
if (element) {
var display = element.style.display;
if (display == "none") {
element.style.display = "block";
text.innerHTML = "▲";
} else {
element.style.display = "none";
text.innerHTML = "▼";
}
}
}
HTML:
<h4>Procedure</h4>
<h4 onclick="toggle('telnetPrint')">Telnet<a id="arrow">▼</a></h4>
<div id="telnetPrint" style="display: none;">
<ol>
<li>fjkldsaj;lkf</li>
</ol>
<h4 onclick="toggle('telnetPrint')">Hide -</h4>
</div>
<p> </p>
<h4 onclick="toggle('linuxPrint')">Linux Computer▼</h4>
<div id="linuxPrint" style="display: none">
<ol>
<li>fjkldsjfklsa</li>
</ol>
<h4 onclick="toggle('linuxPrint')">Hide -</h4>
</div>
If anyone can help, I'd greatly appreciate it.
p.s. no jQuery please

It looks like you are calling the same "arrow". You only have arrow set for Telnet. You can add an arrow as well to linuxPrint. I would ID them as :
<a id="arrowtelnetPrint"></a>
and
<a id="arrowlinuxPrint"></a>
That way you can use the "id" to change the correct one. Here is the jsfiddle:
https://jsfiddle.net/ezdrhLtr/2/
This will have the full code, with minor adjustments, that toggle both arrows.

var text = document.getElementById("arrow");
you are referencing an element with id 'arrow'. Everytime the toggle function is executed, you will flip the element with id 'arrow'. What you can do is pass a boolean value to know if its to be flipped or not
<h4 onclick="toggle('telnetPrint',true)">Telnet<a id="arrow">▼</a></h4>
and in your script
function toggle(id, flipOrNot) {
var element = document.getElementById(id);
var text = document.getElementById("arrow");
if (element) {
var display = element.style.display;
if (display == "none") {
element.style.display = "block";
if(flipOrNot){
text.innerHTML = "▲";
}
} else {
element.style.display = "none";
if(flipOrNot){
text.innerHTML = "▼";
}
}
}
}
for other elements, you can do
<h4 onclick="toggle('linuxPrint',false)">Hide -</h4>
to prevent flip

Related

Javascript - get class of another element with onclick

I want to individually toggle to different divs using the same function. Each of these divs has a common class and a different id. The function toggle is called using an onclick parameter on two separate <a> elements:
<a class="btn" id="btnOne" onclick="toggler();">Show/hide divOne</a>
<div class="box" id="divOne">
<a class="btn" id="btnTwo" onclick="toggler();">Show/hide divTwo</a>
<div class="box" id="divTwo">
I first tried to get these divs with getElementsByClassName but, as it returns an HTMLCollection, the script can't target each div individually.
So I tried to select the <a> tags ids (btnOne and btnTwo), but couldn't figure out how to retrieve the divs class using these ids (as we're talking about two different elements here).
In the end, I came back to the getElementById method, as I couldn't figure out how to select them based on their class:
function toggler() {
var id = document.getElementById("divId");
if (id.style.display === "none") {
id.style.display = "block";
} else {
id.style.display = "none";
}
};
This leaves me with two functions instead of just one. Any suggestion on how to target the two divs individually?
You can access the next sibling using nextElementSibling presuming the box will always be right after the hyperlink.
// Put the buttons into an array
const buttons = [...document.getElementsByClassName("btn")];
// Assing an event listener for every button
buttons.map(button => button.addEventListener("click", function(e) {
// Find the next sibling
const box = e.target.nextElementSibling;
// Toggle the display value
if (box.style.display === "none") {
box.style.display = "block";
} else {
box.style.display = "none";
}
}));
a {
display: block;
}
.box {
width: 5rem;
height: 2rem;
background-color: blue;
}
<a class="btn">Show/hide divOne</a>
<div class="box"></div>
<a class="btn">Show/hide divTwo</a>
<div class="box"></div>
There is a simple way to select the divs with their class name and you already used it.
The answer is getElementsByClassName. But in vanilla JS things are a little bit (over)complicated.
It will not target both divs individually. Instead, if you want to select the first div with this class you would do it like this:
getElementsByClassName('classname')[0]
If you want to select the second div you would use:
getElementsByClassName('classname')[1]
and so on. But there is a way of course.
You want to use loops:
var x = document.getElementsByClassName("classname");
for (var i = 0; i < x.length; i++) {
if (x[i].style.display === "none") {
x[i].style.display = "block";
} else {
x[i].style.display = "none";
}
}
In this way, you will target ALL divs with this class.
I'd dynamically add the events on the switches, using their classes. I added the class showHideDivBtn to them. To make sure you know which div you have to toggle, I used a data-id.
With addEventListener, I can use the event variable I named e. With this one, I have access to properties, such as the data-id I wrote.
let buttons = document.getElementsByClassName("showHideDivBtn");
for (let i = 0; i < buttons.length; ++i)
{
buttons[i].addEventListener('click', function(e)
{
let divToToggle = document.getElementById(e.srcElement.dataset.id);
if (divToToggle.style.display === "none")
divToToggle.style.display = "block";
else
divToToggle.style.display = "none";
});
}
<a class="btn showHideDivBtn" data-id="divOne" id="btnOne">Show/hide divOne</a>
<div class="box" id="divOne">One</div>
<br />
<a class="btn showHideDivBtn" data-id="divTwo" id="btnTwo">Show/hide divTwo</a>
<div class="box" id="divTwo">Two</div>
Use substr to get the word after extracting 'btn' from anchor id which will result in One or Two then while defining the if use "div"+word this will get the div by it is related a tag
function toggler() {
var word=this.id.substr(3);
var id = document.getElementById("div"+word);
if (id.style.display === "none") {
id.style.display = "block";
} else {
id.style.display = "none";
}
};

Hide all elements within a class that don't match the ID of a variable

I have a site with tiles much like the Windows 10 Start menu.
I need these tiles to have fullwidth drop downs once clicked, I'll need the dropdown to close if the same tile is clicked again and I need one dropdown to close others if a different tile is clicked.
These tiles will have all sorts of names so I was hoping to create some javascript that would be dictated by the ID of the tile clicked.
Here's what I have so far:
<div id="gaming" class="box one-one blue" onclick="showSection(this);">
<div id="gaming-section" class="section">
<div id="marketing" class="box one-one blue" onclick="showSection(this);">
<div id="marketing-section" class="section">
function showSection(obj) {
var tileName =obj.getAttribute('id');
var sectionName =(tileName+'-section');
document.getElementById(sectionName).style.display = "block";
}
Any help would be much appreciated
exchange document.getElementById(sectionName).style.display = "block"; for this:
const elemStyle = document.getElementById(sectionName);
if (elem.style.display !== 'block') {
elem.style.display = 'block';
else {
const elements = document.getElementsByClassName('section');
elements.forEach(elem => {
elem.style.display = 'none';
}
}

using javascript to change display

So I looked at other questions and I found an answer that was chosen on this link:
Toggle (show/hide) element with javascript
This below was the following function that works in the answer to change the display.
function toggle(id) {
var element = document.getElementById(id);
if (element) {
var display = element.style.display;
if (display == "none") {
element.style.display = "block";
} else {
element.style.display = "none";
}
}
}
I tried that in my code, but it's not working. I've attached a link to the JSFiddle at the end or this post.
I have a parent div with the id of #activities. It contains multiple children, but the important ones are the li,p, and div which has an id as #suggestion_input. Below is the HTML.
HTML
<div id="activities" class="info_container">
<h1>Our Activities</h1>
<div class="contain">
<ul>
<li>Activity 1</li>
<li>Activity 2</li>
<li>Activity 3 </li>
<li>Activity 4 </li>
<li>Activity 5</li>
<li>Activity 6 </li>
<li>Activity 7</li>
<li>Your Suggestions</li>
</ul>
<p>
Bacon ipsum dolor sit amet ribeye tenderloin meatball, chuck andouille beef ribs jerky ...
</p>
<div id="suggestion_input">
<label for="name" >Your Name</label>
<input type="text" id="name" name="name">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<label for="suggestions">Suggestions</label>
<textarea id="suggestions" name="suggestions" rows="39"></textarea>
</div>
When the user clicks the last li in the ul called "Your Suggestions", then the p will have display set to none and the #suggestion_input will have display of inline-block. Currently, their css is set to inline-block and none respectively.
CSS
#activities p{
display:inline-block;
width:500px;
vertical-align:top;
margin-top:20px;
margin-left:20px;
background:#FFFDA1;
}
#suggestion_input{
display:none;
margin-left:150px;
vertical-align:top;
margin-top:20px;
text-align:center;
}
And then this is my javascript which I think reflects the answer in the link except that it isn't a function.
Javascript - This code is part of an addEventListener. Event is a "click".
if(e.target.innerText === 'Your Suggestions'){
var para = document.getElementById('activities').querySelector('p');
var display = para.style.display;
/* if you uncomment this, then the following code will work
outside of the if display == 'inline-block' condition
para.style.display = 'none';
suggestion_input.style.display = 'inline-block';
*/
if(display == 'inline-block'){
// This code will not work
para.style.display = 'none';
suggestion_input.style.display = 'inline-block';
console.log('works');
}
}else{
if(display == 'none'){
display = "inline-block";
}
}
}
When I click, nothing happens. Is the error because of the condition in the if statement of "display == 'none'" ?
Here is the JSfiddle: http://jsfiddle.net/a4t7w/1/
There is a couple of error in your js , I listed them in the comment , 4 in totoal
document.getElementById('activities').addEventListener("click",function(e){
// SKIP THIS CODE, THE ERROR LIES BELOW
// 1. put the var outside of the if/else
var para = document.getElementById('activities').querySelector('p');
var display = para.style.display;
// THIS WAS WHERE THE ERROR OCCURS
if(e.target.innerText === 'Your Suggestions'){
if(display = 'inline-block'){ // 2. it should be "=" instead of "=="
para.style.display = 'none';
suggestion_input.style.display = 'inline-block';
console.log('works');
}
}else{
if(display = 'none'){
suggestion_input.style.display = 'none'; // 3. add in suggestion display none
para.style.display = 'inline-block'; // 4. "para.style.display" instead of "display"
}
}
}
});
A working demo : http://jsfiddle.net/a4t7w/7/
So now when you click suggestion , the from show up, than if you click the activity, the paragraph come back in, from disappear
Okay, so to start, part of your problem is with the understanding that para.style.display equals inline-block like is set in your style sheet. Unfortunately in this usage Javascript is accessing your inline styles as defined with the element itself and not what you are defining with external CSS.
So this statement: if(display == 'inline-block') never returns true because display is actually set to "". If you aren't already, get familiar with Firebug for Firefox. It does wonders for helping debug this kind of stuff.
Another thing is here: if(e.target.innerText === 'Your Suggestions'). Instead of innerText you should use innerHTML or textContent. The innerText property is an IE thing.
Now, to solve your problem! I see you are attaching the click event to the <div> element that holds your <ul> element. If you only need this click event you function on your "Suggestions" <li> then I would recommend you isolate that particular element when attaching your listener.
Change your HTML and JS:
<li id="suggestionToggle">Your Suggestions</li>
...
document.getElementById('suggestionToggle').addEventListener("click",function(e) ...
Next you can rewrite your Javascript to test for the presence of "" or inline-block when setting your styles on the <p> element. Here's an updated version of your code:
document.getElementById('activities').addEventListener("click",function(e)
{
var suggestion_input = document.getElementById('suggestion_input');
var para = document.getElementById('activities').querySelector('p');
var light_green = document.getElementById('activities').querySelector('.light_green');
if(light_green){
light_green.style.backgroundColor="red";
light_green.className = "";
e.target.style.backgroundColor = '#0DFFB9';
e.target.className = 'light_green';
} else {
e.target.style.backgroundColor = '#0DFFB9';
e.target.className = 'light_green';
}
if(e.target.innerHTML == 'Your Suggestions') {
var display = para.style.display;
if(display == 'inline-block' || display == "") {
// This code will not work ** It will now! **
para.style.display = 'none';
suggestion_input.style.display = 'inline-block';
} else {
if(display != "inline-block") {
para.style.display = "inline-block";
suggestion_input.style.display = "none";
}
}
} else {
para.style.display = "inline-block";
suggestion_input.style.display = "none";
}
});
JS Fiddle to see the toggle in action is here: http://jsfiddle.net/RyUz5/8/
Hope that helps!
EDIT: Enabled proper toggling that I accidentally stripped out as noted by yancie.
Read this line carefully:
/*
if you uncomment this, then the following code will work
outside of the if display == 'inline-block' condition
para.style.display = 'none';
suggestion_input.style.display = 'inline-block';
*/
now all you have to do is uncomment this line:
para.style.display = 'none';
suggestion_input.style.display = 'inline-block';

JavaScript onclick requires two clicks

My problem is that when onclicktriggers the toggleNew function it's not executing but when I click the div a second time it's executing just as it should...
HTML:
<div id="aside_main">
<div onclick="toggleNew();">click</div>
content
</div>
<div id="aside_new">
content
</div>
JS:
function toggleNew() {
var e = document.getElementById('aside_main');
var se = document.getElementById('aside_new');
if(e.style.display == 'block') {
e.style.display = 'none';
se.style.display = 'block';
} else {
e.style.display = 'block';
se.style.display = 'none';
}
}
CSS:
#aside_main {
display: block;
}
#aside_new {
display: none;
}
What is happening here and how can I make the function work the first time a user clicks the div?
This will not work properly because you are using following line inside 'div#aside_main' which is going to be hidden.
<div onclick="toggleNew();">click</div>
Try keeping it outside like this-
<div onclick="toggleNew();">click</div>
<div id="aside_main">
content
</div>
<div id="aside_new">
content2
</div>
Also in javascript it is not checking for 'e.style.display' first time in if condition.
Try using
if(e.offsetWidth > 0 || e.offsetHeight > 0){
e.style.display = 'none';
se.style.display = 'block';
}
else
{
e.style.display = 'block';
se.style.display = 'none';
}
You need to call the function like onclick="toggleNew();" in the div onclick. I just added your code in fiddle.
May not be the best answer, but the fix was to use inline css by style attribute.
Like this:
<div id="aside_main" style="display: block; border: 2px solid green;">
<div onclick="toggleNew();">click</div>
content
</div>
<div id="aside_new" style="display: none; border: 2px solid red;">
content
</div>
e.style.display represents the style of the element defined by the style attribute, it does not give you the computed style. to get the computed style use
if (window.getComputedStyle(e,null).getPropertyValue("display") == 'block){
I had the same double click required issue. I was using an internal style sheet which was correctly setting the display like this.
When loading the HTML file #YourID was not visible as expected.
#YourID {
display: none;
}
When clicking the button tied to the function I noticed that the first click set the inline display to style="display: none;". The second click set the inline style="display: block;" and of course then it displayed as expected.
I found that I needed to set the element directly inline with style="display: none;" and just removed the intern style sheet entry (Above "#YourID").
I'm doubtful that this is 100% the correct answer in every scenario but it would seem the underlying issue is caused by the element not being set in the appropriate initial state for the function to act on it properly.
https://jsfiddle.net/em05a1kf
<div id="YourID" style="display: none;">
<b>Super Hidden Content</b>
</div>
<button onclick="ToggleID('YourID');">Do Foo</button>
<script type="text/javascript">
function ToggleID(idname) {
var x = document.getElementById(idname);
(x.style.display === "none") ? (x.style.display = "block") : (x.style.display = "none");
return false;
}
</script>
In your condition : Use onclick="toggleNew();" // calling
This is the way to call a function.
And if you want to Pass the function then you use only toggleNew //passing
They are two different activities.
Here is another way of doing that. You just need to add two lines to your javascript code-
document.getElementById('aside_main').style.display='block';
document.getElementById('aside_new').style.display='none';
set initial display property in javascript. This will work fine.
One way (for me the simplest way) to solve this is by counting clicks.
For this purpose you set the new intiger variable click to 0 outside of your function toggleNew() and everytime you call your function you increase variable click for 1 like this:
<script> var click = 0;
function toggleNew() {
click = click +1;
var e = document.getElementById('aside_main');
var se = document.getElementById('aside_new');
if (click > 1) {
if(e.style.display == 'block') {
e.style.display = 'none';
se.style.display = 'block';
} else {
e.style.display = 'block';
se.style.display = 'none';
}
} else {
e.style.display = 'none';
se.style.display = 'block';
}
}
</script>

Toggling div visibility for a graph

I'm creating a flow graph using HTML, CSS and JS.
This is the fiddle:
http://jsfiddle.net/Newtt/HRedA/3/
This is my JS:
var flag = false;
function showdiv(id) {
var div = document.getElementById(id);
div.style.display = flag ? 'none' : 'block';
flag = !flag;
}
It shows the root node of the graph and has to be clicked on to increase the levels.
As you can see there are some issues regarding the number of clicks required to show the divs that are hidden. I would like some help to fix two things:
Number of clicks required
When I open stage two and stage three, clicking on stage one should collapse all the open stages. However, with the current code, that doesnt seems to be working.
By setting the display-flag on the actual element you will avoid toggling a global state:
function showdiv(id) {
var div = document.getElementById(id);
var divFlag = div.expandedFlag == true
div.style.display = divFlag ? 'none' : 'block';
div.expandedFlag = !divFlag;
}
Or even simpler by using the elements display-state to decide if show/hide:
function showdiv(id) {
var div = document.getElementById(id);
div.style.display = (div.style.display == 'block') ? 'none' : 'block';
}
Including Part 2:
For part two. Including a structure containing children nodes for recursive hiding:
function showdiv(id) {
var div = document.getElementById(id);
var hideFlag = (div.style.display == 'block');
div.style.display = (hideFlag ? 'none' : 'block');
if(hideFlag){hideChildren(id)}
}
var children = {
'two' : ['three-one','three-two']
};
function hideChildren(parent) {
if(children[parent] !== undefined){
for(var i = 0; i<children[parent].length; i++){
var id = children[parent][i]
document.getElementById(id).style.display = 'none';
hideChildren(id)
}
}
}
For part 2 I would again use the DOM to your advantage. I agree that if you intend to generate these dynamically it's going to take some rework of the DOM structure and code.
For this example however, I created the following JSFiddle
http://jsfiddle.net/HRedA/15/
<div class="wrapper">
<div class="stage-one">
<div class="box node-one"></div>
<div class="stage-two" id="two" style='display:none'>
<div class="box node-two">
<div class="stage-three-one" id="three-one" style="display:none;">
<div class="box node-four"></div>
<div class="box node-five"></div>
</div>
</div>
<div class="box node-three">
<div class="stage-three-two" id="three-two" style="display:none;">
<div class="box node-six"></div>
</div>
</div>
</div>
</div>
</div>
So the html above is reduced so it's not cluttering the page, but you'll notice that instead of all the elements being at the same level the nodes are structured inside of one another just like the flow diagram is structured. This means that when you hide a parent it will also hide it's children. This has the side-effect/feature of also remembering what you had expanded. For example if you hide the root and then show it again all your previous expansions will remain.
The reason this won't work dynamically is that all the lines and boxes are positioned by hand. I even had to move some around when they became children of other nodes.
Hope this helps.

Categories

Resources