Toggle div based on id - javascript

Drawing a silly blank on this, but I need to be able to toggle a div based on the id that's clicked (so the english should only show english and french should only show french). So the english div should show at start, and when you click on french link, the english hides and show the french div. Both links currently toggle both divs when clicking.
function langToggle(id) {
var english = document.getElementById("english");
var frech = document.getElementById("french");
english.style.display = (
english.style.display == "none" ? "block" : "none");
french.style.display = (
french.style.display == "none" ? "block" : "none");
}
a {
background-color: #333;
color: #fff;
padding: 5px;
cursor: pointer;
}
<a onclick="langToggle(english)">English</a>
<a onclick="langToggle(french)">French</a>
<div id="english">
<p>English</p>
</div>
<div id="french" style="display:none;">
<p>French</p>
</div>

What you did was a toggle function. But what you need is just to clear all the elements, and set only the one you need.
See the modified Javascript.
function langToggle(id) {
english.style.display = "none";
french.style.display = "none";
id.style.display = "block";
}
a {
background-color: #333;
color: #fff;
padding: 5px;
cursor: pointer;
}
<a onclick="langToggle(english)">English</a>
<a onclick="langToggle(french)">French</a>
<div id="english">
<p>English</p>
</div>
<div id="french" style="display:none;">
<p>French</p>
</div>

Related

Hide/Show div and then hide all div

I am trying to make a "meet the staff" section that has hidden bios that display on click. Right now the div displays as it should, but only disappears when the original button is clicked again. I am needing some additional javascript to hide any opened divs when a different (or same) button is clicked. I don't know enough javascript to know what to try in order to make this happen. Thanks in advance!
HTML
<div id="lastname" class="toggle-div" style="display: none;">
<div><p>bio</p>
</div>
</div>
<button class="bio-button" onclick="myBiof()">Click for Bio</button>
Javascript
<script>
function myBiof() {
var y = document.getElementById("lastname");
if (y.style.display === "block") {
y.style.display = "none";
} else {
y.style.display = "block";
}
}
</script>
You will need to add some attributes to your HTML to keep track of which item is active, what item a button controls and which ones should be hidden from screen readers. aria-controls aria-expanded and aria-hidden do just that. Once a button is clicked... if it is currently open, just close it (remove active) and toggle the appropriate attributes. If it is not open, close all of them (remove active), open the one you clicked on (add active) and toggle the appropriate attributes. Here is a simple example:
const buttons = document.querySelectorAll("button");
const people = document.querySelectorAll(".person");
const handleClick = (event) => {
const clickedBtn = event.target;
if (clickedBtn.getAttribute("aria-expanded") === "true") {
let personId = clickedBtn.getAttribute("aria-controls");
let person = document.getElementById(personId);
person.classList.remove("active");
person.setAttribute("aria-hidden", "true");
clickedBtn.setAttribute("aria-expanded", "false");
} else if (clickedBtn.getAttribute("aria-expanded") === "false") {
people.forEach(person => {
person.classList.remove("active")
person.setAttribute("aria-hidden", "true");
});
buttons.forEach(button => button.setAttribute("aria-expanded", "false"));
let personId = clickedBtn.getAttribute("aria-controls");
let person = document.getElementById(personId);
person.classList.add("active");
person.setAttribute("aria-hidden", "false");
clickedBtn.setAttribute("aria-expanded", "true");
}
}
buttons.forEach(button => button.addEventListener("click", handleClick));
button {
display: block;
background: transparent;
border: none;
border-bottom: 1px solid #000;
width: 100%;
height: 2rem;
}
.person-container {
width: 400px;
margin: 0 auto;
}
.person {
display: none;
border-left: 1px solid #000;
border-right: 1px solid #000;
border-bottom: 1px solid #000;
padding: 1rem;
}
.person h2 {
margin-top: 0px;
}
.person p {
margin-bottom: 0px;
}
.active {
display: block;
}
<div class="person-container">
<button aria-controls="person-one" aria-expanded="false">Show Person One</button>
<div id="person-one" aria-hidden="true" class="person">
<h2>Name One</h2>
<p>Person One Bio</p>
</div>
<button aria-controls="person-two" aria-expanded="false">Show Person Two</button>
<div id="person-two" aria-hidden="true" class="person">
<h2>Name Two</h2>
<p>Person Two Bio</p>
</div>
<button aria-controls="person-three" aria-expanded="false">Show Person Three</button>
<div id="person-three" aria-hidden="true" class="person">
<h2>Name Three</h2>
<p>Person Three Bio</p>
</div>
</div>
/*
Function to add all the events to the buttons.
Checking if divs are hidden or not with [data-hidden] attribute.
This HMTML attributes can be named however you want but starting
with data-
Note that this code will only work if every button
is placed in the HTML after the bio div
*/
function addEventsAndListenToThem() {
const buttons = document.querySelectorAll('.bio-button')
buttons.forEach(btn => {
btn.onclick = (e) => {
const target = e.target.previousElementSibling
// If element is hided, show it changing
// attribute data-hidden value to false
target.getAttribute('data-hidden') === 'true' ?
target.setAttribute('data-hidden', 'false') :
target.setAttribute('data-hidden', 'true')
}
})
const hide_or_show_all = document.querySelector('.bio-button-all')
// Var to check wether .bio-button-all
// has been pressed or not
var showing = false
hide_or_show_all.onclick = () => {
// Get al divs with data-hidden property
const all_bios = document.querySelectorAll('div[data-hidden]')
showing === false ? (
() => {
// Show all divs
all_bios.forEach(bio => bio.setAttribute('data-hidden', 'false'))
showing = true
}
)() :
(
// Hide all divs
() => {
all_bios.forEach(bio => bio.setAttribute('data-hidden', 'true'))
showing = false
}
)()
}
}
addEventsAndListenToThem()
/*
Display none only to [data-hidden="true"] elements
*/
[data-hidden="true"] {
display: none;
}
.bio-button,
.bio-button-all {
display: block;
margin: 10px 0px;
}
<div id="lastname" class="toggle-div" data-hidden='true'>
<div>
<p>First bio</p>
</div>
</div>
<button class="bio-button">Click for first Bio</button>
<div id="lastname" class="toggle-div" data-hidden='true'>
<div>
<p>Second bio</p>
</div>
</div>
<button class="bio-button">Click for second Bio</button>
<div id="lastname" class="toggle-div" data-hidden='true'>
<div>
<p>Third bio</p>
</div>
</div>
<button class="bio-button">Click for third Bio</button>
<button class="bio-button-all">Show/Hide all</button>

Displaying show/hide content with a button and an .active css class

I am trying to create a testimonial section on a wordpress site where there is an "expand" button to show the full testimonial quote. I want the text in the button to change to "collapse" after it is clicked. I also need to add a class to the div wraper so I can implement custom css styling when the button is active. I need this pasted three times. The problem is it fails after the first testimonial.
I have this working with the code below, with it duplicated three times (for three different testimonials) and it works on a basic html document. But when I implement it in a wordpress site by pasting the code, only the first testimonial totally works. The other two do show/hide my inner div element, but they won't insert the .active class or change the text of the button to "collapse"
Both of the second testimonials give a
"Uncaught TypeError: Cannot set property 'innerHTML' of null" in the console.
So for example, here are two out of three of my testimonials I want to show. I have to change the ID's on them to avoid the javascript conflict.
function showhide() {
var content = document.getElementById('hidden-content');
var wrap = document.getElementById('testimonial-wrap');
var btn = document.getElementById('button1');
if (content.style.display === 'none') {
content.style.display = 'block';
wrap.style.background = 'grey';
btn.innerHTML = 'COLLAPSE';
wrap.classList.add('active');
} else {
content.style.display = 'none';
wrap.style.background = 'white';
btn.innerHTML = 'EXPAND';
wrap.classList.remove('active');
}
}
function showhide2() {
var content2 = document.getElementById('hidden-content2');
var wrap2 = document.getElementById('testimonial-wrap2');
var btn2 = document.getElementById('button2');
if (content2.style.display === 'none') {
content2.style.display = 'block';
wrap2.style.background = 'grey';
btn2.innerHTML = 'COLLAPSE';
wrap2.classList.add('active');
} else {
content2.style.display = 'none';
wrap2.style.background = 'white';
btn2.innerHTML = 'EXPAND';
wrap2.classList.remove('active');
}
}
<div id="testimonial-wrap" style="background-color: white;">
<div id="testimonial">
above testimonial content
<div id="hidden-content" style="display: none;">
<p>"hidden content”</p>
</div>
<button id="button1" onclick="showhide()">EXPAND</button>
</div>
</div>
<div id="testimonial-wrap2" style="background-color: white;">
<div id="testimonial">
above testimonial content
<div id="hidden-content2" style="display: none;">
<p>"hidden content.”</p>
</div>
<button id="button2" onclick="showhide2()">EXPAND</button>
</div>
</div>
I think this is what you're looking for. You can do it much easier with jQuery & a small amout of code.
I didn't use display: none as I want to add the transition to the action. (transition won't work with display: none)
$(document).ready(function() {
$(".toggle-button").on("click", function() {
$(this).closest(".testimonial-wrap").toggleClass("active");
});
});
.testimonial-wrap {
background-color: #C1C1C1;
padding: 5px;
margin-bottom: 10px;
}
.testimonial-wrap.active {
background-color: #0095FF
}
.hidden-content {
height: 0px;
visibility: hidden;
transition: all 0.5s ease-out;
}
.active .hidden-content {
height: 100px;
visibility: visible;
transition: all 0.5s ease-in;
background-color: rgba(0, 0, 0, 0.5);
}
button {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="testimonial-wrap">
<div id="testimonial">
<p>above testimonial content</p>
<div class="hidden-content">
<p>"hidden content”</p>
</div>
<button id="button1" class="toggle-button">EXPAND</button>
</div>
</div>
<div class="testimonial-wrap">
<div id="testimonial">
<p>above testimonial content</p>
<div class="hidden-content">
<p>"hidden content.”</p>
</div>
<button id="button2" class="toggle-button">EXPAND</button>
</div>
</div>

Add a div below inline-block wrapped row - Part 2

A solution suggested by #musicnothing in an older thread displays a content div below the row of inline divs, this works good when the div.wrapblock is clicked itself.
http://jsfiddle.net/SYJaj/7/
function placeAfter($block) {
$block.after($('#content'));
}
$('.wrapblock').click(function() {
$('#content').css('display','inline-block');
var top = $(this).offset().top;
var $blocks = $(this).nextAll('.wrapblock');
if ($blocks.length == 0) {
placeAfter($(this));
return false;
}
$blocks.each(function(i, j) {
if($(this).offset().top != top) {
placeAfter($(this).prev('.wrapblock'));
return false;
} else if ((i + 1) == $blocks.length) {
placeAfter($(this));
return false;
}
});
});
The issue I'm having.
I need to trigger the same effect, but by adding the click event to a link within the wrapblock itself.
My code is nearly identical.
What I have changed is the click event handle, from $('.wrapblock').click(function() to $('.more').on('click', function() I also needed to add .closest(".wrapblock") for the content div to position itself outside of the wrapblock.
$('.more').on('click', function() {
...
if ($blocks.length == 0) {
placeAfter($(this).closest(".wrapblock"));
return false;
}
Everything can be seen and tested http://jsfiddle.net/7Lt1hnaL/
Would be great if somebody could shed some light on how I can calculate which block it needs to follow with the offset method, thanks in advance.
As you can see in the latest fiddle example, the content div is not displaying below the row of divs.
I also apologise, I wanted to post on the thread in discussion but I only have a minor posting reputation which doesn't let me, thanks.
var $chosen = null;
var $allBlocks = [];
$(function(){
$allBlocks = $('.wrapblock');
})
$(window).on('resize', function() {
if ($chosen != null) {
$('#content').css('display','none');
$('body').append($('#content'));
$chosen.trigger('click');
}
});
$('.more').on('click', function() {
$chosen = $(this);
var position = $chosen.parent('.wrapblock').position();
$('#content').css('display','inline-block');
$allBlocks.filter(function(idx, ele){
return $(ele).position().top == position.top;
})
.last()
.after($('#content'));
});
.wrapblock
{
background: #963a3a;
display: inline-block;
width: 90px;
height: 90px;
color: white;
font-size: 14px;
text-align: left;
padding: 10px;
margin: 10px;
vertical-align:top;
position:relative;
}
#content
{
display:none;
vertical-align:top;
width:100%;
background: #5582c1;
font-size: 12px;
color: white;
padding: 10px;
}
.more {
position:absolute;
bottom:15px;
right:15px;
cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div class="wrapblock">1
<span class="more" data-ref="1">more</span>
</div>
<div class="wrapblock">2
<span class="more" data-ref="2">more</span>
</div>
<div class="wrapblock">3
<span class="more" data-ref="3">more</span>
</div>
<div class="wrapblock">4
<span class="more" data-ref="4">more</span>
</div>
<div class="wrapblock">5
<span class="more" data-ref="5">more</span>
</div>
<div class="wrapblock">6
<span class="more" data-ref="6">more</span>
</div>
<div class="wrapblock">7
<span class="more" data-ref="7">more</span>
</div>
<div class="wrapblock">8
<span class="more" data-ref="8">more</span>
</div>
<div class="wrapblock">9
<span class="more" data-ref="9">more</span>
</div>
<div id="content">Some Content</div>
Seems to do what you want. Basically, it just filters down the set of all blocks to the row of the block you clicked on using the assumption that they'll all have the same vertical offset (top), then takes the last one, because jQuery will keep them in document order, so that'll be the last one in the layout row.
Oh, and I updated the fiddle http://jsfiddle.net/7Lt1hnaL/1/

Rearrange HTML Focused Tab's Row on button click

I'm porting a program from XAML/C# to HTML/CSS/JavaScript.
I have tabbed sections which rearrange the focused tab's row to the bottom when clicked. This is automatically performed by the TabControl in XAML.
XAML/C#
General Focused
Video Focused
HTML/CSS/JavaScript
How can I do the same with JavaScript?
I'm using this script https://www.w3schools.com/w3css/w3css_tabulators.asp
// Display Tab Section
function OpenTab(tabName) {
var i;
var x = document.getElementsByClassName("tabSection");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
document.getElementById(tabName).style.display = "block";
}
.btnTab {
width: 33.33%;
display: block;
float: left;
background: #020f31;
color: #fff;
padding: 0.2em;
border-top: 1px solid #0080cb;
border-right: 1px solid #0080cb;
border-bottom: none;
border-left: 1px solid #0080cb;
cursor: pointer;
}
<!-- Tabs -->
<div id="sectionTabs">
<button class="btnTab" onclick="OpenTab('General')">General</button>
<button class="btnTab" onclick="OpenTab('Stream')">Stream</button>
<button class="btnTab" onclick="OpenTab('Display')">Display</button>
<button class="btnTab" onclick="OpenTab('Video')">Video</button>
<button class="btnTab" onclick="OpenTab('Audio')">Audio</button>
<button class="btnTab" onclick="OpenTab('Subtitles')">Subtitles</button>
</div>
<!-- Sections -->
<div id="General" class="tabSection">
General ...
</div>
<div id="Stream" class="tabSection" style="display:none">
Stream ...
</div>
<div id="Display" class="tabSection" style="display:none">
Display ...
</div>
<div id="Video" class="tabSection" style="display:none">
Video ...
</div>
<div id="Audio" class="tabSection" style="display:none">
Audio ...
</div>
<div id="Subtitles" class="tabSection" style="display:none">
Subtitles ...
</div>
Fiddle Link
Well if you can change markup a little bit like wrapping the first row and second row of the buttons on different div...
Also try to avoid inline javascript and use data-attributes here
Steps:
create a new array instance from iterable object y usinf Array.from
add a click event on all the buttons using addEventListener
hide all the elements containing tabSection class and also remove active class from all the buttons using for loop.
get the data-tab value from clicked button and set display:block to the respected tab and also add active class to the current button.
now for switching the .first and .second div up and down use insertBefore inside the if condition to compare the index of the clicked button
var x = document.getElementsByClassName("tabSection");
var y = document.getElementsByClassName("btnTab");;
var a = document.querySelector(".first");
var b = document.querySelector(".second")
var p = document.getElementById("sectionTabs");
Array.from(y).forEach(function(elem, index) {
elem.addEventListener("click", function() {
for (var i = 0; i < x.length; i++) {
x[i].style.display = "none";
y[i].classList.remove("active");
}
var tab = this.getAttribute("data-tab");
document.getElementById(tab).style.display = "block";
this.classList.add("active");
if (index < 3) {
p.insertBefore(b, p.childNodes[0])
} else {
p.insertBefore(a, p.childNodes[0])
}
})
})
.btnTab {
width: 33.33%;
display: block;
float: left;
background: #020f31;
color: #fff;
padding: 0.2em;
border-top: 1px solid #0080cb;
border-right: 1px solid #0080cb;
border-bottom: none;
border-left: 1px solid #0080cb;
cursor: pointer;
outline: none;
}
.btnTab.active {
background: red;
}
<!-- Tabs -->
<div id="sectionTabs">
<div class="first">
<button class="btnTab" data-tab="General">General</button>
<button class="btnTab" data-tab="Stream">Stream</button>
<button class="btnTab" data-tab="Display">Display</button>
</div>
<div class="second">
<button class="btnTab active" data-tab="Video">Video</button>
<button class="btnTab" data-tab="Audio">Audio</button>
<button class="btnTab" data-tab="Subtitles">Subtitles</button>
</div>
</div>
<!-- Sections -->
<div id="General" class="tabSection" style="display:none">
General ...
</div>
<div id="Stream" class="tabSection" style="display:none">
Stream ...
</div>
<div id="Display" class="tabSection" style="display:none">
Display ...
</div>
<div id="Video" class="tabSection">
Video ...
</div>
<div id="Audio" class="tabSection" style="display:none">
Audio ...
</div>
<div id="Subtitles" class="tabSection" style="display:none">
Subtitles ...
</div>
Reference Link:
Array.from
forEach()
element.addEventListener
element.classList
element.style
node.insertBefore
This can be achieved by adding a bit more code to OpenTab().
First of all, to check whether a button in the top row or bottom row was pressed, you may add classes row1 to the first row of buttons and row2 to the second row. Then, you can check which button was pressed in OpenTab() by passing in this as an additional parameter: onclick="OpenTab(this, 'General').
With these changes to HTML, the javascript can be changed to change the button pressed into account. In the code below, each if statement checks that the current button is in a particular row with elem.classList.contains("rowX") and that the row is the top row with parent.firstElementChild.classList.contains("rowX"). It then loops through the number of tabs in the row (amount of tabs can vary) and places them in the beginning of the #sectionTabs div.
function OpenTab(elem, tabName) { // Note that `elem` is where `this` is passed
// Your original code
var i;
var x = document.getElementsByClassName("tabSection");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
document.getElementById(tabName).style.display = "block";
// Additional code
row1_elem = document.getElementsByClassName("row1"); // initially top row
row2_elem = document.getElementsByClassName("row2"); // initially bottom row
parent = document.getElementById("sectionTabs");
// check that button in top row was clicked
if (elem.classList.contains("row1") == 1 && parent.firstElementChild.classList.contains("row1") == 1) {
// move elements from bottom row up
for (var j = 0; j < row2_elem.length; j++) {
parent.insertBefore(row2_elem[row2_elem.length-1], parent.firstChild);
}
// check that button in top row was clicked
} else if (elem.classList.contains("row2") == 1 && parent.firstElementChild.classList.contains("row2") == 1) {
// move elements from bottom row up
for (var j = 0; j < row1_elem.length; j++) {
parent.insertBefore(row1_elem[row1_elem.length-1], parent.firstChild);
}
}
}
A jsfiddle is also available:
https://jsfiddle.net/o687eLyb/
Note that you could also wrap the rows into separate divs with IDs rowX and use slightly different logic to check which row was pressed; however, this answer has the benefit that it keeps HTML structure the same.

Block elements are parsed outside their block parents

I am writing a simple little nameplate page and am using multiple paragraphs hidden and shown with javascript to get all the sections in one document.
Here is my html:
<header>
<h1><span>Scott Colby</span></h1>
<nav>
<div id="twitternav">Twitter</div>
<div id="tumblrnav">Tumblr</div>
<div id="flickrnav">Flickr</div>
<div id="facebooknav">Facebook</div>
<div id="linksnav">Links</div>
<div id="aboutnav" class="active">About Me</div>
</nav>
</header>
<div id="content">
<p id="twitter">
Placeholder text for Twitter
</p>
<p id="tumblr">
Placeholder text for Tumblr
</p>
<p id="flickr">
Placeholder text for Tumblr
</p>
<p id="facebook">
Placeholder text for Tumblr
</p>
<p id="links">
Placeholder text for Links
</p>
<p id="about" class="active">
<div id="portrait"><img src="img/portrait.jpg" width="188" height="221" alt="-----" /><br /><span class="credit">Image: © 2011 Jim Thomas</span></div>
<div>Placeholder text for About Me</div>
</p>
</div>
My CSS:
nav {
color: white;
margin: 0 5px -8px 0;
text-align: right;
z-index: 1;
}
nav div{
display: inline;
margin: 0 0 0 .9em;
padding: .25em .25em .25em .25em;
z-index: 1;
}
nav div:hover {
background: #F77D00;
}
nav div.active {
background: #FF9900;
}
#content p {
display: none;
font-size: 85%;
z-index: -1;
}
#content p.active {
display: block;
}
And my javascript:
function hideAll() {
document.getElementById('twitter').className = '';
document.getElementById('twitternav').className = '';
document.getElementById('tumblr').className = '';
document.getElementById('tumblrnav').className = '';
document.getElementById('flickr').className = '';
document.getElementById('flickrnav').className = '';
document.getElementById('facebook').className = '';
document.getElementById('facebooknav').className = '';
document.getElementById('links').className = '';
document.getElementById('linksnav').className = '';
document.getElementById('about').className = '';
document.getElementById('aboutnav').className = '';
}
function showTwitter() {
hideAll();
document.getElementById('twitter').className = 'active';
document.getElementById('twitternav').className = 'active';
}
function showTumblr() {
hideAll();
document.getElementById('tumblr').className = 'active';
document.getElementById('tumblrnav').className = 'active';
}
function showFlickr() {
hideAll();
document.getElementById('flickr').className = 'active';
document.getElementById('flickrnav').className = 'active';
}
function showFacebook() {
hideAll();
document.getElementById('facebook').className = 'active';
document.getElementById('facebooknav').className = 'active';
}
function showLinks() {
hideAll();
document.getElementById('links').className = 'active';
document.getElementById('linksnav').className = 'active';
}
function showAbout() {
hideAll();
document.getElementById('about').className = 'active';
document.getElementById('aboutnav').className = 'active';
}
Now, I know that's a lot of code to go through, but it's pretty simple stuff I think.
Here is my problem: even when the #about p is not active and has display:none (i.e. another section is active and visible), the image and the div with "Placeholder text for About" are both visible. When I investigated this in firebug, it shows something like this:
<p id="about"> </p>
<div id="portrait"><img .... /></div>
<div>Placeholder text for About</div>
Why do the two div's migrate outside their parent element? How can I make them disappear along with their parent?
The <p> element does not allow block level elements like <div> inside it. When the HTML parser sees the <div> tag, it assumes that the </p> tag has been omitted (it's optional) and that the p element is complete. Hence the DOM you see with the div elements as following siblings of the p element.
Tip: It's always a good idea to validate your HTML before posting a question on SO. Had you done so, the validator would have indicated the error to you.

Categories