I am using jQuery 3.x. I am trying to append a dynamically created element before and after an element using insertBefore() and insertAfter(). However, only insertBefore() is working, and another one is ignored. When I am commenting one then other is working. why?
p = $("<p></p>").text("This is a dynamicly created element");
p.insertAfter($('nav'));
p.insertBefore($('nav'));
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 20px;
}
header,
nav,
main,
aside,
footer {
padding: 10px 15px;
border: 1px solid mediumseagreen;
text-align: center;
}
header {
background: dodgerBlue;
}
nav {
background: mediumSeaGreen;
}
main {
background: #d3d3d3;
}
main,
aside {
height: 1200px;
}
main {
width: 80%;
float: left;
}
aside {
width: 20%;
float: right;
}
div::after {
content: " ";
float: none;
clear: both;
display: table;
}
main {
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
This is header
</header>
<nav>
This is navbar
</nav>
<main>
<article>
<h2>This is heading</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Cum atque fuga, eos neque ipsum enim id inventore necessitatibus laboriosam quo nobis, repellendus maxime veritatis error ut expedita, velit aspernatur asperiores!
</p>
</article>
</main>
<aside>
This is side bar
</aside>
<div></div>
<footer>
This is footer
</footer>
The issue is because the p references only a single element. You insert it in to the DOM in the insertAfter() call, then move the same element to a new location using insertBefore().
To do what you require you can clone() the element before the second insertion. Also note that you don't need to create an entire jQuery object to select nav, you can just pass the selector as a string. Try this:
let p = $("<p />", {
text: "This is a dynamicly created element"
});
p.insertAfter('nav');
p.clone().insertBefore('nav');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 20px;
}
header,
nav,
main,
aside,
footer {
padding: 10px 15px;
border: 1px solid mediumseagreen;
text-align: center;
}
header {
background: dodgerBlue;
}
nav {
background: mediumSeaGreen;
}
main {
background: #d3d3d3;
}
main,
aside {
height: 1200px;
}
main {
width: 80%;
float: left;
}
aside {
width: 20%;
float: right;
}
div::after {
content: " ";
float: none;
clear: both;
display: table;
}
main {
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>This is header</header>
<nav>This is navbar</nav>
<main>
<article>
<h2>This is heading</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Cum atque fuga, eos neque ipsum enim id inventore necessitatibus laboriosam quo nobis, repellendus maxime veritatis error ut expedita, velit aspernatur asperiores!
</p>
</article>
</main>
<aside>This is side bar</aside>
<div></div>
<footer>This is footer</footer>
One other thing to mention, I would suggest researching flexbox layouts. They're a much more modern and extensible technique than forcing display: table on a div to create a multi-column layout.
Related
I suspect the issue is with the line const active = document.querySelector(".accordion.active"); in JS code below. It doesn't seem to be retrieving that element. Could you please help me debug it? Or should I use something else instead of querySelector? It is also found that this.classList.add("active"); is not adding the "active class" to the accordion element when it is clicked.
document.addEventListener("DOMContentLoaded", function(event) {
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
const active = document.querySelector(".accordion.active");
console.log(active);
if (active) {
active.classList.remove('active'); // remove active class from accordions
}
this.classList.add("active"); // add it to this one
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
});
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 1.5rem;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
display: none;
background-color: white;
overflow: hidden;
background: red;
}
.active+.panel {
display: block;
}
<div class="row">
<button class="accordion"><div class="question"><?php echo $label; ?></div></button>
<div class="panel">
<p class="answer">
<?php echo $answer; ?>
</p>
</br>
</div>
</div>
<div class="row">
<button class="accordion"><div class="question"><?php echo $label; ?></div></button>
<div class="panel">
<p class="answer">
<?php echo $answer; ?>
</p>
</br>
</div>
</div>
You probably should re-think your approach since in your case, you will not even be in need of JavaScript - for the basics! If you need a custom accordion, then you can use JavaScript, and I try to explain to you how.
What you need is a clean HTML with <details> and <summary>. See this example:
<details class="accordion">
<summary>Question 1</summary>
<strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus molestias ex, rem ducimus quibusdam nihil aliquam corporis id sint aperiam dolores, accusantium culpa adipisci similique doloremque eius reiciendis. Veniam, perferendis.
</details>
<details class="accordion">
<summary>Question 2</summary>
<strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum doloribus tenetur tempore esse consectetur incidunt, distinctio eaque suscipit error fugit tempora, quas accusantium recusandae autem voluptatibus qui quasi molestiae odit.
</details>
With CSS you can style it the way you want it. If you want to remove the arrows, you can try it with details > summary { list-style: none; }. Also, you can use any other characters. In this example, we use the signs + (plus) and when the accordion is already opened, it should be - (minus.):
details > summary {
list-style-type: '+ ';
}
details[open] > summary {
list-style-type: "- ";
}
details > summary::-webkit-details-marker {
display: none;
}
summary {
background-color: #ccc;
}
<details class="accordion">
<summary>Question 1</summary>
<strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus molestias ex, rem ducimus quibusdam nihil aliquam corporis id sint aperiam dolores, accusantium culpa adipisci similique doloremque eius reiciendis. Veniam, perferendis.
</details>
<details class="accordion">
<summary>Question 2</summary>
<strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum doloribus tenetur tempore esse consectetur incidunt, distinctio eaque suscipit error fugit tempora, quas accusantium recusandae autem voluptatibus qui quasi molestiae odit.
</details>
As you can see, all open questions remain open. If you want only the active question to stay open, you may use JavaScript.
document.querySelectorAll('details').forEach((accordion) => {
accordion.addEventListener('click', (e) => {
document.querySelectorAll('details').forEach((event) => {
if (accordion !== event) {
event.removeAttribute('open');
}
});
});
});
details > summary {
list-style-type: '+ ';
}
details[open] > summary {
list-style-type: "- ";
}
details > summary::-webkit-details-marker {
display: none;
}
summary {
background-color: #ccc;
}
<details class="accordion">
<summary>Question 1</summary>
<strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus molestias ex, rem ducimus quibusdam nihil aliquam corporis id sint aperiam dolores, accusantium culpa adipisci similique doloremque eius reiciendis. Veniam, perferendis.
</details>
<details class="accordion">
<summary>Question 2</summary>
<strong>Answer:</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum doloribus tenetur tempore esse consectetur incidunt, distinctio eaque suscipit error fugit tempora, quas accusantium recusandae autem voluptatibus qui quasi molestiae odit.
</details>
You don't need to manipulate the display of the active element, your CSS already does that. Also you should not both add and toggle the active class on this - that is equivalent to removing it.
I've also added an if statement to check if the clicked element is already active so that collapsing it again works.
document.addEventListener("DOMContentLoaded", function(event) {
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
const active = document.querySelector(".accordion.active");
console.log(active);
if (active) {
active.classList.remove('active'); // remove active class from accordions
}
if (active !== this) {
this.classList.toggle("active");
}
});
}
});
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 1.5rem;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
display: none;
background-color: white;
overflow: hidden;
background: red;
}
.active+.panel {
display: block;
}
<div class="row">
<button class="accordion"><div class="question">Label1</div></button>
<div class="panel">
<p class="answer">
Answer goes here
</p>
</div>
</div>
<div class="row">
<button class="accordion"><div class="question">Label2</div></button>
<div class="panel">
<p class="answer">
Second answer
</p>
</div>
</div>
//
this.classList.add("active"); is not adding the "active class" to the accordion element when it is clicked.
//
It is adding. But immediately the class is toggled, so it is removed. That toogle class line is commented.
I have added the css for green color to active accordion, which you can see after moving the cursor off the accordion element.
document.addEventListener("DOMContentLoaded", function(event) {
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
const active = document.querySelector(".accordion.active");
console.log(active);
if (active) {
active.classList.remove('active'); // remove active class from accordions
// this is if other heading is clicked
}
// this.classList.toggle("active"); // not needed
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
this.classList.remove("active"); // remove it to this one
panel.style.display = "none";
} else {
this.classList.add("active"); // add it to this one
panel.style.display = "block";
}
});
}
});
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 1.5rem;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.accordion.active{
background:green;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
display: none;
background-color: white;
overflow: hidden;
background: red;
}
.active+.panel {
display: block;
}
<div class="row">
<button class="accordion"><div class="question">Accordion Label 1</div></button>
<div class="panel">
<p class="answer">
Accordion Answer 1
</p>
</br>
</div>
</div>
<div class="row">
<button class="accordion"><div class="question">Accordion Label 2</div></button>
<div class="panel">
<p class="answer">
Accordion Answer 1
</p>
</br>
</div>
</div>
I always prefer delegation.
I am wrapping the accordion in a DIV and delegate the clicks from there
No need to show / hide the panes since the CSS .active+.panel { display: block; } does that for us
document.addEventListener("DOMContentLoaded", function(event) { // when page has loaded
const acc = document.getElementById("accodionContainer"); // the container
const buttons = acc.querySelectorAll(".accordion"); // the buttons in the container
acc.addEventListener("click", e => { // any click in the container
const currentButton = e.target.closest(".accordion"); // you have stuff inside the relevant element, make sure we use the .accordion element
if (!currentButton) return // something not a (in a ) button was clicked
// toggle clicked button, remove active from the rest
buttons.forEach(acc => acc.classList[acc === currentButton ? "toggle" : "remove"]("active"));
});
});
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 1.5rem;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
display: none;
background-color: white;
overflow: hidden;
}
.active+.panel {
display: block;
}
.accordion.active span::after {
content: "➖";
}
.accordion span::after {
content: "➕";
}
<div id="accodionContainer">
<div class="row">
<button class="accordion"><span></span> Question 1</button>
<div class="panel">
<p class="answer">
Answer 1
</p>
</div>
</div>
<div class="row">
<button class="accordion"><span></span> Question 2</button>
<div class="panel">
<p class="answer">
Answer 2
</p>
</div>
</div>
</div>
The below answer is not related to the question, but it is good to have the html hierarchy.
const accordionToggles = document.querySelectorAll('.accordion-toggle');
const accordionItems = document.querySelectorAll('.accordion-item');
const flush = true; // set to true,if only one accordion bodyto be open. set false, to open multiple accordion-body
accordionToggles.forEach((toggleBtn) => {
toggleBtn.addEventListener('click', (event) => {
const accordionItem = event.target.closest(".accordion-item");
if (!flush) {
accordionItem.classList.toggle('active');
return;
}
if (accordionItem.classList.contains('active')) {
accordionItem.classList.toggle('active');
return;
}
accordionItems.forEach((item) => {
item.classList.remove('active');
})
accordionItem.classList.add('active');
})
});
.accordion {}
.accordion-item {
border: 1px solid gray;
}
.accordion-item+.accordion-item {
border-top: none;
}
.accordion-header {}
.accordion-toggle {
width: 100%;
border: none;
height: 30px;
border-bottom: 1px solid black;
text-align: left;
position: relative;
}
.accordion-toggle:after {
position: absolute;
top: 5px;
right: 5px;
font-size: 18px;
}
.accordion-body {
padding: 10px;
display: none;
}
.accordion-item.active .accordion-body {
display: block;
}
.accordion-item .accordion-toggle:after {
content: "+"
}
.accordion-item.active .accordion-toggle:after {
content: "-"
}
<div class="accordion">
<div class="accordion-item">
<div class="accordion-header">
<button class="accordion-toggle">Title 1</button>
</div>
<div class="accordion-body">
Accordion Content 1
</div>
</div>
<div class="accordion-item">
<div class="accordion-header">
<button class="accordion-toggle">Title 2</button>
</div>
<div class="accordion-body">
Accordion Content 2
</div>
</div>
</div>
I'm basically trying to implement a popup that blurs the background and makes it so that the user can't touch the buttons and other components in the background.
You can disable the whole div by using css.
#div-id{
pointer-events: none;
}
This will disable all the elements inside the div.
If you want the blur effect you can add opacity: 0.2; to the div css.
You can add this class on your main element container to blur the whole background
.blured {
filter: blur(2px);
-webkit-filter: blur(2px);
}
And add pointer-events: none; on your modal or popup
By using css backdrop-filter property and pointer-events you can achieve what u need .. see example below
* {
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 1rem;
}
body {
margin: 0;
padding: 0;
}
.disabled{
pointer-events: none;
color : grey;
}
.modal {
position: fixed;
inset: 0;
isolation: isolate;
background-color: rgba(19, 128, 119, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.popup {
position: absolute;
z-index: 1;
width: 400px;
background-color: black;
color: white;
padding: 1rem;
}
.blur{
position: absolute;
inset: 0;
z-index: -1;
backdrop-filter: blur(0.2rem);
}
<div class="text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid dolorum illo, non incidunt earum natus neque architecto a autem maxime voluptatibus tempora minima, provident expedita quidem cumque ab. Error, tenetur?
</div>
<div class="modal">
<div class="blur"></div>
<div class="popup">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi quam architecto minima nihil aliquid quis unde
illo mollitia iure. Quia.
<br /> <br /> <br />
<a href="#" class="disabled">
I am a disabled link !
</a>
</div>
</div>
References : pointer-events and backdrop-filter.
Note: Also remember using pointer events doesn't means you can't use keyboard to focus.. To prevent keyboard tab focus you might need to use tabindex attribute on the link
body{
position:relative;
///rest of your style
}
.backdrop {//create a div inside body // <div class".backdrop"></div>
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
z-index: 1;
backdrop-filter: blur(5px);
}
.backdrop:enabled { //here we have disable all pevnts
pointer-events: none;
}
I am trying to add an on-click-smooth-scroll effect like this one: https://michalsnik.github.io/aos/
I have read this: Smooth scroll to specific div on click and I am unable to adapt it. I don't understand what scrollTop: $("#page2").offset().top does.
My issue is that the scroll is "snapping". And that's probably because I have applied scroll-snap on the containers.
Also, when you're in-between the pages and click on the scroll down arrow it will either move up or down.
I would like to get the second page on full view whenever I press on that arrow. It should Scroll Down until #page2 has height: 100vh or it occupies the whole view port.
// eliminate scroll-bar
var child = document.getElementById('child-container');
child.style.right = child.clientWidth - child.offsetWidth + "px";
//scroll down effect on scroll-down-arrow
$(".scroll-down-arrow").click(function() {
$('html,body,#child-container').animate({scrollTop: $("#page2").offset().top}, 'slow', 'linear');
});
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* *** index.html - START *** */
body, html {
height: 100%;
width: 100%;
overflow: hidden;
}
#parent-container {
height: 100%;
width: 100%;
overflow: hidden;
position: relative;
}
#child-container {
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px; /* exact value is given through JavaScript */
overflow: auto;
scroll-snap-type: both proximity;
}
header {
height: 100%;
background-color: grey;
background-attachment: fixed;
background-position: bottom center;
background-repeat: no-repeat;
background-size: cover;
text-align: center;
scroll-snap-align: center;
}
header h1 {
font-size: 32px;
font-weight: bold;
position: sticky;
top: 5%;
margin-bottom:10px;
}
header p {
position: sticky;
width: 450px;
text-align: center;
margin: auto;
margin-top: 100px;
font-size: 1.5em;
}
header .scroll-down-arrow {
position: absolute;
left: 50%;
bottom: 20px;
display: block;
text-align: center;
font-size: 20px;
z-index: 100;
text-decoration: none;
text-shadow: 0;
width: 30px;
height: 30px;
border-bottom: 2px solid #fff;
border-right: 2px solid #fff;
left: 50%;
transform: translate(-50%, 0%) rotate(45deg);
animation: fade_move_down 3s ease-in-out infinite;
cursor: pointer;
}
/*animated scroll arrow animation*/
#keyframes fade_move_down {
0% { transform:translate(0,-15px) rotate(45deg); opacity: 0; }
25% {opacity: 1;}
/* 50% { opacity: 1; } */
100% { transform:translate(0,10px) rotate(45deg); opacity: 0; }
}
.container_page_2 {
width: 100%;
height: 100vh;
scroll-snap-align: center;
overflow: hidden;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div id="parent-container">
<div id="child-container">
<!-- #header -->
<header>
<div class="nav-container">
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<h1 id="sticky-title">Lorem ipsum</h1>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Modi debitis in libero tenetur suscipit iusto eum nulla dolorum aperiam adipisci unde veritatis vel iure, a nam, saepe exercitationem illum vitae.</p>
<div class="scroll-down-arrow"></div>
</header>
<!-- #page2 -->
<div id="page2" class="container_page_2">
<div class="column active">
<div class="content">
<h1>1</h1>
<div class="box">
<h2>background-attachment: fixed;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg1"></div>
</div>
<div class="column">
<div class="content">
<h1>2</h1>
<div class="box">
<h2>background-attachment: scroll;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg2"></div>
</div>
<div class="column">
<div class="content">
<h1>3</h1>
<div class="box">
<h2>background-attachment: scroll;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg3"></div>
</div>
<div class="column">
<div class="content">
<h1>4</h1>
<div class="box">
<h2>background-attachment: fixed;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg4"></div>
</div>
</div>
https://codepen.io/bleah1/pen/gjYBgQ
I haven't added all the elements from the second page, but it doesn't matter, because the scrolling isn't affected. As you can see it's not smooth at all, it's actually pretty snappy.
What do you think ? I would like to keep the scroll-snap, because I like that idea.
Hi can you try this solution.
Basically I removed the css when click event starts then added it when the scrollTop event ends.
Remember to remove it from your css #child-container
$(".scroll-down-arrow").click(function() {
$('#child-container').css('scroll-snap-type','')
$('html,body,#child-container').animate({
scrollTop: $("#page2").offset().top}, 'slow', 'linear')
.promise()
.done(() => {$('#child-container')
.css('scroll-snap-type','both proximity')
});
});
Based on #Ekin Alcar answer I was able to fix my issue. I followed his idea of removing the scroll-snap-type css attribute from #child-container by using $('#child-container').css('scroll-snap-type',''); inside of the original script, like this:
$(".scroll-down-arrow-container").click(function() {
$('#child-container').css('scroll-snap-type','');
$('html, body, #child-container').animate({
scrollTop: $(window).height()
}, 1000)
.promise()
.done(() => {$('#child-container')
.css('scroll-snap-type','both proximity')
});
});
The trick with .css is that it can only remove attributes that are used in the style tag inside a .html file. It won't work with a .css stylesheet.
From the API's documentation:
It does not, however, remove a style that has been applied with a CSS rule in a stylesheet or < style > element.
As such scroll-snap-type: both proximity; was removed from the .css file and added in the .html file:
<div id="child-container" style="scroll-snap-type: both proximity;">
Also, to fix the effect of scrolling up or down whenever you're in between pages I've replaced scrollTop: $("#page2").offset().top with scrollTop: $(window).height(). Don't ask me why it works, but it does.
I am trying to make a slide(up/down) system for my collapse components (like bootstrap) but I can't get the height of the elements to animate(without height there is no possible way to animate the element I think so- if this is wrong, then how can I animate the element?)!
NOTE: [I want to use pure javascript]
document.getElementById('btn').addEventListener('click', function(){
this.nextElementSibling.classList.toggle('active');
})
body{
font-family: Segoe UI;
font-size: 1rem;
line-height: 1.5;
}
.collapse{
border: 1px solid #e7e7e7;
border-radius: .25rem;
overflow: hidden;
}
#btn{
padding: .75rem 1.25rem;
width: 100%;
border: none;
font-size: inherit;
background-color: #fff;
cursor: pointer;
}
#btn:focus{
outline: 0;
}
.collapse-content{
font-size: 95%;
padding: .75rem .75rem;
display: none;
}
.collapse-content.active{
display: block;
}
<div class="collapse">
<button id="btn"> Click Me </button>
<div class="collapse-content">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Mollitia ut explicabo nesciunt minima pariatur saepe eveniet officia ducimus perferendis suscipit?
</div>
</div>
Bare-bones vanilla javascript implementation that'll account for any internal height (with consistent transition speed) can be achieved with some minor changes to the markup.
<div class="collapse">
<button id="btn"> Click Me </button>
<div class="collapse-wrapper">
<div class="collapse-content">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Mollitia ut explicabo nesciunt minima pariatur saepe eveniet officia ducimus perferendis suscipit?
</div>
</div>
</div>
Note the addition of the collapse-wrapper div. This'll allow you to render the content and measure its height without actually displaying the content. Then it's just a simple case of showing/hiding the content on click:
* {
box-sizing: border-box;
}
.collapse-wrapper {
overflow: hidden;
transition: all 300ms ease-in;
}
const wrapper = document.querySelector('.collapse-wrapper')
const content = document.querySelector('.collapse-content')
const button = document.getElementById('btn')
let open = true
// Set initial height to content height, if shown by default
if (open) {
wrapper.style.height = `${content.getBoundingClientRect().height}px`
}
function toggleOpen () {
if (open) {
wrapper.style.height = '0px'
open = false
} else {
const height = content.getBoundingClientRect().height
wrapper.style.height = `${height}px`;
open = true
}
}
button.addEventListener('click', toggleOpen)
Here's a fiddle
You need to use max-height. Yes, it's nasty, since it means you need to set some arbitrary max height that may need to be adjusted later if the content grows. However, you cannot animate height in this situation because the height is not defined before the content opens.
Something like:
.collapse-content{
font-size: 95%;
padding: .75rem .75rem;
max-height: 0;
transition: max-height .3s;
}
.collapse-content.active{
max-height: 200px; //something bigger than what you need
}
function slide(){
document.getElementById("sliding").style.maxHeight = "1000px";
}
#sliding{
transition: 0.5s;
max-height: 0px;
overflow: hidden;
}
<button onclick ="slide();">Slide it</button>
<div id = "sliding">
<h1>It works</h1>
<p>Hello there</p>
</div>
This is sort of hacky because you have to set maxHeight to the largest you think your content will get.
Source: How can I transition height: 0; to height: auto; using CSS?
I have some content in my post. But I want to hide it until I click to a link in this post. I have yet to build this site, but I will say my idea.
The first Heading
The second Heading
The third Heading
The fourth Heading
/* The content following are hidden Until I clicked to a link above. /
/ Content is available wrapped in a div tag, do not loaded from another site. */
Content 1 will be show only click to "1. The first Heading"
Content 2 will be show only click to "2. The second Heading"
Content 3 will be show only click to "3. The third Heading"
Content 4 will be show only click to "4. The fourth Heading"
Can use CSS or Ajax / jQuery to create the effect?
You could do it using the following jquery code:
$(document).ready(function(){
$("#hide").click(function(){
$("p").hide();
});
$("#show").click(function(){
$("p").show();
});
});
Here is the complete demo how you can hide and show the element by click event.
I have made a pure css accordion that achieves the same functionality.Checkout the following link at codepen
HTML:
<div class="container">
<ul>
<li>What is java Programming Language?
<div class="acc-content" id="first">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nemo harum vel aliquid. Quaerat soluta sed aperiam temporibus ipsum obcaecati porro commodi error unde reprehenderit ipsa, dolore id, totam dolores, quae.
</p>
</div></li>
<li>How is javascript different from java?
<div class="acc-content" id="second">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nemo harum vel aliquid. Quaerat soluta sed aperiam temporibus ipsum obcaecati porro commodi error unde reprehenderit ipsa, dolore id, totam dolores, quae
</p>
</div></li>
<li>Other front end technologies
<div class="acc-content" id="third">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nemo harum vel aliquid. Quaerat soluta sed aperiam temporibus ipsum obcaecati porro commodi error unde reprehenderit ipsa, dolore id, totam dolores, quae
</p>
</div></li>
</ul>
</div>
CSS:
*{
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body{
padding-top: 50px;
font : 1em cursive;
background-image: url(http://www.mrwallpaper.com/wallpapers/fantasy-winter-scenery-1920x1200.jpg);
background-size: cover;
color: #fff;
overflow-x: hidden;
}
.container{
position: relative;
width: 100%;
max-width: 500px;
margin: auto;
padding: 5px;
}
ul{
margin: 0;
padding: 0;
list-style: none;
}
.acc-header{
display: block;
cursor: pointer;
padding: 10px 20px;
background-color: #000;
opacity: 0.7;
text-transform: uppercase;
text-decoration: none;
text-align: center;
color: #fff;
border-radius: 2px;
margin-bottom: 10px 0 0 10px;
}
.acc-content p{
margin: 10px;
}
.acc-content{
background-color: #222;
opacity: 0.7;
height: 0px;
overflow: hidden;
-webkit-transition: height 0.4s ease;
-moz-transition: height 0.4s ease;
-ms-transition: height 0.4s ease;
-o-transition: height 0.4s ease;
transition: height 0.4s ease;
}
.acc-content:target{
height: 170px;
}
With jQuery it can be pretty easy. By default you hide .content divs with CSS and display the corresponding one on heading click. Consider bellow example.
var $content = $('.content');
$('h2').click(function() {
$content.removeClass('show')
.filter('.content-' + $(this).data('content'))
.addClass('show');
});
.content {
display: none;
padding: 5px;
background: #EEE;
}
.content.show {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2 data-content="1">Heading #1</h2>
<h2 data-content="2">Heading #2</h2>
<h2 data-content="3">Heading #3</h2>
<div class="content content-1">Content #1</div>
<div class="content content-2">Content #2</div>
<div class="content content-3">Content #3</div>
If I understand well, I would recommend to load from ajax the content on first click and then hide it instead of deleting the toggled panel and retrieve it again from AJAX each time (so that there is no wait on each click and less requests).
So here's a way of doing it:
$('.header').click(function()
{
var clickedHeader= $(this);
if (clickedHeader.next().is('.toggle:visible'))
{
clickedHeader.next().slideDown(800);
}
else if (clickedHeader.next().is('.toggle:hidden'))
{
clickedHeader.next().slideUp(800);
}
else
{
$.get(url, data, function(data)
{
// First do some treatment if needed...
clickedHeader.after('<div class="toggle" style="display:none;">'+data+'</div>');
clickedHeader.next().slideDown(800);
});
}
});
This will work if you have HTML like this for ex.
<div class="header">First header</div>
<div class="header">Second header</div>
<div class="header">Third header</div>
<div class="header">Fourth header</div>
and after each header you would toggle a div that has class '.toggle'.
Hope it helps.