Vanilla Javascript Accordion Functionality - javascript

I need help with closing other panels once I open a click on a different panel. I have been trying to get my head around it for the past week but I'm just not able to.
I don't want to use a jquery library, I want to be able to code this code directly into my website.
I think the javascript code needed is simple in concept but hard to write for someone like me. I still don't exactly understand how javascript commands, functions, etc. work.
Any help or guidance is greatly appreciated!
Here is my code:
HTML
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="accordion.css">
</head>
<body>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
<script src="accordion.js"></script>
</body>
</html>
CSS
.accordion {
background-color: #2364aa;
color: #ffffff;
cursor: pointer;
padding: 18px;
border: none;
text-align: left;
outline: none;
font-size: 21px;
transition: 0.4s;
}
.open, .accordion:hover {
background-color: #205A99;
}
.accordion:after {
content: '\f067';
font-family: "Font Awesome 5 Free";
color: #ffffff;
float: right;
font-size: 1.25em;
line-height: 25px;
}
.open:after {
content: "\2212";
}
.panel {
max-height: 0;
overflow: hidden;
transition: 0.2s ease-out;
}
.panel-body {
padding: 18px 0;
}
#media only screen and (min-width: 960px) {
.container {
display: table;
box-sizing: border-box;
}
.row .col {
margin: 10px 0;
}
.col {
display: table-cell;
}
.col.middle {
vertical-align: middle;
}
.col-2 {
width: 50%;
padding-right: 72px;
}
.col-4 {
width: 33.33333333333333333%;
padding-right: 72px;
}
}
Javascript
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}

Event Delegation
Wrap everything in a block element.
var main = document.querySelector(`main`)
Add an EventListener to that "parent" element
main.addEventListener('click', ....
Now if main or any descendants of main are clicked, a callback function will be invoked. So we have only one EventListener listening for a click event on the behalf of each .accordion. We determine which .accordion was actually clicked by using an if condition and the event.target property.
The rule of mutual exclusivity applies to how an accordion works:
Only one .accordion + .panel combo can have the .active class.
Whenever it's time to change (in this case, e.target (the clicked element) has been clicked), all .accordions will remove the .active class (whether they actually had it or not).
After there are no element with the .active class, you then give it to e.target.
Changes
.accordion + .panel.active instead of .accordion.active.
.style.maxHeight replaced by class .active:
.panel.active {
max-height: 2000px;
height:auto;
overflow: hidden;
transition: 0.2s ease-out;
}
Demo
Details are commented in Demo
// Reference the parent of all .accordion
var main = document.querySelector('main');
/* Register main to click events...
|| when main or any of its descendant elements are clicked...
*/
main.addEventListener("click", function(e) {
/* Collect all .accordion into a NodeList and convert it into
|| an array.
*/
var acc = Array.from(document.querySelectorAll(".accordion"));
/* Loop thru each .accordion to remove the .active class
|| from each .panel
*/
for (let a = 0; a < acc.length; a++) {
var panel = acc[a].nextElementSibling;
panel.classList.remove('active');
}
/* After nothing has class .active, assign .active to the
|| .panel of the clicked element (e.target)
*/
if (e.target !== e.currentTarget) {
var tgt = e.target.nextElementSibling;
tgt.classList.add("active");
}
});
.accordion {
background-color: #2364aa;
color: #ffffff;
cursor: pointer;
padding: 18px;
border: none;
text-align: left;
outline: none;
font-size: 21px;
transition: 0.4s;
}
.open,
.accordion:hover {
background-color: #205A99;
}
.accordion:after {
content: '\f067';
font-family: "Font Awesome 5 Free";
color: #ffffff;
float: right;
font-size: 1.25em;
line-height: 25px;
}
.open:after {
content: "\2212";
}
.panel {
max-height: 0;
overflow: hidden;
transition: 0.2s ease-out;
}
.panel.active {
max-height: 2000px;
height:auto;
overflow: hidden;
transition: 0.2s ease-out;
}
#media only screen and (min-width: 960px) {
.container {
display: table;
box-sizing: border-box;
}
.row .col {
margin: 10px 0;
}
.col {
display: table-cell;
}
.col.middle {
vertical-align: middle;
}
.col-2 {
width: 50%;
padding-right: 72px;
}
.col-4 {
width: 33.33333333333333333%;
padding-right: 72px;
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<main>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
</main>
</body>
</html>

I hope this function helps you
function closeAll() {
var accs = document.querySelectorAll('.accordion');
for(var i = 0; i < accs.length; i ++) {
accs[i].classList.remove('active');
var panel = accs[i].nextElementSibling;
panel.style.maxHeight = null;
}
}
Update We can skip closing clicked element by adding this condition to closeAll function:
if (accs[i] == tar) {
continue;
}
Full code here
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function(ev) {
closeAll(ev.target);
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
function closeAll(tar) {
var accs = document.querySelectorAll('.accordion');
for (var i = 0; i < accs.length; i++) {
if (accs[i] == tar) {
continue;
}
accs[i].classList.remove('active');
var panel = accs[i].nextElementSibling;
panel.style.maxHeight = null;
}
}
.accordion {
background-color: #2364aa;
color: #ffffff;
cursor: pointer;
padding: 18px;
border: none;
text-align: left;
outline: none;
font-size: 21px;
transition: 0.4s;
}
.open,
.accordion:hover {
background-color: #205A99;
}
.accordion:after {
content: '\f067';
font-family: "Font Awesome 5 Free";
color: #ffffff;
float: right;
font-size: 1.25em;
line-height: 25px;
}
.open:after {
content: "\2212";
}
.panel {
max-height: 0;
overflow: hidden;
transition: 0.2s ease-out;
}
.panel-body {
padding: 18px 0;
}
#media only screen and (min-width: 960px) {
.container {
display: table;
box-sizing: border-box;
}
.row .col {
margin: 10px 0;
}
.col {
display: table-cell;
}
.col.middle {
vertical-align: middle;
}
.col-2 {
width: 50%;
padding-right: 72px;
}
.col-4 {
width: 33.33333333333333333%;
padding-right: 72px;
}
}
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="accordion.css">
</head>
<body>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
<h3 class="accordion">Basics of Sailing</h3>
<div class="panel">
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Before Choosing a Sailboat</h4>
<p>Before looking for a boat to buy, you need to first know what you want to do with the boat, whether that is to go on vacation, learn how to sail, competitions or recreational sailing.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Car Topping & Trailing</h4>
<p>It's a good practice to carry the boat upside-down to allow the sides to rest on the side of the roof racks and to prevent the hull from getting damaged.</p>
</div>
<div class="col col-4 middle">
<!-- <div class="space"></div> -->
<h4>Safety Equipment</h4>
<p>You have to always have to think: safety first. Sailing is a dangerous and could result in severe injuries or death if the proper precautions are not taken.</p>
</div>
</div>
<script src="accordion.js"></script>
</body>
</html>

This is a class called Accordion in Vanilla js. It mostly shows the functionality in javascript , style is not good :)
class Accordion {
constructor(AccordionData) {
// State
this.data = AccordionData;
// View Layer
this.mainContainer = document.querySelector('.container');
this.allAccordionDetailsElements = [];
this.init();
}
init() {
this.createAccordions();
this.bindEvents();
}
createAccordions() {
this.data.forEach(acc => {
let accordionHTML = this.generateHTML(acc);
this.mainContainer.appendChild(accordionHTML);
});
this.allAccordionDetailsElements = document.querySelectorAll('.accordion-details');
}
checkIfCurrentTarget(targetEl, detailsEl) {
return detailsEl.dataset.target === targetEl.id;
}
getDisplayStatus(element) {
return element.style.display;
}
toggleDetailsVisibility(detailsEl) {
const isVisible = this.getDisplayStatus(detailsEl) === 'block';
if (!isVisible) {
detailsEl.style.display = 'block';
} else {
detailsEl.style.display = 'none';
}
}
hideAllDetails() {
this.allAccordionDetailsElements.forEach(detailsSection => {
detailsSection.style.display = 'none';
});
}
bindEvents() {
this.mainContainer.addEventListener('click', (e) => {
if (typeof e === 'undefined') return;
const targetEl = e.target;
const isAccordionHeader = targetEl.classList.contains('accordion-header');
if (isAccordionHeader) {
this.hideAllDetails();
this.allAccordionDetailsElements.forEach(detailsSection => {
if (this.checkIfCurrentTarget(targetEl, detailsSection)) {
this.toggleDetailsVisibility(detailsSection)
}
});
}
});
}
generateHTML(accordionElData) {
const { id, headerText, detailsText } = accordionElData;
const sectionEl = document.createElement('section');
sectionEl.className = 'accordion-container';
const headerEl = document.createElement('button');
headerEl.type = 'button';
headerEl.className = 'accordion-header';
headerEl.textContent = headerText;
headerEl.id = id;
const articleEl = document.createElement('article');
articleEl.className = 'accordion-details';
articleEl.textContent = detailsText;
articleEl.dataset.target = id;
sectionEl.appendChild(headerEl);
sectionEl.appendChild(articleEl);
return sectionEl;
}
}
const AccordionData = [
{
id: 'acc-1',
headerText: 'Section 1',
detailsText: 'This is dummy Text for Section 1'
},
{
id: 'acc-2',
headerText: 'Section 2',
detailsText: 'This is dummy Text for Section 2'
},
{
id: 'acc-3',
headerText: 'Section 3',
detailsText: 'This is dummy Text for Section 3'
}
];
window.addEventListener('DOMContentLoaded', () => {
const accordion = new Accordion(AccordionData);
});
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.accordion-details {
display: none;
}
<main class="container"></main>

Related

Smooth scrolling not working from all links

I have a page with three on-page links that should all smoothly animate scrolling to the anchor link, but only the first link scrolls smoothly. Incidentally, the first link is the only link "above" the anchored link. The other two links, which ignore the smooth scrolling script, are located above the link and first scroll to the top of the page before snapping to the anchored link. How can I configure it so that the links beneath the anchored link scroll up smoothly to the anchored link, without scrolling to the top first?
Here's what I have:
const links = document.querySelectorAll(".cta-btn a");
for (const link of links) {
link.addEventListener("click", clickHandler);
}
function clickHandler(e) {
e.preventDefault();
const href = this.getAttribute("href");
const offsetTop = document.querySelector(href).offsetTop;
$("input[id$='input']").focus();
$(".guitar-service-address>span.placeholder-location").hide();
scroll({
top: offsetTop,
behavior: "smooth"
});
}
$('#input').on("focus", function() {
$(".guitar-service-address>span.placeholder-location").hide();
});
$(function() {
$("span.placeholder-location + input").keyup(function() {
if ($(this).val().length) {
$(this).prev('span.placeholder-location').hide();
} else {
$(this).prev('span.placeholder-location').show();
}
});
$("span.placeholder-location").click(function() {
$(this).next().focus();
});
});
if ($(window).width() < 768) {
$(".placeholder-above").append($(".placeholder-float").text());
}
.container {
max-width: 990px;
}
.tab-placeholder {
display: none;
}
input[id="input"] {
width: 500px;
}
.guitar-service-address>span.placeholder-location {
position: absolute;
margin: 6px 8px;
color: #686e74;
cursor: auto;
font: Helvetica 15px/20px bold;
font-weight: bold;
z-index: 1;
}
.guitar-service-address>.placeholder-location>.font-alt {
color: #686e74;
font-weight: normal;
}
input {
padding: 5px;
font-size: 11pt;
color: black;
border: 1px solid #979797;
border-bottom: 4px solid #000;
}
.help-block {
font-size: 90%;
}
.test {
padding: 20px;
}
.section {
padding: 150px 20px;
}
#head {
background-color: #ddd;
padding: 10px 20px;
}
#ckav {
background-color: #d4d4d4;
}
#cta {
background-color: #fdfd4d;
}
#media (max-width: 768px) {
input[id="input"] {
width: 300px;
}
span>.placeholder-float {
display: none;
}
.tab-placeholder {
display: block;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<section class="section" id="head">
<div class="container">
<h2>CTA</h2>
<div>
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive
innovation via workplace diversity and empowerment.
</div>
<div class="cta-btn">
Check your guitar
</div>
</div>
</section>
<section class="section" id="ckav">
<div class="container test">
<div class="row">
<div class="col-sm-12">
<div class="placeholder-above"></div>
<div class="guitar-service-address">
<span class="placeholder-location"><span class="placeholder-float">Find guitar repair specialist. </span><span class="font-alt">Enter your guitar make and model...</span></span>
<input id="input" type="text" />
</div>
<p class="help-block">What is this?</p>
</div>
</div>
</div>
</section>
<section class="section" id="stuff">
<div class="container">
<h2>Stuff</h2>
<div>
Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed base benefits. Dramatically visualize customer directed convergence without revolutionary ROI. Efficiently unleash cross-media
information without cross-media value.
</div>
<div class="cta-btn">
Check your guitar
</div>
</div>
</section>
<section class="section" id="cta">
<div class="container">
<h2>CTA</h2>
<div>
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive
innovation via workplace diversity and empowerment.
</div>
<div class="cta-btn">
Check your guitar
</div>
</div>
</section>
Here's a fiddle: http://jsfiddle.net/Codewalker/6or8pwjx/
Just add this to the CSS. you don't even need Javascript/Jquery.
* {
scroll-behavior: smooth;
}
👉 Check it in action on Jsfiddle

Only show the content of a clicked header and hide others

I have a page with four headers with id of heading-1, heading-2, heading-3 and heading-4. These headers have contents in div containers with the ids of content-1, content-2, content-3 and content-4 respectively.
The four headings are shown by default but only the contents of "content-1" div is shown while the remaining three containers "content-2", "content-3", and "content-4" are hidden and not displayed.
To NOT display these contents by default, I set their "display" property to "none". To display "content-1", I added class name of "active" which set the "display" property to block.
To give the "heading-1" a green color, I added class name of "active" which set the color to green
The contents of any container "content-1, content-2, content-3 and content-4 should ONLY SHOW when their corresponding headings "heading-1", "heading-2", "heading-3" and "heading-4" is clicked. On clicking it, only the clicked header and its corresponding content container should be given the class names "active" for the color and display styles to be applied respectively.
Only one content and header can be active at a time. If a new header is clicked, the class name "active" should be removed on any header and content that has it and added to the clicked one.
I already wrote out the code for this but it is not scalable and can really get overwhelming when you have lots of headers and contents.
What is the best way/method/code to achieve this solution?
Note: The code should be in VANILLA JavaScript and NOT Jquery.
Thanks.
const headingOne = document.getElementById("heading-1");
const headingTwo = document.getElementById("heading-2");
const headingThree = document.getElementById("heading-3");
const headingFour = document.getElementById("heading-4");
const contentOne = document.getElementById("content-1");
const contentTwo = document.getElementById("content-2");
const contentThree = document.getElementById("content-3");
const contentFour = document.getElementById("content-4");
document.addEventListener("click", function(event) {
if (event.target === headingOne) {
headingOne.classList.add("active");
headingTwo.classList.remove("active");
headingThree.classList.remove("active");
headingFour.classList.remove("active");
contentOne.classList.add("active");
contentTwo.classList.remove("active");
contentThree.classList.remove("active");
contentFour.classList.remove("active");
} else if (event.target === headingTwo) {
headingTwo.classList.add("active");
headingOne.classList.remove("active");
headingThree.classList.remove("active");
headingFour.classList.remove("active");
contentTwo.classList.add("active");
contentOne.classList.remove("active");
contentThree.classList.remove("active");
contentFour.classList.remove("active");
} else if (event.target === headingThree) {
headingThree.classList.add("active");
headingOne.classList.remove("active");
headingTwo.classList.remove("active");
headingFour.classList.remove("active");
contentThree.classList.add("active");
contentOne.classList.remove("active");
contentTwo.classList.remove("active");
contentFour.classList.remove("active");
} else if (event.target === headingFour) {
headingFour.classList.add("active");
headingOne.classList.remove("active");
headingThree.classList.remove("active");
headingTwo.classList.remove("active");
contentFour.classList.add("active");
contentOne.classList.remove("active");
contentThree.classList.remove("active");
contentTwo.classList.remove("active");
}
})
.container {
display: flex;
max-width: 1000px;
margin: 0 auto;
background: whitesmoke;
border: 1px solid #ccc;
border-radius: 10px;
padding: 50px;
}
.heading {
font-size: 28px;
line-height: 140%;
cursor: pointer;
}
.heading.active {
color: green;
}
.box {
width: 50%;
}
.content {
font-size: 18px;
line-height: 25px;
display: none;
}
.content.active {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Headers/Contents</title>
</head>
<body>
<div class="container">
<div class="box box-1">
<h2 class="heading active" id="heading-1">Heading One</h2>
<h2 class="heading" id="heading-2"> Heading Two</h2>
<h2 class="heading" id="heading-3">Heading Three</h2>
<h2 class="heading" id="heading-4">Heading Four</h2>
</div>
<div class="box box-2">
<div class="content active" id="content-1">
<p>Content One</p>
</div>
<div class="content" id="content-2">
<p>Content Two</p>
</div>
<div class="content" id="content-3">
<p>Content Three</p>
</div>
<div class="content" id="content-4">
<p>Content Four</p>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Here is another approach to shorten the code , using a common class for heading and content without requiring to set an id.
let H2 = document.querySelectorAll(".box-1 h2.heading");
let AllSet = document.querySelectorAll( ".box-2 > div.content,.box-1 h2.heading ");
for (let e of H2) {
e.addEventListener("click", function() {
let linkedClass = this.classList.item(1);
toggleActive(linkedClass);
});
}
function toggleActive(linkedClass) {
for (let i = 0; i < AllSet.length; i++) {
AllSet[i].classList.remove("active");
if (AllSet[i].classList.item(1) == linkedClass) {
AllSet[i].classList.add("active");
}
}
}
.container {
display: flex;
max-width: 1000px;
margin: 0 auto;
background: whitesmoke;
border: 1px solid #ccc;
border-radius: 10px;
padding: 50px;
}
.heading {
font-size: 28px;
line-height: 140%;
cursor: pointer;
margin:0;
}
.heading.active {
color: green;
}
.box {
flex:1;
display:flex;
flex-direction:column;
}
.content:nth-child(even){background:#bee}
.content {
font-size: 18px;
line-height: 25px;
display: none;
flex:1;
background:#eeb
}
.content.active {
display: block;
}
<div class="container">
<div class="box box-1">
<h2 class="heading heading-1 active">Heading One</h2>
<h2 class="heading heading-2"> Heading Two</h2>
<h2 class="heading heading-3">Heading Three</h2>
<h2 class="heading heading-4">Heading Four</h2>
<h2 class="heading heading-5">Heading Five</h2>
</div>
<div class="box box-2">
<div class="content heading-1 active">
<p>Content One</p>
</div>
<div class="content heading-2">
<p>Content Two</p>
</div>
<div class="content heading-3">
<p>Content Three</p>
</div>
<div class="content heading-4">
<p>Content Four</p>
</div>
<div class="content heading-5">
<p>Content Five</p>
</div>
</div>
</div>
You could do something along these lines:
const headings = document.querySelectorAll(".heading");
const contents = document.querySelectorAll(".content");
document.addEventListener("click", function(event) {
// If we just clicked on a heading
if (event.target.classList.contains('heading')) {
// Extract the number from the id
const id = parseInt(event.target.id.match(/\d+/)[0], 10);
// Only add the active class on the current heading
for (let i = 0; i < headings.length; i++) {
const heading = headings[i];
heading.classList[heading === event.target ? 'add' : 'remove']('active');
}
// Only add the active class on the current content
for (let i = 0; i < contents.length; i++) {
const content = contents[i];
content.classList[content.id === `content-${id}` ? 'add' : 'remove']('active');
}
}
})
.container{display:flex;max-width:1000px;margin:0 auto;background:#f5f5f5;border:1px solid #ccc;border-radius:10px;padding:50px}.heading{font-size:28px;line-height:140%;cursor:pointer}.heading.active{color:green}.box{width:50%}.content{font-size:18px;line-height:25px;display:none}.content.active{display:block}
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="style.css"> <title>Headers/Contents</title></head><body> <div class="container"> <div class="box box-1"> <h2 class="heading active" id="heading-1">Heading One</h2> <h2 class="heading" id="heading-2"> Heading Two</h2> <h2 class="heading" id="heading-3">Heading Three</h2> <h2 class="heading" id="heading-4">Heading Four</h2> </div><div class="box box-2"> <div class="content active" id="content-1"> <p>Content One</p></div><div class="content" id="content-2"> <p>Content Two</p></div><div class="content" id="content-3"> <p>Content Three</p></div><div class="content" id="content-4"> <p>Content Four</p></div></div></div><script src="app.js"></script></body></html>

HTML JS CSS Value not showing in Column Cards

I'm a bit new to technology.
i"m trying to build a dashboard. But when I pass the attribute id to the cards. it's not displaying the values.
Sometimes I'm only getting value for the 1st card only. do I have to add additional div? or any other ways?
how to resolve these?.
window.onload = function() {
getCovidStats();
}
function getCovidStats() {
fetch('https://coronavirus-tracker-api.herokuapp.com/v2/locations/225')
.then(function(resp) { return resp.json() })
.then(function(data) {
let population = data.location.country_population;
let update = data.location.last_updated;
let confirmedCases = data.location.latest.confirmed;
let deaths = data.location.latest.deaths;
document.getElementById('population').innerHTML = population.toLocaleString('en');
document.getElementById('update').innerHTML = update.substr(0, 10);
document.getElementById('cases').innerHTML = confirmedCases.toLocaleString('en');
document.getElementById('deaths').innerHTML = deaths.toLocaleString('en');
document.getElementById('percent').innerHTML = ((Number(deaths)/Number(confirmedCases))*100).toLocaleString("en", {minimumFractionDigits: 2, maximumFractionDigits: 2}) + "%";
})
.catch(function() {
console.log("error");
})
setTimeout(getCovidStats, 43200000) // update every 12 hours
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>* {box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
h1{
font-size: smaller;
}
/* Float four columns side by side */
.column {
float: left;
width: 25%;
padding: 0 10px;
}
/* Remove extra left and right margins, due to padding */
.row {
margin: 0 -5px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* Responsive columns */
#media screen and (max-width: 600px) {
.column {
width: 100%;
display: block;
margin-bottom: 20px;
}
}
/* Style the counter cards */
.card {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
padding: 16px;
text-align: center;
background-color: #f1f1f1;
}
</style>
<script type="text/javascript" src="app.js"></script>
</head>
<body>
<h2>Responsive Column Cards</h2>
<p>Resize the browser window to see the effect.</p>
<div class="row">
<div class="column">
<div class="card">
<h3>Card 1</h3>
<h4>Cases</h4>
<h1 id="population"></h1>
</div>
</div>
<div class="column">
<div class="card">
<h3>Card 2</h3>
<h4>Cases</h4>
<h1 id="cases"></h1>
</div>
</div>
<div class="column">
<div class="card">
<h3>Card 3</h3>
<h4>Cases</h4>
<h1 id="deaths"></h1>
</div>
</div>
<div class="column">
<div class="card">
<h3>Card 4</h3>
<h4>Cases</h4>
<h1 id="percent"></h1>
</div>
</div>
</div>
</body>
</html>
above is how I'm getting. I need to display the values in the red boxed areas.
This happend because this line
document.getElementById('update').innerHTML = update.substr(0, 10);
you don't have an element with the id update. And JS crash in that line. You need to comment that line or add a validator to check if that element exist.
window.onload = function() {
getCovidStats();
}
function getCovidStats() {
fetch('https://coronavirus-tracker-api.herokuapp.com/v2/locations/225')
.then(function(resp) { return resp.json() })
.then(function(data) {
let population = data.location.country_population;
let update = data.location.last_updated;
let confirmedCases = data.location.latest.confirmed;
let deaths = data.location.latest.deaths;
document.getElementById('population').innerHTML = population.toLocaleString('en');
document.getElementById('cases').innerHTML = confirmedCases.toLocaleString('en');
document.getElementById('deaths').innerHTML = deaths.toLocaleString('en');
document.getElementById('percent').innerHTML = ((Number(deaths)/Number(confirmedCases))*100).toLocaleString("en", {minimumFractionDigits: 2, maximumFractionDigits: 2}) + "%";
})
.catch(function() {
console.log("error");
})
setTimeout(getCovidStats, 43200000) // update every 12 hours
}
* {box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
h1{
font-size: smaller;
}
/* Float four columns side by side */
.column {
float: left;
width: 25%;
padding: 0 10px;
}
/* Remove extra left and right margins, due to padding */
.row {
margin: 0 -5px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* Responsive columns */
#media screen and (max-width: 600px) {
.column {
width: 100%;
display: block;
margin-bottom: 20px;
}
}
/* Style the counter cards */
.card {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
padding: 16px;
text-align: center;
background-color: #f1f1f1;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h2>Responsive Column Cards</h2>
<p>Resize the browser window to see the effect.</p>
<div class="row">
<div class="column">
<div class="card">
<h3>Card 1</h3>
<h4>Cases</h4>
<h1 id="population"></h1>
</div>
</div>
<div class="column">
<div class="card">
<h3>Card 2</h3>
<h4>Cases</h4>
<h1 id="cases"></h1>
</div>
</div>
<div class="column">
<div class="card">
<h3>Card 3</h3>
<h4>Cases</h4>
<h1 id="deaths"></h1>
</div>
</div>
<div class="column">
<div class="card">
<h3>Card 4</h3>
<h4>Cases</h4>
<h1 id="percent"></h1>
</div>
</div>
</div>
</body>
</html>
You are missing an HTML block. Add this to your HTML below the population data:
<div class="column">
<div class="card">
<h3>Card</h3>
<h4>Cases</h4>
<h1 id="update"></h1>
</div>
</div>
Just a quick tips, you can put your CSS styles in an external file and the access it so that your HTML file won't get messy. Create a new file (style.css) and copy all your CSS and paste into the style.css. Then add <link rel="stylesheet" type="text/css" href="style.css"> to the head of HTML.

Text box to display updated text and save on button click

I am trying to set-up this daily scheduler. I am working on the 9am slot. What I want to happen is for the person to click on the box to the right of the time, make a text input, and click the save button to save the text and display it in that time slot.
Am I able to achieve this with just HTML or will I need to implement JS to make this work?
body {
font-family: 'Open Sans', sans-serif;
font-size: 16px;
line-height: 1;
}
textarea{
background: transparent;
border: none;
resize: none;
color: #000000;
border-left: 1px solid black;
padding: 10px;
}
.jumbotron {
text-align: center;
background-color: transparent;
color: black;
border-radius: 0;
border-bottom: 10px solid black;
}
.description{
white-space: pre-wrap;
}
.time-block{
text-align: center;
border-radius: 15px;
}
.row {
white-space: pre-wrap;
height: 80px;
border-top: 1px solid white;;
}
.hour {
background-color: #ffffff;
color: #000000;
border-top: 1px dashed #000000;
}
.past {
background-color: #d3d3d3;
color: white;
}
.present {
background-color: #ff6961;
color: white;
}
.future {
background-color: #77dd77;
color: white;
}
.saveBtn {
border-left: 1px solid black;
border-top-right-radius: 15px;
border-bottom-right-radius: 15px;
background-color: #06AED5;
color: white;
}
.saveBtn i:hover {
font-size: 20px;
transition: all .3s ease-in-out;
}
.gray {
background-color: gray;
}
.red {
background-color: red;
}
.green{
background-color: green;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="libs/moment.min.js"></script>
<title>Work Day Scheduler</title>
</head>
<body>
<header class="jumbotron">
<h1 class="display-3">Work Day Scheduler</h1>
<p class="lead">A simple calendar app for scheduling your work day</p>
<p id="currentDay" class="lead"></p>
</header>
<div class="container">
<!-- Timeblocks go here -->
<!-- 9am -->
<div class="row">
<div class='col-2 hour'> 9am
</div>
<div class="col-8 description red border-bottom">
<input class="border-0 form-control textarea bg-transparent" type="text" placeholder="Default input">
</div>
<div class="col-2 saveBtn">
<button type="submit" class="btn btn-primary mb-2">Save</button>
</div>
</div>
<!-- 10am -->
<div class="row">
<div class='col-2 hour'> 10am
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
<!-- 11am -->
<div class="row">
<div class='col-2 hour'> 11am
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
<!-- 12am -->
<div class="row">
<div class='col-2 hour'> 12pm
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
<!-- 1pm -->
<div class="row">
<div class='col-2 hour'> 1pm
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
<!-- 2pm -->
<div class="row">
<div class='col-2 hour'> 2pm
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
<!-- 3pm -->
<div class="row">
<div class='col-2 hour'> 3pm
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
<!-- 4pm -->
<div class="row">
<div class='col-2 hour'> 4pm
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
<!-- 5pm -->
<div class="row">
<div class='col-2 hour'> 5pm
</div>
<div class="col-8 description red">
</div>
<div class="col-2 saveBtn">
</div>
</div>
</body>
</html>
Demo: jsFiddle DEMO
HTML
All the HTML you need
<h1>Work Day Scheduler</h1>
<h3>A simple calendar app for scheduling your work day</h3>
<div class="Scheduler"></div>
JavaScript
We'll use Window.localStorage to save the data so that the user can exit or refresh the page.
Create a 24h to AM/PM converter:
const ampm = h => (h%12||12)+(h<12?'AM':'PM');
Use Window.localStorage to read (and store) your LS Object in the Browser memory.
const LS = JSON.parse(localStorage.scheduler||'{}'); // String is now an Object
Create and store the HTML template as JS String
const template_schedule = h => `<div class="Schedule">
<div class="Schedule-time">${ampm(h)}</div>
<textarea class="Schedule-desc" data-h="${h}">${LS[h]||''}</textarea>
<div class="Schedule-save">SAVE</div>
</div>`;
and append that string into the DOM using Element.insertAdjacentHTML given a start from and end to hour:
for (let h=from; h<=to; h++) {
EL_scheduler.insertAdjacentHTML('beforeend', template_schedule(h))
}
Now the fun part.
Save on textarea blur!
The textarea can be blurred by clicking outside of the textarea - on the "SAVE" element or anywhere else. So it'll work in every case. (Show the transparent "SAVE" text by using CSS :focus and the adjacend sibling combinator +)
const save = ev => {
const h = ev.target.getAttribute('data-h'); // Get the hour
LS[h] = ev.target.value; // Update Object
localStorage.scheduler = JSON.stringify(LS); // Store into localStorage as string
};
EL_scheduler.querySelectorAll('.Schedule-desc').forEach(el => {
el.addEventListener('blur', save);
});
Live example:
Since StackOverflow sandboxes the live-snippet iframe and the localStorage will not work - head to this jsFiddle DEMO
And here's the SO-Snippet for completeness sake:
const from = 9; // use 24h format here
const to = 17; // use 24h format here
// Use window.localStorage to retrieve and store your data object as string
const LS = JSON.parse(localStorage.scheduler||'{}'); // now an Object
const EL_scheduler = document.querySelector('.Scheduler');
const ampm = h => (h%12||12)+(h<12?'AM':'PM');
const template_schedule = h => `<div class="Schedule">
<div class="Schedule-time">${ampm(h)}</div>
<textarea class="Schedule-desc" data-h="${h}">${LS[h]||''}</textarea>
<div class="Schedule-save">SAVE</div>
</div>`;
// Populate Scheduler
for (let h=from; h<=to; h++) {
EL_scheduler.insertAdjacentHTML('beforeend', template_schedule(h))
}
// Logic to save the data:
// On textarea blur Event - save the data by reading the data-h value
const save = ev => {
const h = ev.target.getAttribute('data-h'); // Get the hour
LS[h] = ev.target.value; // Update Object
localStorage.scheduler = JSON.stringify(LS); // Store into localStorage as string
};
EL_scheduler.querySelectorAll('.Schedule-desc').forEach(el => {
el.addEventListener('blur', save);
});
/*QuickReset*/ *{margin:0;box-sizing:border-box;}
body {font: 16px/1.4 sans-serif; color:#555;}
h1, h3 {text-align:center; font-weight:300;}
.Scheduler {
width: calc(100% - 40px);
max-width: 500px;
margin: 1em auto;
}
.Schedule {
border-top: 1px dashed #aaa;
display: flex;
padding: 2px 0;
}
.Schedule > *{
padding: 0.5em 0.8em;
}
.Schedule-time {
width: 70px;
text-align: right;
}
.Schedule-desc {
flex: 1;
font: inherit;
min-height: 70px;
resize: vertical;
background: #eee;
border: none;
border-right: 1px solid #555;
}
.Schedule-desc:focus {
outline: none;
background: #cbe8ef;
}
.Schedule-desc:focus+.Schedule-save{
color: #fff; /* Show the SAVE text on textarea :focus */
}
.Schedule-save {
color: transparent;
background: #06AED5;
border-radius: 0 1em 1em 0;
display: flex;
align-items: center;
user-select: none;
}
<h1>Work Day Scheduler</h1>
<h3>A simple calendar app for scheduling your work day</h3>
<div class="Scheduler"></div>

HTML Journal with Toggling Journal Entries - Jquery not working

I am writing a civil war journal for my 8th grade Social Studies class and I am presenting it as an HTML file that had the title of each journal and the date. When I click in each entry, it is supposed to open the actual journal entry and close any other open ones. I am having a problem with making the journal entries open up once you click on the journal. Here's my code. (P.S. All of the journal entries aren't finished yet.)
EDIT: Code is working in JSFiddle, but not in the browser.
HTML
<html>
<head>
<link href="http://s3.amazonaws.com/codecademy-content/courses/ltp2/css/bootstrap.min.css" rel="stylesheet">
<link rel='stylesheet' href='journal.css'/>
<title>Pericby Zarby Journal</title>
</head>
<body>
<div id='container'>
<div class='entry current'>
<div class='item row'>
<div class="col-xs-3">
<p class='date'>July 22, 1861</p>
</div>
<div class='col-xs-6'>
<p class='title'>The Battle of Bull Run</p>
</div>
</div>
<div class='description row'>
<div class="col-xs-3"> </div>
<div class="col-xs-6">
<p>As I step through the door of the steel mill, I overhear the conversation that my boss and one of my co-workers are having.
I hear something about a battle and the start of a civil war. I walk by and suddenly my friend Joe runs up to me and says, 'Did ya hear?'
I give him a funny look that shows how confused I am. He proceeds to tell me about the Battle of Bull Run.
He says that a small skirmish between the Union and Confederate armies happened in Virginia. He also says that the Union lost.
That last part really surprises me. The North is supposed to have a lot more men than the South and should have destroyed the South.
Hopefully the rest of the war does not go like this.</p>
</div>
<div class='col-xs-3'> </div>
</div>
</div>
<div class='entry'>
<div class='item row'>
<div class='col-xs-3'>
<p class='date'>December 15, 1862</p>
</div>
<div class='col-xs-6'>
<p class='title'>Fredericksburg</p>
</div>
</div>
<div class='description row'>
<div class='col-xs-3'> </div>
<div class='col-xs-6'>
<p>While I am drinking and talking with some of the other blacks in the 86th regiment, I spot a small man running up to Colonel Smith
and telling him that General Burnside needs reinforcements. Smith shouts for everyone to get into formation and begin marching towards
Fredericksburg. After about an hour of marching I am finally able to see a battlefield, but it does not look pretty.
A group of Union soldiers is charging towards a Confederate barricade. One by one, each soldier is getting killed.
No progress is made by the soldiers. My regiment begins to line up in formation for another charge. As Burnside begins to realize that the
charge before is proving futile he barks a command to retreat. My regiment marches in an orderly fashion back to the nearest Union camp.</p>
</div>
<div class='col-xs-3'> </div>
</div>
</div>
<div class='entry'>
<div class='item row'>
<div class='col-xs-3'>
<p class='date'>January 2, 1863</p>
</div>
<div class='col-xs-6'>
<p class='title'>Emancipation Proclamation</p>
</div>
</div>
<div class='description row'>
<div class='col-xs-3'> </div>
<div class='col-xs-6'>
<p>It’s official. Lincoln has released the Emancipation Proclamation. Slaves from states in rebellion are now free.
Southern plantations won’t let that happen though. No slaves are going to be freed, yet. The best thing about the Emancipation Proclamation
is that now slaves have something to fight for. They will begin to help win this war, in big and small ways.
Also, France and Great Britain can’t help the South anymore because they are slave-free countries.
Lincoln may have just given the Union the boost they need to win this civil war. I know it has given me hope.</p>
</div>
<div class='col-xs-3'> </div>
</div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="journal.js"></script>
</body>
</html>
CSS
body {
position: fixed;
background-image: url("http://housedivided.dickinson.edu/grandreview/wp-content/uploads/2010/02/HD_4USCinfantryDetail.preview.jpg");
background-size: cover;
}
#container {
height: 700px;
width: 600px;
border-right: 3px black solid;
border-left: 3px black solid;
border-bottom: 3px black solid;
position: fixed;
margin: 10px auto;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
.row {
margin: 0;
}
.current .item{
background-color: rgba(112,112,112,0.7);
}
.entry {
background-color: rgba(216, 209, 209, 0.7);
border-top: 3px solid black;
}
.item {
cursor: pointer;
padding-top: 7px;
padding-bottom: 18px;
}
.description {
display: none;
padding-top: 10px;
padding-bottom: 10px;
}
.item .date {
margin-left: 20px;
font-weight: bold;
font-style: italic;
}
.item .title {
font-weight: bold;
font-size: 20px;
text-align: center;
}
Javascript
var main = function() {
$('.entry').click(function() {
$('.entry').removeClass('current');
$('.description').hide();
$(this).addClass('current');
$(this).children('.description').show();
});
}
$(document).ready(main);
If you're working locally, you may need to change this line:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
to:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Categories

Resources