Expanding text within expanding text - javascript

I have found several codes providing brilliant options to have a text expand upon clicking another one. Expand/collapse functions, I believe we are all somewhat familiar with them.
What I am mainly interested in though, is how could I have expanding text show from a block of text that I already had expanded from another line?
I mean that I'd click a line and it shows another line which I can click again and shows a third one that was not visible before at all.
Is that possible in any way? Thank you very much for any feedback for which you take your time to provide in advance!

I see that you tagged jQuery, so I write small really simple example https://jsfiddle.net/alienpavlov/khk40ygm/2/ using it:
$('.expand').click(function(e) {
e.stopPropagation();
var $expand = $(this).children('.expand.hide');
if ($expand.length >= 1) {
$expand.removeClass('hide');
} else {
$(this).find('.expand').addClass('hide');
}
});
div {
padding: 15px;
}
.hide {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div class="expand">
Some parent text
<div class="expand hide">
1st level child
<div class="expand hide">
2n level child
</div>
</div>
</div>
In this example you can have more than 2 child elements

You can try something like this: https://jsfiddle.net/julienetienne/41g9f1pt/1/
The key is to get the index, I prefer to put node-list into arrays, that way you can use first class functions.
This rough demo I made will need a polyfill or two for IE8 support:
var section = document.getElementsByTagName('section')[0];
var sectionArray = [].slice.call(section.children);
var lastElement;
section.addEventListener('click', showHide, true);
function showHide(e) {
var target = e.target;
var indexOfSection = sectionArray.indexOf(target);
var elementToToggleHeight;
// If element is a paragraph
if (target.tagName === 'P') {
var elementToToggle = section.children[indexOfSection + 1];
if(indexOfSection + 1 < sectionArray.length){
elementToToggleHeight = parseInt(window.getComputedStyle(elementToToggle, null).getPropertyValue("height"),10);
}
if (elementToToggleHeight) {
// Function to close all except current and first
sectionArray.forEach(function(paragraph){
if(paragraph !== target && paragraph !== sectionArray[0]){
paragraph.style.height = 0;
}
});
} else {
if(elementToToggle)
elementToToggle.style.height = '1em';
}
}
}
p {
font-size: 1em;
cursor: pointer;
height: 0;
overflow: hidden;
background: yellowgreen;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
margin: 0;
padding: 0;
}
p:first-child {
height: auto;
}
<section>
<p>The path of the righteous man is beset on all sides</p>
<p>by the inequities of the selfish and the tyranny of evil men</p>
<p>And I will strike down upon thee with great </p>
<p>vengeance and furious anger those who would attempt</p>
<p>to poison and destroy my brothers</p>
</section>

Related

CSS height not transitioning in Javascript

help me out here with why my CSS transition isn't working.
I'm trying to animate a height change to a <div> element, either growing it or shrinking it, through a Javascript function. My markup is this:
<div class="post series expandcollapse" id="id1" style="height: 32px;">
<p class="expandcollapse_link" id="expandcollapselink1" name="expandcollapselink1" onclick="toggleCollapsedItem('1');"><img id="expandcollapseimage1" name="expandcollapseimage1" class="expandcollapse" src="images/icons/expandcollapse.png" alt=""></p>
<h2 onclick="toggleCollapsedItem('1');">Some random heading here</h2>
<p>Some random text here</p>
</div>
The CSS attached to the <div>, <h2>, and <img> are:
div.expandcollapse { border-bottom-left-radius: 20px; border-bottom-right-radius: 20px; height: 32px; transition: all 5s ease-in; overflow: hidden; }
div.expandcollapse h2 { cursor: pointer; }
div.expandcollapse img.expandcollapse { transition: all .5s; cursor: pointer; }
div.expandcollapse p.expandcollapse_link { float: right; margin-top: -2px; }
When the user clicks on this, it launches this Javascript function:
function toggleCollapsedItem(clickedItem, forceOpen = false) {
var collapsedItem = document.getElementById('id' + clickedItem);
var collapsedLink = document.getElementById('expandcollapselink' + clickedItem);
var collapsedImage = document.getElementById('expandcollapseimage' + clickedItem);
if(typeof collapsedItem.originalHeight === 'undefined') {
Object.defineProperty(collapsedItem, 'originalHeight', { value: collapsedItem.style.height, writable: false });
}
if((collapsedItem.style.height === collapsedItem.originalHeight) || (forceOpen === true)) {
collapsedItem.style.height = 'auto';
collapsedImage.style.transform = 'rotate(45deg)';
} else {
collapsedItem.style.height = collapsedItem.originalHeight;
collapsedImage.style.transform = '';
}
}
The image rotates gradually (according to the transition) exactly as it should, but the height doesn't care about the transition. Can anyone spot my problem here? I've been up and down Google, Stackoverflow for similar questions, all over the place, and I can't get the height to change gradually.
Thanks, everyone.
You cannot transition to or from auto values, only between defined values.
I'm assuming the element can be of varying height. If you're already using Javascript, you can transition from 32px to the element's offsetHeight.
So instead
collapsedItem.style.height = 'auto';
you'd do
collapsedItem.style.height = collapsedItem.offsetHeight+'px';

How to show the full name of an acronym on hover in CSS/JS?

Let's say I want my website to display "CSS" and, when hovered, make a sliding animation that results in displaying "Cascading Style Sheets".
I achieved a similar animation using the font size as a workaround (Fiddle) but I would like the words to slide from the initials.
Any idea on how this could be done using the simplest forms of CSS or JS ?
p span {
font-size: 0px;
transition: all 0.3s ease-out;
}
p:hover span {
font-size: 15px;
}
<p>
C<span>ascading</span>
S<span>tyle</span>
S<span>heets</span>
</p>
here is a js solution, it could be a bit better because I reuse the same logic in both functions but that works, feel free to change where the listener listens (in this case document)
const mouseEnterHandle = (event) => {
if (event.target.closest('p')) {
event.target.closest('p').querySelectorAll('span').forEach(el => {
el.style.width = el.scrollWidth + 'px';
})
}
}
const mouseOutHandle = (event) => {
if (event.target.closest('p')) {
event.target.closest('p').querySelectorAll('span').forEach(el => {
el.style.width = 0;
})
}
}
document.addEventListener('mouseover', (event) => mouseEnterHandle(event))
document.addEventListener('mouseout', (event) => mouseOutHandle(event))
document.removeEventListener('mouseover', (event) => mouseEnterHandle(event))
document.removeEventListener('mouseout', (event) => mouseOutHandle(event))
p {
overflow: hidden
display: flex;
}
p:hover span {
opacity: 1;
}
p span {
transition: all 0.3s ease-out;
width: 0;
opacity: 0;
display: inline-flex;
overflow: hidden;
}
<p>
C<span>ascading</span>
S<span>tyle</span>
S<span>heets</span>
</p>
So, after seeing bluebird's answer, I tried to come up with a CSS-only answer.
I achieved the desired result, the only "trick" here is that it is needed to measure the (approximate) width of the words. Here, they respectively are of 56, 24 and 33 pixels wide. I added the length of a space (3.2px) to the first two.
I tried to put
p:hover > span {
width:100%;
}
to avoid this case-by-case solution but it then expands too much.
If anyone has a more (CSS-only) general solution, this answer would be perfectly completed.
p {
display: flex;
}
p:hover > #cas {
width: calc(56px + 3.2px);
}
p:hover > #sty {
width: calc(24px + 3.2px);
}
p:hover > #she {
width: 33px;
}
p span {
width:0px;
overflow:hidden;
transition: all 0.3s ease-in;
transition: all 0.3s ease-out;
}
<!DOCTYPE html>
<html>
<body>
<p>
C<span id="cas">ascading</span>
S<span id="sty">tyle</span>
S<span id="she">heets</span>
</p>
</body>
</html>

Toggling Between a CSS class with pure javascript on 'n' elements

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>

Calculate the future height of a element

My intent is to animate using CSS3 the transition of height of a element when a child div get expanded.
<div id="container">
<div id="content">
<span>Small Conent</span>
<div id="big">
<p>
This is way bigger content, will be visible after you have clicked the
"Expand" button.
</p>
<p>It should animate up to the correct position.</p>
</div>
</div>
<button id="expand">Expand</button>
</div>
I came up with this hack, using max-height. But there are a couple of problems:
The max-height must have a value
The animation will start and stop according to the max-height given value, so if you insert a crazy value like 2000px the animation will have a great delay.
To better illustrate the problem, I created a Fiddle: http://jsfiddle.net/egDsE/3/
The only way of having a precise animation, is to insert the correct value of max-height.
My intention is to calculate using JavaScript (and JavaScript only) what the height of the parent will be once the child is expanded. But of course, I will need to calculate it before the actual transition takes place.
Is this possible? Only pure JS please.
Actually, you don't need to do all of the that cloning and stuff...just give the element height auto, check the size and set the height back to 0. It'll happen so fast the browser has no chance to repaint.
Now, this works like a charm, but the thing is that setting the height in Javascript immediately afterward will cause the transition to fail. I just throw a 100ms timeout around it and then it works fine.
Javascript:
document.getElementById('expand').addEventListener('click', function () {
var el = document.getElementById('big');
if (!el.className) {
el.className = 'expanded';
document.getElementById('expand').innerHTML = 'Retract';
var elH = getHeight(el);
window.setTimeout(function() {
el.style.height = elH+'px';
}, 100);
} else {
el.className = '';
el.style.height = '0';
document.getElementById('expand').innerHTML = 'Expand';
}
});
function getHeight(el) {
el.style.height = 'auto';
elHeight = el.offsetHeight;
el.style.height = '0';
return elHeight;
}
CSS:
#container {
border: 1px solid gray;
padding: 20px;
}
#big {
overflow: hidden;
height: 0;
transition: height 0.5s ease-in-out;
-webkit-transition: height 0.5s ease-in-out;
-moz-transition: height 0.5s ease-in-out;
-o-transition: height 0.5s ease-in-out;
}
HTML: no changes to your markup
<div id="container">
<div id="content"> <span>Small Conent</span>
<div id="big">
<p>This is way bigger content, will be visible after you have clicked the "Expand" button.</p>
<p>It should animate up to the correct position.</p>
</div>
</div>
<button id="expand">Expand</button>
</div>
DEMO
I have updated the Fiddle with the solution proposed in the comments by Bartdude, optimizing it for performances as possible.
http://jsfiddle.net/egDsE/5/
var calculateHeight = function(e) {
var outside = document.getElementById('outside');
var clone = e.cloneNode(true);
outside.appendChild(clone);
var elHeight = outside.offsetHeight;
outside.removeChild(clone);
return elHeight;
}
The outside div have very basic css:
#outside {
position: absolute;
top: -1000;
left: -1000;
}
I won't recommend this solution if image are involved, they would slow down the operation significantly.
not to be that guy but I would use LESS if you don't want to be as intensive and get this functionality

Fade In and Fade Out JavaScript function for all the content div on main page, on click

I'm currently working on a web site and I need help for an opacity transition. My problem is, I want to put all my content divs on my main page and depending on which "section" the user want by clicking on my navigating bar (top of the page), the corresponding div will fade in while the current div will fade out. I thought about using jQuery, but I think I prefer doing that by myself. Here's my code (which doesn't work, obviously):
function ShowDiv(id) {
var activeDiv = document.getElementById(id);
var divs = document.getElementsByClassName("contentDiv");
for (var i = 0; i < divs.length; i++) {
if (divs[i] != activeDiv) {
divs[i].style.opacity = "0";
divs[i].style.setProperty("-webkit-transition", "all 1s ease-in-out");
divs[i].style.display = "none"
}
}
activeDiv.style.position = "block";
activeDiv.style.opacity = "1";
activeDiv.style.setProperty("-webkit-transition", "all 1s ease-in-out");
}
The function is "triggered" when I click on my navigating bar. Depending on what is clicked, the "id" will be different. Basically, I make a variable that content the "need to be shown" div, and then the others. I want the "non selected" divs to fade out, and then the "selected" div to fade in, but that doesn't work. I searched but I didn't find any answer, but one interesting thing is that the first time it will work, but won't the second, third, etc.. time.
Any suggestions? Do I really need to use jQuery?
Sounds similar to something I did not so long ago. (I did however go the Jquery route) So if you decide to change your mind, here is a FIDDLE I created to answer pretty much the same question another poster had.
Don't let this scare you, but it uses:
$('#Hotel, #Meals').on('click', function(){
var el = $('#'+this.id+'body');
if ( el.is(':visible') ) return false;
el.animate({width:'toggle'},1000)
$('#Mealsbody, #Hotelbody').not(el).hide();
});
//#Hotels & #Meals being the ID of the content you want to bring in.
//At the moment it uses .animate, but you could change this to .fadeIn if you wanted to do so
Anyways, if you do decide to take on Jquery, you can use this as a starting point.
Good luck,
Mike
HTML
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
<div class="contentDiv" id="1">1</div>
<div class="contentDiv" id="2">2</div>
<div class="contentDiv" id="3">3</div>
CSS
.contentDiv {
width:100px;
height: 100px;
background: #333;
margin-bottom: 5px;
color: #fff;
-moz-transition: all 1s ease;
-o-transition: all 1s ease;
-webkit-transition: all 1s ease;
transition: all 1s ease
}
.remove {
opacity: 0;
height:0;
line-height:0;
padding:0;
border:none;
}
Javascript
function ShowDiv(id) {
var activeDiv = document.getElementById(id);
var divs = document.getElementsByClassName("contentDiv");
for (var i = 0; i < divs.length; i++) {
if (i != id) {
divs[i].classList.add('remove');
}else {
divs[i].classList.remove('remove');
}
}
}
Working Demo

Categories