Edit: Added JS below, per user comments. Here is a great example of what I am trying to accomplish:
On a single blog page, I would like to have two buttons, “Audio” (div id=“audio”) and “Video” (div id=“video”).
By default Video would be enabled and display a video player within a div. If the user clicks Audio the browser changes the end of the URL to #audio which triggers a div style change. That would cause the Video player to be hidden and the Audio player to become visible. Likewise, if the user clicks Video again the end of the URL changes to #video which triggers a div style change and the Audio player to become hidden and the Video player to become visible.
Quite a bit of research has led me to being able to toggle a single div on and off, but I cannot figure out how to accomplish what I laid out above.
Here are my div statements.
When URL ends in #video
<div id="video" style="position: static; visibility: visible; overflow: visible; display: block;">MY VIDEO PLAYER</div>
<div id="audio" style="position: absolute; visibility: hidden; overflow: hidden; display: block;">MY AUDIO PLAYER</div>
When URL ends in #audio
<div id="video" style="position: absolute; visibility: hidden; overflow: hidden; display: block;">MY VIDEO PLAYER</div>
<div id="audio" style="position: static; visibility: visible; overflow: visible; display: block;">MY AUDIO PLAYER</div>
I am not even going to bother posting my javascript for this. It only works to toggle a single div on and off. I should mention, I am not a coder, but have come along ways on my journey and this site has always been a goto for me. First time question.
Thanks so much in advance for any assistance this outstanding site can provide.
Edit: Here is the js I used which only allows the toggle of a single div
<script>
function myFunction() {
var x = document.getElementById("video");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
</script>
My first thought, though I’m unable to check currently, would be to use the following stylesheet rules:
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrapper {
width: 80vw;
margin: 1em auto;
border: 1px solid #000;
}
#audio,
#video {
/* hide both elements by default: */
visibility: hidden;
}
#audio:target,
#video:target {
/* show the element whose id is the
document's hash (‘at the end of
the URL’) */
visibility: visible;
}
<nav>
<ul>
<li>Video</li>
<li>Audio</li>
</ul>
</nav>
<div class="wrapper">
<div id="video">MY VIDEO PLAYER</div>
<div id="audio">MY AUDIO PLAYER</div>
</div>
JS Fiddle demo.
This does, of course, rely on CSS rather than JavaScript, though; so this may not be the answer you want.
Following the comment left by the OP:
Unfortunately, I need video to be visible by default. Then if the user clicks audio, video would become hidden.
The following code works:
/* aesthetics, irrelevant to the actual demo */
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrapper {
border: 1px solid #000;
width: 80vw;
margin: 1em auto;
}
.wrapper>div {
flex: 1 0 auto;
height: 5em;
}
/* using 'display: flex' to allow the use of the
'order' property on the chilren of this element: */
.wrapper {
display: flex;
}
/* hiding the child elements of the wrapper by default: */
.wrapper>div {
visibility: hidden;
}
/* selecting the child of the .wrapper element with
the class of 'defaultOnLoad', placing it first
in the visual order of its parent with the
'order: -1' property; and making it visible on
page-load: */
.wrapper>.defaultOnLoad {
order: -1;
visibility: visible;
}
/* selecting the '.defaultOnLoad' element that
is a general (later) sibling of an element
that is the ':target' (whose id appears following
the '#' in the URL), both of which are children of
'.wrapper' and hiding that element: */
.wrapper> :target~.defaultOnLoad {
visibility: hidden;
}
/* finding the child of the '.wrapper' element that
is the ':target' of the document and making it
visible: */
.wrapper>:target {
visibility: visible;
}
<nav>
<ul>
<li>Video</li>
<li>Audio</li>
</ul>
</nav>
<div class="wrapper">
<!-- in the CSS we'll be selecting/styling the '.defaultOnLoad' element based
on the state of another sibling being the ':target'; as CSS cannot select
previous-siblings I've moved the '#video' to be the last child of the
'.wrapper', but used CSS to maintain the visual order: -->
<div id="audio">MY AUDIO PLAYER</div>
<div id="video" class="defaultOnLoad">MY VIDEO PLAYER</div>
</div>
JS Fiddle demo.
As a demonstration for why I would recommend CSS over JavaScript, when CSS is able to perform the task, see the following, in which multiple other media options are added and no CSS changes are required:
/* aesthetics, irrelevant to the actual demo */
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrapper {
border: 1px solid #000;
width: 80vw;
margin: 1em auto;
}
.wrapper>div {
flex: 1 0 auto;
height: 5em;
}
/* using 'display: flex' to allow the use of the
'order' property on the chilren of this element: */
.wrapper {
display: flex;
}
/* hiding the child elements of the wrapper by default: */
.wrapper>div {
visibility: hidden;
}
/* selecting the child of the .wrapper element with
the class of 'defaultOnLoad', placing it first
in the visual order of its parent with the
'order: -1' property; and making it visible on
page-load: */
.wrapper>.defaultOnLoad {
order: -1;
visibility: visible;
}
/* selecting the '.defaultOnLoad' element that
is a general (later) sibling of an element
that is the ':target' (whose id appears following
the '#' in the URL), both of which are children of
'.wrapper' and hiding that element: */
.wrapper> :target~.defaultOnLoad {
visibility: hidden;
}
/* finding the child of the '.wrapper' element that
is the ':target' of the document and making it
visible: */
.wrapper>:target {
visibility: visible;
}
<nav>
<ul>
<li>Video</li>
<li>Audio</li>
<li>Print</li>
<li>Braille</li>
</ul>
</nav>
<div class="wrapper">
<!-- in the CSS we'll be selecting/styling the '.defaultOnLoad' element based
on the state of another sibling being the ':target'; as CSS cannot select
previous-siblings I've moved the '#video' to be the last child of the
'.wrapper', but used CSS to maintain the visual order: -->
<div id="audio">MY AUDIO PLAYER</div>
<div id="print">The 'print' option</div>
<div id="braille">The 'braille' option</div>
<div id="video" class="defaultOnLoad">MY VIDEO PLAYER</div>
</div>
JS Fiddle demo.
With regards to the comment, below, from the OP:
One issue I am running into is the ID obviously causes the browser to jump. Is there some syntax I am not aware of that would keep the browser in place? …I need each of those DIV elements (audio/video) to replace one another without jumping or moving one below the other when a user clicks either audio or video.
There doesn't seem to be a CSS means of preventing the page scrolling to the position of the targeted elements – the #video and #audio – this may prevent the use of <a> elements, unfortunately; there is an alternative, using <label> and <input> elements, but this has the complication of adding extra HTML elements for the functionality:
/* aesthetics, irrelevant to the actual demo */
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* giving an arbitrarily large margin-bottom to
demonstrate that there is no default scrolling
on clicking the <label>: */
nav {
margin-bottom: 800px;
}
/* a <label> element doesn't have default styling to imply
its interactive nature, so here we style the cursor to
depict that it can be clicked: */
nav label {
cursor: pointer;
}
.wrapper {
border: 1px solid #000;
width: 80vw;
margin: 1em auto;
}
/* hiding the <div> children contained within the .wrapper
element: */
.wrapper>div {
height: 5em;
visibility: hidden;
}
/* selecting the <div> elements that are the immediate sibling
of an <input> whose 'type' attribute is equal to 'radio' and
which matches the ':checked' pseudo-class, and setting their
visibility to 'visible': */
.wrapper input[type=radio]:checked + div {
visibility: visible;
}
/* hiding the <input> elements: */
.wrapper input[type=radio] {
display: none;
}
<nav>
<ul>
<!-- using <label> elements instead of <a>; using the 'for'
(HTMLLabelElement.HTMLFor property) to associate the
<label> with the relevant <input> (the 'for' attribute
must be equal to the 'id' attribute/property of the
<input>: -->
<li><label for="video">Video</label></li>
<li><label for="audio">Audio</label></li>
</ul>
</nav>
<div class="wrapper">
<!-- we place the <input> as the previous sibling of the relevant
<div> element (although this is a convenience in order to
simplify the selector): -->
<input type="radio" name="mediaChoice" id="video" checked="checked" />
<div id="video">MY VIDEO PLAYER</div>
<input type="radio" name="mediaChoice" id="audio" />
<div id="audio">MY AUDIO PLAYER</div>
</div>
JS Fiddle demo.
A further revision of the above, this time to use JavaScript:
let nav = document.querySelector('nav'),
mediaContainer = document.querySelector('div.wrapper'),
// because one option needs to be shown on page-load, and
// the user's ability to choose the media is determined
// via the click event, here we have to create a click
// event (a new MouseEvent), which can bubble through
// the DOM to be detected by an ancestor:
clickEvent = new MouseEvent('click', {
'bubbles': true
});
// named function to handle events; the EventObject
// ('event') is passed automagically from the
// EventTarget.addEventListener() method:
function mediaToggle(event) {
// preventing the default behaviour of the
// HTMLAnchorElement (which prevents the link
// being 'followed' and prevents page-jumping):
event.preventDefault();
// here we retrieve the hash (the '#identifier'
// fragment) of the clicked (event.target) <a>
// element:
let selector = event.target.hash;
// here we retrieve the NodeList of all '.media'
// elements in the document; and use
// NodeList.forEach() to iterate over that collection:
document.querySelectorAll('.media').forEach(
// we're using an Arrow function here; 'elem' is a
// reference to the current element-node of the NodeList
// over which we're iterating:
(elem) => {
// here we perform this function for all nodes;
// using the Element.classList API to toggle the
// 'active' class; the switch which follows determines
// whether the class-name is added, retained, removed or
// or left off. The 'switch' is a condition which evaluates
// to a true/false (or truthy/falsey) result:
elem.classList.toggle('active',
// here we use Element.matches(CSSSelector) to test whether
// the current element-node matches the supplied CSS selector;
// if it does the class-name is added (or retained), if not
// the class-name is removed (or not-added):
elem.matches(selector));
});
}
// adding the mediaToggle() function - note the deliberate lack of
// parentheses - as the event-handler for the 'click' event:
nav.addEventListener('click', mediaToggle);
// using Element.querySelector() to find the first/only element
// that matches the supplied CSS selector
nav.querySelector('.defaultOnLoad')
// firing the created MouseEvent on that element via
// the EventTarget.dispatchEvent() method:
.dispatchEvent(clickEvent);
*,
::before,
::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrapper {
border: 1px solid #000;
width: 80vw;
margin: 1em auto;
}
.media {
visibility: hidden;
}
.media.active {
visibility: visible;
}
<nav>
<ul>
<!-- using the 'defaultOnLoad' class-name to identify which media
should be the default on page-load: -->
<li>Video</li>
<li>Audio</li>
<li>Print</li>
<li>Braille</li>
</ul>
</nav>
<div class="wrapper">
<div id="video" class="media">Video</div>
<div id="audio" class="media">Audio</div>
<div id="print" class="media">Print</div>
<div id="braille" class="media">Braille</div>
</div>
JS Fiddle demo.
References:
CSS:
Adjacent-sibling (+) combinator.
:checked.
:target.
JavaScript:
Arrow functions.
document.querySelector().
document.querySelectorAll().
Element.classList.
Element.matches().
Event.preventDefault().
EventTarget.addEventListener().
EventTarget.dispatchEvent().
MouseEvent() constructor.
NodeList.prototype.forEach().
Here is a plain js way to do it. I am making a few assumptions here (about your markup).
function doMagic() {
let urlType = window.location.href,
audio = document.getElementById('audio'),
video = document.getElementById('video');
if (urlType.split('#')[1] === 'audio'{ //turn it into an array of two, split at the hash
audio.style.display = 'block';
video.style.display = 'none';
}
if (urlType.split('#')[1] === 'video'{
audio.style.display = 'none';
video.style.display = 'block';
}
}
// assuming you have a button element for this
let videoBtn = document.getElementById('vidBtn'),
audBtn = document.getElementById('audBtn');
vidBtn.addEventListener('click', function() { doMagic(); });
audBtn.addEventListener('click', function() { doMagic(); });
Both event listeners calls the same function. If your buttons have the same class then you can just simplify this to one event listener.
// assuming your buttons have the class name "myBtn"
let theBtn = document.getElementByClass('myBtn');
theBtn.addEventListener('click', function() { doMagic(); });
The answer that I have found was not done with plain javascript but with a framework called jquery. You could learn more about jquery from their website, or from w3schools.
Below is the code.
$("#audio").click(function(){
$("#audio").toggleClass("active");
$("#video").toggleClass("active");
});
$("#video").click(function(){
$("#video").toggleClass("active");
$("#audio").toggleClass("active");
});
.active{
background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<head>
</head>
<body>
<div id="video" class = "active" style="position: static; visibility: visible; overflow: visible; display: block;">MY VIDEO PLAYER</div>
<br>
<div id="audio" style="position: static; overflow: hidden; display: block;">MY AUDIO PLAYER</div>
First, we check when the element with id audio is clicked then we toggle between the class of active and inactive for the video id and audio id.
Similarly, when we check for the id video to be clicked we toggle the active class between the video id and audio id.
I hope this helped :)
Related
I want to make a window that expands when clicked and closes when clicked again. I am using flask to display all lines of data but that should not be a problem and can actually be ignored. Right now I have it set that when you click the div it expands but once you let go the div closes again. Is there any way I can turn this div into a toggle of some kind many using python or javascript or even CSS?
HTML/Python flask:
<div class="container">
{%for i, value in verb_data.items() %}
<div class="indevidual_verbs">{{ i }} . {{ value }}</div><br>
{%endfor%}
</div>
CSS:
.indevidual_verbs {
cursor: pointer;
}
.indevidual_verbs:active {
padding-bottom: 300px;
}
Depending on what you want to do, you could even use the details html element, that automatically implements that functionality.
If you can use javascript, there is a way to easily toggle a class:
// Get a reference to the container
const container = document.getElementById("container");
// When we click on the container...
container.addEventListener("click", function (e) {
// we can toggle the "open" class
this.classList.toggle("open");
});
/* Just a way to show the container */
#container {
padding: 20px;
border: solid 1px #000;
}
/* Hide the content (you can do it in many different ways) */
#container .inner {
display: none;
}
/* Show the content when the "open" class is added to the container */
#container.open .inner {
display: block;
}
<div id="container">
<div class="inner">
This is just some example text
</div>
</div>
How can one keep just some child elements clearly visible, when the parent is opaque, without affecting the rendering performances of the page?
I am trying to make specific elements under an overlay to react and to be rendered as if they were above the overlay.
is there any way in CSS to influence the rendering of specific underlaying elements when blending with the overlay? Or is the mix-blend-mode working just top-down direction? (e.g. I'd like to tell just .visible to blend with the overlay differently; while all the rest underneath should have the normal blending)
is filter working just on the selected element? Doesn't it affects the rendering of underlaying elements, when the selected element has opacity?
(e.g. Make the overlay make everything below as greyscale)
And, since I assume the answers of both questions above won't help me, the actual question is:
Is it possible to visually compensate, on the .visible elements, the opacify effect
Note:
I know that one solution could be to use a selector:
div.data :not(.visible) { opacify: .5; }
rather than using an overlay, but I have doubts how performances would be (especially on not powerful mobiles) trying to apply opacify to the thousands of .data-subelements that would be :not(.visible). And also the click|hover on the opacified/disabled elements wouldn't be prevented (while I want just click on the area of the .visible to be caught).
Any performant solution? Or what I am asking is impossible nowadays?
I tried to write an example snippet but I couldn't find a way to solve my problem.
Javascript is also accepted
The priority is to have that visual/functional effect with decent performances on a generic mobile, when the number of li is above 100 and each of them contains 100 DOM elements.
/* structure */
div.data { display: table; }
ul { display: table-row-group; }
li { display: table-row; }
li > div { display: table-cell; }
/* style */
li > div.content { color: red; }
.visible { color: green; border: 1px dashed black; padding: 0 5px; }
/* Overlay */
.data:after {
content: '';
position: absolute;
top: 0; bottom: 0; right: 0; left: 0;
}
/* reduce emphasis */
.data:after {
background-color: white;
opacity: .5;
}
/* visual "disabled" effect */
.data:after {
filter: grayscale(100%);
}
<div class="data">
<ul>
<li>
<div class="content">Foobar1</div>
<div>
<span class="content">other content</span>
<span class="visible">
Not opaque
<input type="checkbox" checked />
</span>
</div>
</li>
<li>
<div class="content">Foobar2</div>
<div>
<span class="content">other content</span>
<span class="visible">
Not opaque
<input type="checkbox" />
</span>
</div>
</li>
</ul>
</div>
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have two div . div-1 and div-2 .it need to stay all the time but i want some menu items using another div name div-3 which is visible when button clicks. i want div-3 over div-1 and div-2 at the same time .how can i do that ?
JavaScript Notes
An IIFE is a very common design pattern in JavaScript commonly used to hide your code so that it doesn't get changed by other scripts.
You can add event listeners to elements using the addEventListner function.
You can get an HTMLElement using the querySelector function which accepts a selector. So to get the first div you could use document.querySelector("div"). To get the element with id "item" you can use document.querySelector("#item"). To get the first element element with class "active" you can use document.querySelector(".active"). You can also use document.querySelector("div#item.active") to get the item the fills all the previous requirements or document.querySelector("div, #item, .active") to get the item that fills any of the requirements. As you can see this works the same as CSS selectors.
To check if a variable is an instance of a Class you can use instanceof.
There are several ways to change how an Element looks using JavaScript.
One is to change the class name element.className = "active", another is to directly change the styling element.style = "opacity: 1;".
Styling Notes
To position an element on top of other elements you need to set its position to absolute and the container's element to relative. When you absolutely position an element then its positioned relatively to the last container that is positioned relatively (default is <html> element).
There are 2 main ways to position elements next to each other: float: left; and display: inline-block;. This works like writing on a notepad, it fits as many elements on next to each other and the repeats the same process below them.
There are 3 common ways to hide an element. 1: opacity: 0; which just makes the item invisible but it it's still there so you should probably also use pointer-events: none; so that it doesn't stop you from clicking what's behind it. 2: height: 0; This just shrinks the element so that it has no height which essentially makes it invisible. 3: display: block; this essentially completely removes the element.
Example
// IIFE Design Pattern
(function() {
// Run onLoad function if page is fully loaded
if (document.readystate === "complete") onLoad();
// Else add an event listener to call onLoad function when page gets fully loaded
else document.addEventListener("DOMContentLoaded", onLoad);
var divIsActive = false;
/**
* Function to be called when page is fully rendered
* #returns {void}
*/
function onLoad() {
// Find button
var button = document.querySelector("#toggle");
// Check if the button was found
if (button instanceof HTMLElement) {
// Add an click event listener
button.addEventListener("click", toggle);
}
}
/**
* Toggles the div to open/close
* #returns {void}
*/
function toggle() {
// Find Div
var div = document.querySelector("#div-3");
// Check if the div was found
if (div instanceof HTMLElement) {
// swap the boolean value
divIsActive = !divIsActive;
// change the classname based on the boolean value
div.className = divIsActive ? "active" : "";
}
}
})();
html,
body {
height: 100%;
width: 100%;
margin: 0;
}
#container {
position: relative;
height: 100%;
width: 100%;
}
#div-1, #div-2 {
height: 100%;
float: left;
width: 50%;
}
#div-1 {
background-color: brown;
}
#div-2 {
background-color: pink;
}
#div-3 {
background-color: green;
pointer-events: none;
transition: all 0.4s;
position: absolute;
height: 40%;
opacity: 0;
right: 0;
left: 0;
top: 0;
}
#div-3.active {
pointer-events: all;
opacity: 1;
}
#toggle {
position: absolute;
cursor: pointer;
z-index: 99999;
right: 10px;
top: 10px;
}
<div id="container">
<button id="toggle">Toggle</button>
<div id="div-1"></div>
<div id="div-2"></div>
<div id="div-3"></div>
</div>
Update
I'd modded the CSS given by David Thomas a bit. Its now a banner.
.div.popular::before {
/* setting the default styles for
the generated content: */
display: block;
width: 10em;
height: 2em;
line-height: 2em;
text-align: center;
background: #F60;
color: #fff;
font-size: 1.4rem;
position: absolute;
top: 30px;
right: 0px;
z-index: 1;
}
I would like to make a folded corner sort of like in this post: Folded banner using css
--- Original post ---
Let me first explain what I'm trying to do. I'm trying to give some post some extra attention by making a little circle with some call-to-action text in it.
But I only want this to trigger when a div has a specific class.
So if the div the class populair or sale I would like to have a little circle show up on that post. This script what I am using right now.
$(document).ready(function($){
if($("#front-page-items").hasClass('populair')){
$(".populair-div").show();
}
if($("#front-page-items").hasClass('sale')){
$(".sale-div").show();
}
});
And this HTML:
<div class="populair-div" style="display:none;">
<strong>Populair</strong>
</div>
<div class="sale-div" style="display:none;">
<strong>Sale</strong>
</div>
But this only show's the populair-div and not the other one. I'm guessing my script is wrong. Should I use else for all the other call-to-action classes?
$(document).ready(function($){
if($("#front-page-items").hasClass('populair')){
$(".populair-div").show();
}
else($("#front-page-items").hasClass('sale')){
$(".sale-div").show();
}
else($("#front-page-items").hasClass('Free')){
$(".free-div").show();
} // and so on
});
Is there someone that could help me out? Also is it possible to echo the div so I don't have to write a whole div for every call-to-action div?
For something like this, where the displayed text is explicitly linked to the class-name of the element it's easiest to use CSS and the generated content available, effectively hiding the elements you don't wish to show by default and then explicitly allowing elements you want to show, along with the generated content of those elements (using the ::before and ::after pseudo-elements:
div {
/* preventing <div> elements
from showing by default: */
display: none;
}
div.populair-div,
div.sale-div {
/* ensuring that elements matching
the selectors above (<div>
elements with either the 'sale-div'
or 'populair-div' class-names
are shown: */
display: block;
}
div.populair-div::before,
div.sale-div::before {
/* setting the default styles for
the generated content: */
display: block;
width: 4em;
height: 4em;
line-height: 4em;
text-align: center;
border: 3px solid transparent;
border-radius: 50%;
}
div.populair-div::before {
/* setting the text with the
"content" property: */
content: "Popular";
/* providing a specific colour
for the generated contents'
border: */
border-color: #0c0;
}
div.sale-div::before {
content: "Sale";
border-color: #f90;
}
/* entirely irrelevant, just so you can
see a (slightly prettified) difference
should you remove the default display
property for the <div> elements: */
code {
background-color: #ddd;
}
em {
font-style: italic;
}
<div class="neither-popular-nor-sale">
<p>
This element should not be shown, it has neither a class of <code>"populair-div"</code> <em>or</em> <code>"sale-div"</code>.
</p>
</div>
<div class="populair-div">
</div>
<div>Also not to be shown.</div>
<div class="sale-div">
</div>
You can use toggle function for this. It will be shorter and clearer.
Display or hide the matched elements.
Note: The buttons is for tests.
$(document).ready(function($){
init();
});
function init() {
$(".populair-div").toggle($("#front-page-items").hasClass('populair'));
$(".sale-div").toggle($("#front-page-items").hasClass('sale'));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="front-page-items" class="populair sale"></div>
<div class="populair-div">populair-div</div>
<div class="sale-div">sale-div</div>
<hr />
<button onclick="document.getElementById('front-page-items').classList.toggle('populair');init()">toggle populair</button>
<button onclick="document.getElementById('front-page-items').classList.toggle('sale');init()">toggle sale</button>
Working on creating functionality where when the user clicks on one of the products (each of the elements have the same assigned ID card-reveal) it adds a CSS class to the container specifically clicked (active state) to show information for that specific item and then finally, when the user clicks the cancel button the CSS class is removed (activate state gone).
Unfortunately I have run to a few hiccups where when I click on the 1st element it adds the class to that element but the other elements I click do not add the class, as well the close button does not function at all. I would like to finish the solution in Pure Javascript. Also if you see a few classie() methods, I am using Classie.js to help with CSS class toggling.
Any help will be appreciated! Thank You!
Html
<a id="card-reveal" class="card-view" href="javascript:void(0)"><h3 class='hover-title'>View More</h3></a>
<div class="card-cover">
<span class="card-exit"></span>
<a class="card-follow" href="javascript:void(0)">Follow {{object.product_website_name}}.com</a>
<a class="card-buy" target="_blank" href="{{object.product_slug_url}}">Buy {{object.product_name }}</a>
<a id="card-close" class="card-info" href="javascript:void(0)"><span class="icon-indie_web-03"></span></a>
<ul class="card-social">
<label>Share</label>
<li><span class="icon-indie_web-04"></span></li>
<li><span class="icon-indie_web-05"></span></li>
</ul>
</div>
CSS
.card-cover {
width:100%;
height: 100%;
background: none repeat scroll 0% 0% rgba(255, 91, 36, 0.9);
color: #FFF;
display: block;
position: absolute;
opacity: 0;
z-index:200;
overflow: hidden;
-webkit-transform:translate3d(0, 400px, 0);
transform:translate3d(0, 400px, 0);
-webkit-backface-visibility:hidden;
backface-visibility: hidden;
-webkit-transition-property:opacity, transform;
transition-property:opacity, transform;
-webkit-transition-duration:0.2s;
transition-duration:0.2s;
-webkit-transition-timing-function:cubic-bezier(0.165, 0.84, 0.44, 1);
transition-timing-function:cubic-bezier(0.165, 0.84, 0.44, 1);
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
.card-cover.card--active {
opacity: 1;
-webkit-transform:translate3d(0, 0, 0);
transform:translate3d(0, 0px, 0);
}
JS below:
var cardContainer = document.querySelector('.card-cover'),
cardTargets = Array.prototype.slice.call( document.querySelectorAll( '#card-reveal' ) ),
eventType = mobilecheck() ? 'touchstart' : 'click',
cardClose = document.getElementById('card-close'),
resetMenu = function() {
classie.remove( cardContainer, 'card--active' );
},
resetMenuClick = function( ) {
cardCloseaddEventListener(globalMenuEventType, function() {
resetMenu();
document.removeEventListener(eventType, resetMenuClick);
}, false);
};
cardTargets.forEach(function (element, index) {
if( element.target ) {
element.addEventListener(eventType, function( event ) {
event.preventDefault();
event.stopPropagation();
classie.add(cardContainer, 'card--active');
document.addEventListener(eventType, resetMenuClick);
} ,false);
}
});
There are two simple ways I can think of doing something like this.
First, if you can't designate ID's for each card (which it sounds like you can't), you're going to have to go by class names. Like it was mentioned in the comments, you really don't want to use the same ID for multiple elements.
Part of the reason for this is, as you can see from my examples below, that the .getElementById() method is only meant to return one element, where the other methods like .getElementsByClassName() will return an array of elements.
The problem we're trying to solve is that the sub-content you want to display/hide has to be attached to the element you click somehow. Since we're not using ID's and you can't really rely on class names to be unique between elements, I'm putting the div with the information inside a container with the element that toggles it.
Inside a container div, are two divs for content. One is the main content that's always visible, the other is the sub-content that only becomes visible if the main content is clicked (and becomes invisible when clicked again).
The benefit of this method is that since there are no ID's to worry about, you can copy/paste the cards and they'll each show the same behaviour.
var maincontent = document.getElementsByClassName("main-content");
// Note: getElemenstByClassName will return an array of elements (even if there's only one).
for (var i = 0; i < maincontent.length; i++) {
//For each element in the maincontent array, add an onclick event.
maincontent[i].onclick = function(event) {
//What this does is gets the first item, from an array of elements that have the class 'sub-content', from the parent node of the element that was clicked:
var info = event.target.parentNode.getElementsByClassName("sub-content")[0];
if (info.className.indexOf("show") > -1) { // If the 'sub-content' also contains the class 'show', remove the class.
info.className = info.className.replace(/(?:^|\s)show(?!\S)/g, '');
} else { // Otherwise add the class.
info.className = info.className + " show";
}
}
}
.container {
border: 1px solid black;
width: 200px;
margin: 5px;
}
.main-content {
margin: 5px;
cursor: pointer;
}
.sub-content {
display: none;
margin: 5px;
}
.show {
/* The class to toggle */
display: block;
background: #ccc;
}
<div class="container">
<div class="main-content">Here is the main content that's always visible.</div>
<div class="sub-content">Here is the sub content that's only visible when the main content is clicked.</div>
</div>
<div class="container">
<div class="main-content">Here is the main content that's always visible.</div>
<div class="sub-content">Here is the sub content that's only visible when the main content is clicked.</div>
</div>
<div class="container">
<div class="main-content">Here is the main content that's always visible.</div>
<div class="sub-content">Here is the sub content that's only visible when the main content is clicked.</div>
</div>
The second method, would be to use one div for the content that you want to show/hide, and clicking on an element will toggle both its visibility and it's content.
I'll use the previous example as a base, but ideally you would have some kind of MVVM framework like react, knockout, or angular to help you with filling in the content. For the sake of this example, I'm just going to use the text from the div of sub-content.
var info = document.getElementById("Info");
var maincontent = document.getElementsByClassName("main-content");
for (var i = 0; i < maincontent.length; i++) { //getElemenstByClassName will return an array of elements (even if there's only one).
maincontent[i].onclick = function(event) { //For each element in the maincontent array, add an onclick event.
//This does the same as before, but I'm getting the text to insert into the info card.
var text = event.target.parentNode.getElementsByClassName("sub-content")[0].innerHTML;
info.innerHTML = text; // Set the text of the info card.
info.style.display = "block"; //Make the info card visible.
}
}
info.onclick = function(event) {
info.style.display = "none"; // If the info card is ever clicked, hide it.
}
.container {
border: 1px solid black;
width: 200px;
margin: 5px;
padding: 5px;
}
.main-content {
margin: 5px;
cursor: pointer;
}
.sub-content {
display: none;
margin: 5px;
}
#Info {
cursor: pointer;
display: none;
}
<div id="Info" class="container">Here is some test information.</div>
<div class="container">
<div class="main-content">Link 1.</div>
<div class="sub-content">You clicked link 1.</div>
</div>
<div class="container">
<div class="main-content">Link 2.</div>
<div class="sub-content">You clicked link 2.</div>
</div>
<div class="container">
<div class="main-content">Link 3.</div>
<div class="sub-content">You clicked link 3.</div>
</div>