I have a number of user stories on my website page. I don't want to show the entirety of each story if the user doesn't want to read it, so I show the first 2 lines and there is a Show more link to press to see the whole story. The user can then press the link again to See less. The issue comes when the user clicks the Show less link, the story condenses and shows 2 lines, but there is a momentary flicker of say 2 additional lines (can't quite make it out as its there for a few milliseconds). And it is this that I don't want.
This is my HTML and jQuery which is loaded via Ajax Request.
$(document).ready(function() {
$(".content").on("click", ".showMore a", function() {
var $this = $(this);
var content = $this.parent().prev()
var linkText = $this.text().toUpperCase();
if (linkText === "SHOW MORE") {
linkText = "Show less";
content.switchClass("hideContent", "showContent", 400);
} else {
linkText = "Show more";
content.switchClass("showContent", "hideContent", 400);
}
$this.text(linkText);
});
});
.hideContent {
overflow: hidden;
line-height: 1em;
height: 4em;
}
.showContent {
line-height: 1em;
height: auto;
}
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
<div class="hideContent" style="">
<div class="post-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. In id erat pharetra risus fermentum aliquam. Maecenas eu nisi posuere, rutrum orci et, imperdiet elit. Nulla tempor imperdiet sagittis. Aenean cursus justo ac enim lacinia vehicula. Etiam dictum
suscipit nibh, at iaculis velit lobortis vel. Duis pretium diam ut lectus mollis vehicula.</div>
<div class="post-action"><input type="button" value="Like" id="like_94" class="like"><span class="likesTotal" id="likes_94">0</span>
</div>
</div>
<div class="showMore"><a>Show more</a></div>
</div>
</div>
I hope that's what you wanted. You can do this easily by using .addClass and .removeClass
Also. if you content and stories display as exactly as the example in question then .parent() is not what you want you can call .prev() and it will work just find.
Simple Show and Hide
Using addClass and removeClass
Working Demo: https://jsfiddle.net/usmanmunir/cks8d067/
Run snippet below to see it working.
$(document).ready(function() {
$(".showMore").on("click", function() {
var $this = $(this);
var content = $this.prev()
var linkText = $this.text().toUpperCase();
if (linkText === "SHOW MORE") {
linkText = "Show less";
content.addClass("showContent").removeClass("hideContent");
} else {
content.addClass("hideContent").removeClass("showContent");
linkText = "Show more";
}
$this.text(linkText);
});
});
.hideContent {
overflow: hidden;
line-height: 1em;
height: 2em;
}
.showContent {
line-height: 1em;
height: auto;
}
.showMore {
cursor: pointer;
}
<div class="hideContent">
<div class="post-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. In id erat pharetra risus fermentum aliquam. Maecenas eu nisi posuere, rutrum orci et, imperdiet elit. Nulla tempor imperdiet sagittis. Aenean cursus justo ac enim lacinia vehicula. Etiam dictum
suscipit nibh, at iaculis velit lobortis vel. Duis pretium diam ut lectus mollis vehicula.</div>
<div class="post-action"><input type="button" value="Like" id="like_94" class="like"><span class="likesTotal" id="likes_94">0</span>
</div>
</div>
<div class="showMore"><a>Show more</a></div>
Accordion Effects
Using accordion effect we can use .animate and .css
To do the accordion effects we can use .animate and .css for height to show more or less of the story. We will use .siblings
Working Fiddle: https://jsfiddle.net/usmanmunir/ovgah34z/
$(document).ready(function() {
$(".content").on("click", '.showMore', function() {
var $this = $(this);
var content = $this.prev()
var linkText = $this.text().toUpperCase();
if (linkText === "SHOW MORE") {
linkText = "Show less";
$this.siblings('div').css('height', 'auto');
var currHeight = $this.siblings('div').height();
$this.siblings('div').css('height', '2em');
$this.siblings('div').animate({
height: currHeight
}, 500);
} else {
$this.siblings('div').animate({
height: '2em'
}, 500);
linkText = "Show more";
}
$this.text(linkText);
});
});
.hideContent {
overflow: hidden;
line-height: 1em;
height: 2em;
}
.showContent {
line-height: 1em;
height: auto;
}
.showMore {
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
<div class="hideContent">
<div class="post-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. In id erat pharetra risus fermentum aliquam. Maecenas eu nisi posuere, rutrum orci et, imperdiet elit. Nulla tempor imperdiet sagittis. Aenean cursus justo ac enim lacinia vehicula. Etiam dictum
suscipit nibh, at iaculis velit lobortis vel. Duis pretium diam ut lectus mollis vehicula.</div>
<div class="post-action"><input type="button" value="Like" id="like_94" class="like"><span class="likesTotal" id="likes_94">0</span>
</div>
</div>
<div class="showMore"><a>Show more</a></div>
</div>
Let me know.
Here is an example of toggle inside a custom function .
If this is the result that you want .
Do not forget to mark my answer as the right answer!
Regards
function makeTheMagic(){
$("#extraContent").toggle();
let btnText= $("#btnAction").text() == "Show More!"?"Show Less!":"Show More!";
$("#btnAction").text(btnText);
}
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='card'>
<div class='card-body'>
<p class='alert-success'>
In my situation I want to show the first 2 sentences and then show the rest of the text with a toggle, but I couldn't find any examples of this. Did you have any examples of this? Thank you.
</p>
<p class='alert-danger' style='display:none' id='extraContent'>
I have a number of user stories on my website page. I don't want to show the entirety of each story if the user doesn't want to read it, so I show the first 2 lines and there is a Show more link to press to see the whole story. The user can then press the link again to See less. The issue comes when the user clicks the Show less link, the story condenses and shows 2 lines, but there is a momentary flicker of say 2 additional lines (can't quite make it out as its there for a few milliseconds). And it is this that I don't want.
</p>
<span class='btn btn-outline-success' onclick='makeTheMagic()' id='btnAction'>Show More!</span>
</div>
</div>
Related
I am trying to get the height on an element.
I want the parent div to adjust its height to the first paragraph of its children paragraph elements. Then a "Read More"/"Read Less" button expands the parent div to reveal all paragraphs or shrinks to only one paragraph.
I have experimented with useEffect, useLayoutEffect and componentDidMount in a class component and they all seem to need a setTimeout delay for the parent div to attain the perfect height.
Sorry if my code is fuzzy. I am new to React. :)
TIA
import styled from 'styled-components'
import React, { useRef, useLayoutEffect, useState } from 'react'
const CatIntroStyled = styled.div`
width: 1000px;
margin: 0 auto;
button{
display: block;
margin: 0 auto;
background: none;
}
button:focus{
outline: none;
}
`
const IntroText = styled.div`
height: ${({introStyle})=>{
if(introStyle.initIntroHeight === "auto") return "auto"
return introStyle.introExpanded ? introStyle.initIntroHeight+"px": introStyle.initFirstPara+"px";
}};
overflow: hidden;
transition: all 1s;
margin-bottom: 2rem
`
const formatIntro = (text, paraRef)=>{
let formatedText = text.replace(/<[^>]*>?/gm, "").replace(/\n\r/g, "")
let returnText = formatedText.split("\r\n").map((paragraph, key) => {
if(key===0) return <p ref={paraRef} key={key}>{paragraph}</p>;
return <p key={key}>{paragraph}</p>
})
return returnText
}
const CatIntro = ({title, text})=>{
const firstIntroPara = useRef();
const introRef = useRef();
const [intro, setIntro] = useState({
initFirstPara: 0,
initIntroHeight: "auto",
introExpanded: false
})
useLayoutEffect(()=>{
setTimeout(()=>{
setIntro({
...intro,
initIntroHeight: introRef.current.offsetHeight,
initFirstPara: firstIntroPara.current.offsetHeight,
})
}, 1000)
}, [])
return(
<CatIntroStyled>
<h1 className="globalTitleStyle">{title}</h1>
<IntroText ref={introRef} introStyle={intro}>
{formatIntro(text, firstIntroPara)}
</IntroText>
<button onClick={(e)=>{
setIntro({
...intro,
introExpanded: !intro.introExpanded
})
}}>{ intro.introExpanded ? "READ LESS": "READ MORE" }</button>
</CatIntroStyled>
)
}
export default CatIntro
Is there a more robust way of knowing when elements are truly painted on the screen?
TIA
Try using useLayoutEffect.
This runs synchronously immediately after React has performed all DOM mutations. This can be useful if you need to make DOM measurements (like getting the scroll position or other styles for an element).
Example
function App() {
const divRef = React.useRef(null);
React.useLayoutEffect(() => {
console.log(divRef.current.clientHeight)
}, [])
return (
<div ref={divRef} style={{ height: 100, width: 100, backgroundColor: 'red' }}/>
);
}
For this :- You need to change your class component to functional ones.
Maybe i don't fully understand why you need the height.
But if each child of the component is a paragraph, and you either want to show all the paragraphs when expanded, but only one paragraph when not expanded, you could do something like this:
import React, { useState } from "react";
export default function Expandable({ children, initial = false }) {
const [expanded, setExpanded] = useState(initial);
return (
<div>
{expanded ? children : [...children].slice(0,1)}
<button onClick={() => setExpanded(!expanded)}>{`Read ${
expanded ? "less" : "more"
}`}</button>
</div>
);
}
Then you could consume the component like this:
<Expandable>
<p>
egestas ultrices. Curabitur eget lorem eu augue pretium blandit at non
metus. Mauris a venenatis tellus, vel mollis leo. Vivamus nec
elementum neque, non mollis felis.
</p>
<p>
fringilla. Sed convallis sem sed diam vehicula egestas. In tincidunt
hendrerit elit, eu facilisis leo vulputate id. Sed rutrum imperdiet
convallis. Nam mi magna, lacinia vitae consequat vel, consequat eget
ex. Maecenas nec ex egestas, mattis orci sit amet, dictum sem. Sed id
tincidunt felis. Vivamus ipsum erat, sagittis sed consequat et,
molestie a risus. Quisque nec risus fringilla, pellentesque leo a,
venenatis leo.
</p>
<p>
est in varius pulvinar. Ut dignissim condimentum semper. Vestibulum
blandit purus vitae dapibus finibus. Nam iaculis metus orci, et
posuere lectus imperdiet at. Suspendisse non erat tortor.
</p>
<p>ullamcorper sagittis.</p>
</Expandable>
Edit:
You can get the height of the first paragraph like this.
Note: with this approach, you probably need to listen for a resize event and adjust the value of the height state.
import React, { useState, useEffect, useRef } from "react";
export default function Expandable({ children, initial = false }) {
const [expanded, setExpanded] = useState(initial);
const [firstParagraphHeight, setFirstParagraphHeight] = useState(0);
const ref = useRef(null);
useEffect(() => {
const height = ref.current.children[0].getBoundingClientRect().height;
setFirstParagraphHeight(height);
}, []);
return (
<>
<div
ref={ref}
style={{
overflow: "hidden",
maxHeight: expanded ? "none" : `${firstParagraphHeight}px`
}}
>
{children}
</div>
<button onClick={() => setExpanded(!expanded)}>{`Read ${
expanded ? "less" : "more"
}`}</button>
</>
);
}
I have a page showing user submitted stories. Each story only showing a few lines. There is a show more / show less button. I would like to create a fade effect after the first few sentences to accentuate there is more to read, however I'm struggling to do this.
I've tried the code from this question as a starting point link. Though the code from the accepted answer fades my whole page not each story separately. The end goal is to have the 3rd and 4th lines fading and then unfaded and the whole story shown on pressing the show more button and faded again when pressing show less.
EDIT 20200813: Amended the post-text::before position to relative, highlighted by #imvain2. I can see it's in the desired position in front of the post-text class text. But the ::before has no physical dimensions on the page. See screenshot below:
$(document).ready(function() {
$(".content").on("click", '.showMore', function() {
var $this = $(this);
var content = $this.prev()
var linkText = $this.text().toUpperCase();
if (linkText === "SHOW MORE") {
linkText = "Show less";
$this.siblings('div').css('height', 'auto');
var currHeight = $this.siblings('div').height();
$this.siblings('div').css('height', '6em');
$this.siblings('div').animate({height: currHeight}, 500);
} else {
$this.siblings('div').animate({height: '6em'}, 500);
linkText = "Show more";
}
$this.text(linkText);
});
});
.post-text:before {
content:'';
width:100%;
height:100%;
position:relative; <!-- Edit 20200813 !-->
left:0;
top:0;
background:linear-gradient(transparent 150px, white);
}
.post-text {
letter-spacing: normal;
line-height: 2;
font-size: 16px;
font-family: Times, serif;
color: black;
text-align: justify;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
<div class="post">
<div class="hideContent" style="height: 6em;">
<div class="post-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean
semper, ligula quis gravida pretium, ligula arcu vestibulum mi, et
laoreet mauris ex at est. Donec pulvinar laoreet mollis. </div>
<div class="post-action">
<input type="button" value="Like" id="like_93" class="like">
<span class="likesTotal" id="likes_93">0</span>
</div>
</div>
<div class="showMore">Show more</div>
</div>
</div>
I want to target elements which have a visible scrollbar using only CSS. Is this possible without javascript?
For example, If I have 3 divs styled with overflow-y:auto, How do I change the styles for them only when their scrollbar has appeared?
CSS does not cover this selection. You need to use JavaScript.
With pure CSS I doubt it but it doesn't require a lot of javascript code either, look at this example:
document.querySelectorAll('*').forEach(el => {
if (el.offsetHeight > document.documentElement.offsetHeight) {
console.log('I am higher than my father: ', el);
el.classList.add('higher-class');
}
});
.higher-class {
color: red;
}
<div class="container" style="height:50px;">
<div class="some-child" style="height:100px;font-size: 5rem">
Higher element
</div>
</div>
check
offsetHeight property:
https://developer.mozilla.org/es/docs/Web/API/HTMLElement/offsetHeight
And the classList property:
https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
It's not possible without javascript
However it only requires a single line of JS to toggle a CSS class on when the scrollbar is visible:
el.classList.toggle("scrollbarOn", el.scrollHeight > el.clientHeight)
Here's a demo:
//toggles a class on an element when the scrollbar is visible:
function updScrollClass(el) {
return el.classList.toggle("scrollbarOn", el.scrollHeight > el.clientHeight)
}
//changes the height of myDiv every second:
setInterval(function(){
var myDiv = document.getElementById('myDiv')
myDiv.classList.toggle('tall')
updScrollClass(myDiv)
},1000)
#myDiv{
width:150px;
height:200px;
overflow:auto;
}
#myDiv.tall{
height:300px;
}
.scrollbarOn{
background:yellow;
}
<div id='myDiv' class='tall'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis nisl in accumsan porta. Etiam in urna orci. Vestibulum elementum, velit ac vestibulum efficitur, est elit auctor massa, nec porta ante nunc eget tellus. Integer eu ligula felis.
</div>
I don't know why I'm getting two different colors when the page loads. I look into Computed and Styles tabs from the element inspector, but this shows me the same values for each one.
Image:
The first style is for the title, and the other for the "Seguir Leyendo", a "Read more" link, both on top of all, without alphas and nothing to give different tones.
Any clue why this could happend?
Add:
HTML:
<div class="blog_entry">
<div class="blog_entry_image">
<img src="img/blog_imagen_1.png" class="blog_image_home"alt="Blog entry 1">
</div>
<div class="blog_entry_desc">
<h2 class="blog_entry_title"><a href="home-blog-detail-one.html">Lorem ipsum dolor sit amet, consectetur
adipiscing lit. Phaiop ellus ut diam nibh consectetur adpiscing</a></h2>
</div>
<div class="blog_entry_date">
28 Diciembre 2014
</div>
<div class="blog_entry_text">
<p>Nulla arcu quam, tempor a mattis sit amet, efficitur eget ante. Integer elit
libero, rutrum eu augue ut, faucibus dictum nibh. Suspendisse vitae ex sit amet
tellus molestie interdum...</p>
</div>
<div class="blog_entry_options">
<img src="img/blog_facebook.png" alt="Facebook">
<img src="img/blog_twitter.png" alt="Twitter">
Seguir leyendo <img src="img/arrow_read_more.png" alt="Seguir leyendo">
</div>
<div class="clearfix"></div>
</div>
CSS:
.blog_entry_title {
font-size: 24px;
}
.blog_entry_title a, .blog_title_detail {
color: #008ed6;
text-decoration: none;
font-family: HelveticaNeueThin;
}
.blog_entry_options {
position: absolute;
bottom: 0;
right: 0;
}
.blog_entry_options a {
color: #008ed6;
position: absolute;
bottom: 0;
right: 0;
font-size: 17px;
}
By updating/resetting the font-weight you can see the magic :
CSS :
.blog_entry_title a{ font-weight: normal }
I have a CSS script which has 5 buttons.
Clicking the first button should display a text on the same page in a fixed region (maybe by using frames or writing a hidden function and then doing show()).
If the second button is clicked, some other text should appear in the earlier region. Similarly for other buttons. What would be the most efficient way of doing this? (Achieving the fastest loading of the page).
Try this:
HTML
<div id="textcontainer"></div>
<button id="button1" onclick="setText(0);">Button 1</button>
<button id="button2" onclick="setText(1);">Button 2</button>
<button id="button3" onclick="setText(2);">Button 3</button>
<button id="button4" onclick="setText(3);">Button 4</button>
<button id="button5" onclick="setText(4);">Button 5</button>
Javascript:
var text = [
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna.',
'Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus.',
'Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci.',
'Aenean nec lorem. In porttitor. Donec laoreet nonummy augue.',
'Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.'
];
function setText(index){
var box = document.getElementById('textcontainer');
box.innerHTML = text[index];
}
You can see this in action at: http://jsfiddle.net/Wnw7X/
I guess this?
<iframe name="content"></iframe>
Button 1
Button 2
And so on...
Then add some CSS to make them look like buttons (border, background, etc)
ok, here is a complete demo - copy all the code and paste your editor. and run.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script type="text/javascript">
window.onload = function() {
var myArray = ['you click the first button', 'you click the second button', 'you click the third button', 'you click the fourth button', 'you click the fifth button'];
var one = document.getElementById('buttonOne');
var two = document.getElementById('buttonTwo');
var three = document.getElementById('buttonThree');
var four = document.getElementById('buttonFour');
var five = document.getElementById('buttonFive');
one.onclick = clickHandler;
two.onclick = clickHandler;
three.onclick = clickHandler;
four.onclick = clickHandler;
five.onclick = clickHandler;
function clickHandler(evt) {
//console.log(evt.target.id);
var header = document.getElementsByTagName('h1')[0];
if(evt.target.id === 'buttonOne') {
header.innerHTML = myArray[0];
console.log(myArray[0]);
console.log('click');
}
else if (evt.target.id === 'buttonTwo') {
header.innerHTML = myArray[1];
}
else if (evt.target.id === 'buttonThree') {
header.innerHTML = myArray[2];
}
else if (evt.target.id === 'buttonFour') {
header.innerHTML = myArray[3];
}
else if (evt.target.id === 'buttonFive') {
header.innerHTML = myArray[4];
}
}
}
</script>
</head>
<body>
<button id="buttonOne">One</button>
<button id="buttonTwo">Two</button>
<button id="buttonThree">Three</button>
<button id="buttonFour">Four</button>
<button id="buttonFive">Five</button>
<h1> </h1>
</body>
</html>