My Goal: to have my div hidden on page load and show/hide the div with a button using only HTML/CSS/JavaScript.
I have set up a button in HTML and JavaScript to show/hide my div which works great when the div is visible on page load and not hidden using CSS. When I hide the div using CSS display: none; the div is hidden on page load but the button has to be clicked twice before the div becomes visible.
HTML:
<button class="btn btn-link" id="btnLink" onclick="hideLink()">Hide
Content</button> <br><br>
<div id="myLink">
<h1>Div content here</h1>
</div>
CSS:
#myLink {display: none;}
JavaScript:
function hideLink() {
var x = document.getElementById('myLink');
var b = document.getElementById('btnLink');
if (x.style.display === 'none') {
x.style.display = 'block';
b.childNodes[0].nodeValue="Hide Content";
} else {
x.style.display = 'none';
b.childNodes[0].nodeValue="Show Content";
}
}
You should check for !== 'block' rather than === 'none'
The value x.style.display is set to blank when we use none in the css as the css selector is what gets the none attribute than the element ( at lease that is what I understand ). So the check === none actually compares it will blank and return false ( x.style.display = '').
Now once we have set the value to block using JS the element's style.display property has a value which we can compare.
function hideLink() {
var x = document.getElementById('myLink');
var b = document.getElementById('btnLink');
if (x.style.display !== 'block') {
x.style.display = 'block';
b.childNodes[0].nodeValue = "Hide Content";
} else {
x.style.display = 'none';
b.childNodes[0].nodeValue = "Show Content";
}
}
#myLink {
display: none;
}
<button class="btn btn-link" id="btnLink" onclick="hideLink()">
Show Content
</button>
<br><br>
<div id="myLink">
<h1>Div content here</h1>
</div>
The first time the button is clicked, the element itself does not specifically have the style for display set. After your first if/then, then it does.
See here:
http://plnkr.co/edit/4peCJS1vhJskexqdLdKL?p=preview
var t = document.getElementById('output').innerText;
document.getElementById('output').innerText = JSON.stringify(x.outerHTML);
corrected it with another if...
function hideLink() {
var x = document.getElementById('myLink');
var b = document.getElementById('btnLink');
if (x.style.display === ''){
x.style.display = 'none';
}
if (x.style.display === 'none') {
x.style.display = 'block';
b.childNodes[0].nodeValue="Hide Sitemap Section";
} else {
x.style.display = 'none';
b.childNodes[0].nodeValue="Show Sitemap Section";
}
}
Related
My goal is to display various divs conditionally, such that:
display "x" div if it's not already
display "y" div but only if "x" div is already being displayed
display "z" div but only if "x" and "y" div are already being displayed.
With this code I can toggle a div to display and then not display:
function myFunction() {
var x = document.getElementById("myDIV");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
But then when I try do this for more than one div, it doesn’t work.
For example, this is what I tried:
function myFunction() {
var x = document.getElementById("myDIV");
var y = document.getElementById("myDIV2");
if (x.style.display === "none") {
x.style.display = "block";
} else if (y.style.display === "none") {
y.style.display = "block";
} else {
console.log("It worked!");
}
}
I made a couple CodePens showing those snippets:
This is the first snippet that works
This is the second one that doesn't
I would appreciate any help. Thank you!
So let's walk through it in the first myFunction. That will work reliably because there's only two options - "is x set to display: none? if so, do X; otherwise, do Y" will always work as expected because it is simple binary logic (all the cases are handled, it basically just toggles the style.display property). As soon as you bring more complicated logic into the equation and take multiple factors into consideration, it gets a little more slippery, and you need to formulate your approach more carefully.
The logic you basically want is, if X is hidden, show it; if y is hidden AND x is visible, show it; finally, if X is visible AND y is visible AND z is hidden, show it. You can write this in a really readable way by defining some quick isHidden(), isVisible(), and show() helper functions:
EDIT: FZs made a good point about classes not being caught by this logic. To account for that, all we need to do is add an "OR classList contains hidden" expression to the isHidden() check and voila.
// helpers
let isHidden = (el) => el.style.display == "none" || el.classList.contains('hidden'),
isVisible = (el) => !isHidden(el),
show = (el) => el.style.display = "block";
// elements
let x = document.getElementById("div-x"),
y = document.getElementById("div-y"),
z = document.getElementById("div-z");
// click handler
let handleClick = () => {
if (isHidden(x))
show(x);
else if (isHidden(y) && isVisible(x))
show(y);
else if (isHidden(z) && isVisible(y) && isVisible(x))
show(z);
else console.log("It worked!");
}
document.getElementById('btn').addEventListener('click', handleClick);
<div id="div-x" style="display:none">X</div>
<div id="div-y" style="display:none">Y</div>
<div id="div-z" style="display:none">Z</div>
<button id="btn">Click me!</button>
Because elem.style returns the inline style (style="/* styles */") of elem.
As #myDiv and #myDiv2 have only styles applied by stylesheets, x.style.display will return undefined, which isn't "none".
To solve it, I recommend toggling a .hidden class on the elements:
function myFunction() {
var x = document.getElementById("myDIV");
var y = document.getElementById("myDIV2");
if (x.classList.contains("hidden")) {
x.classList.remove("hidden");
} else if (y.classList.contains("hidden")) {
y.classList.remove("hidden");
} else {
console.log("It worked!");
}
}
.hidden{
display: none;
}
<div id="myDiv" class="hidden"></div>
<div id="myDiv2" class="hidden"></div>
Or, to show them alternately (I assume that was the goal):
function myFunction() {
var x = document.getElementById("myDIV");
var y = document.getElementById("myDIV2");
x.classList.toggle("hidden")
y.classList.toggle("hidden")
}
.hidden{
display: none;
}
<div id="myDiv"></div>
<div id="myDiv2" class="hidden"></div>
I am trying to have 3 buttons and when the user clicks on one of them it displays the corresponding table and hides the other two.
I have tried using getting element by id and then setting the display to none or block based on what the current state of the table is.
<div onclick="myFunction()" id="u362" class="ax_default">
</div>
<div onclick="myFunction2()" id="u317" class="ax_default">
</div>
<script>
function myFunction() {
var a = document.getElementById("tbl");
var b = document.getElementById("tbl2");
if (a.style.display === "none") {
a.style.display = "block";
b.style.display = "block";
} else {
a.style.display = "none";
b.style.display = "none";
}
}
function myFunction2() {
var a = document.getElementById("tbl");
var b = document.getElementById("tbl2");
if (b.style.display === "none") {
a.style.display = "block";
b.style.display = "block";
} else {
a.style.display = "none";
b.style.display = "none";
}
}
</script>
The actual results are just really weird and have no resemblance to what I am trying to do.
You have not shown all of your code and are not explaining what you are wanting to do. Right now both of your functions are acting the same way, because you are operating on both tables each time.
If I can "guess" as to what you might want, it would be that when you click on button 1, then it will show table 1 and hide table 2. If you click on button 2 then it will show table 2 and hide table 1. If you look at your code below this is not what you are doing. You are showing both tables or hiding both tables in both instances.
To fix this you can do something like this:
<div onclick="myFunction()" id="u362" class="ax_default">
</div>
<div onclick="myFunction2()" id="u317" class="ax_default">
</div>
<script>
var a = document.getElementById("tbl");
var b = document.getElementById("tbl2");
function myFunction() {
a.style.display = "block";
b.style.display = "none";
}
function myFunction2() {
a.style.display = "none";
b.style.display = "block";
}
</script>
Notice you do not need an if/else in each function unless you are wanting the buttons to toggle on and off, in which case you may only need one button.
Here is a snippet of your code cleaned up. As #kojow7 noted above they basically do the same thing just based off 1 table or the other.
Things to note:
1) use slightly more descriptive variable names in your functions
2) create a css class that does your hiding so that you can simply test for that. when possible try to keep your js from handling styling
3) i replaced your if else with ternary if's. but in reality if all you are doing is toggling the class hidden then you could replace the if with a toggle.
4) instead of adding the onclick listener in your html it should be added via your js. try not to muddy your html with js concerns.
document.getElementById('u362').addEventListener('click', function () {
const tbl1 = document.getElementById("tbl1"),
tbl2 = document.getElementById("tbl2");
tbl1.classList.contains('hidden') ?
(tbl1.classList.remove('hidden'), tbl2.classList.remove('hidden')) :
(tbl1.classList.add('hidden'), tbl2.classList.add('hidden'));
});
document.getElementById('u317').addEventListener('click', function () {
const tbl1 = document.getElementById("tbl1"),
tbl2 = document.getElementById("tbl2");
tbl2.classList.contains('hidden') ?
(tbl1.classList.remove('hidden'), tbl2.classList.remove('hidden')) :
(tbl1.classList.add('hidden'), tbl2.classList.add('hidden'));
});
.hidden {
display: none;
}
<div id="u362" class="ax_default">u362</div>
<span id="tbl1" class="hidden">Table 1</span>
<span id="tbl2" class="hidden">Table 2</span>
<div id="u317" class="ax_default">u317</div>
With the information provided in your question, I suppose that your error comes when hiding or showing your tables. I think you have to change your code at those lines:
if (a.style.display === "none") {
a.style.display = "block";
b.style.display = "block";
} else {
a.style.display = "none";
b.style.display = "none";
}
for something like this:
b.style.display = "none";
if (a.style.display === "none") {
a.style.display = "block";
}
The new code will always hide table b when hitting button a. It doesn't matter the display status of table a because it doesn't really matter for hiding the rest of the tables. And only changes the display status of table a in case of being hidden.
You should do these changes also for each function of each button. I've also recommended to refactor all functions into a unique function and using it for all of the buttons you need.
I assume that the tables are invisible only after clicking the button, the appropriate table appears.
For future reference, please describe exactly what you want to use and you can use JsFiddle to show your code. Sometimes it is difficult to deduce from the descriptions what the writer had in mind :)
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('click', e => {
showTable(e.target.dataset.table);
});
});
function showTable(idTable) {
const tables = document.querySelectorAll('table');
tables.forEach(table => {
table.id === idTable ? table.style.display = 'block' : table.style.display = 'none';
});
}
table {
display: none;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<button id="one" data-table="tb1">One</button>
<button id="two" data-table="tb2">Two</button>
<button id="tree" data-table="tb3">three</button>
<table id="tb1">
<tr>
<td>One</td>
</tr>
</table>
<table id="tb2">
<tr>
<td>Two</td>
</tr>
</table>
<table id="tb3">
<tr>
<td>Three</td>
</tr>
</table>
I had this code, that works ok to show/hide a second element by clicking the first one:
<script>
var acc = document.getElementsByClassName("movs-header");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("active");
var x = this.nextElementSibling;
if (x.style.display === "block") {
x.style.display = "none";
} else {
x.style.display = "block";
}
}
}
</script>
And this is the structure (they are repeating elements in a php system):
<div id="frm_container_[id]" class="movs-box">
<div class="movs-header">
some content here, clickable to show-hide the next sibling div
</div>
<div class="movs-body">
this content will show and hide
</div>
</div>
Now, I need to add this link inside a div with a class="movs-editlink", which has to be outside the movs-box div to refer the id "frm_container", in order to work.
Then the structure will be:
<div id="frm_container_[id]" class="movs-box">
<div class="movs-header">
some content here, clickable to show-hide the next sibling div
</div>
<div class="movs-body">
this content will show and hide
</div>
</div>
<div class="movs-editlink">[editlink label="edit" prefix="frm_container_"]</div> <!-- this div to show and hide along -->
(please don't mind the shortcode, it works fine)
What I need is to show/hide the last div with the same javascript code (when I click the "movs-header" div, but I fail to refer to "this.className", my guess was:
<script>
var acc = document.getElementsByClassName("movs-header");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("active");
var x = this.nextElementSibling;
var xedit = this.getElementsByClassName("movs-editlink").classname;
if (x.style.display === "block") {
x.style.display = "none";
xedit.style.display = "none";
} else {
x.style.display = "block";
xedit.style.display = "block";
}
}
}
</script>
I believe this is not working because the last div is outside the scope of "this", then I think I need to find the NEXT div in the structure with the class "movs-link" to be included in the display toggle, am I right? But I can't find how. Please help.
Based on your markup, rather than getElementsByClassName from this, do it with this.parentNode.nextElementSibling
var xedit = this.parentNode.nextElementSibling;
Or with jquery's nextUntil
var xedit = $(this).parent().nextUntil( "movs-editlink" );
Javascript newbie here. This is basically what I'm working with.
The function below is intended to hide everything enclosed in the newsDisplay class, but nothing happens when clicking the button that calls it.
function showHide() {
var x = document.getElementsByClassName('newsDisplay');
if (x.style.display === 'none') {
x.style.display = 'block';
} else {
x.style.display = 'none';
}
}
.newsDisplay {
display: block;
}
<h1>News<button onclick="showHide();"><img src="..\Images\showHide.png"></button></h1>
<div class="newsDisplay">
<div class="bodyBox">
<h2>Diablo 3</h2>
TEXT/PARAGRAPHS
</div>
</div>
Manually changing display: block; to display: none; behaves exactly as expected, so either the syntax or logic of the function is incorrect, or something is preventing the function from executing when clicking the button.
Could really use some help, thank you!
Try the first element of this class as follows
var x = document.getElementsByClassName('newsDisplay')[0];
Document document.single getElementsByClassName() will return an array of elements with the same class. It is different from document.getElementById() in so far as the latter returns a DOM object rather than an array of DOM objects.
As stated from it's name getElementsByClassName get elements (and not single element), so result of this function call is a collection of elements.
To access to first element of collection you can:
x = document.getElementsByClassName('newsDisplay')[0];
// or
X = document.getElementsByClassName('newsDisplay').item(0);
You can try a loop if you want multiple of the page. The key is you're grabbing a list of elements with the class, since classes aren't unique identifiers. You can either use my code below or switch it to an id and grab it by document.getElementById. Notice the s in document.getElementsByClassName
var x = document.getElementsByClassName('newsDisplay');
function showHide() {
for (var i = 0; i < x.length; i++){
x[i].style.display === 'none'
? x[i].style.display = 'block'
: x[i].style.display = 'none';
}
}
<h1>News<button onclick="showHide();"><img src="..\Images\showHide.png"></button></h1>
<div class="newsDisplay">
<div class="bodyBox">
<h2>Diablo 3</h2>
TEXT/PARAGRAPHS
</div>
</div>
<h1>News<button onclick="showHide();"><img src="..\Images\showHide.png"></button></h1>
<div class="newsDisplay">
<div class="bodyBox">
<h2>Diablo 3</h2>
TEXT/PARAGRAPHS
</div>
</div>
Try the code below.
Use this
var x = document.getElementsByClassName('newsDisplay')[0];
function showHide() {
var x = document.getElementsByClassName('newsDisplay')[0];
if (x.style.display === 'none') {
x.style.display = 'block';
} else {
x.style.display = 'none';
}
}
.newsDisplay {
display: block;
}
<h1>News<button onclick="showHide();"><img src="..\Images\showHide.png"></button></h1>
<div class="newsDisplay">
<div class="bodyBox">
<h2>Diablo 3</h2>
TEXT/PARAGRAPHS
</div>
</div>
If you hit F12, navigate to the sources tab (on google chrome) you can set a breakpoint on your javascript function, your code is running, but you are getting a
Uncaught TypeError: Cannot read property 'display' of undefined
The reason for this is getElementsByClassName is returning a list of elements with that class selector, not just the one. If you want to just do this to the first element you can simply do:
function showHide() {
var x = document.getElementsByClassName('newsDisplay')[0];
if (x.style.display === 'none') {
x.style.display = 'block';
} else {
x.style.display = 'none';
}
}
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>