I have several lines of text, each of them is truncated with ellipses.
.container {
display: block;
border: 1px solid;
width: 200px;
}
.text {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 20px;
text-align: justify;
}
<div class="container">
<p class="text">
This is text1 that has very very very long string.
</p>
<p class="text">
The ellipses of the line is not vertically aligened with the above one.
</p>
<p class="text">
This is text3 that has very very very long string.
</p>
<p class="text">
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</p>
</div>
As you can see from the demo, all the ellipses are not aligned vertically even though the element width is all the same.
How can I make all of them to be aligned vertically?
P.S. Screenshot attached
The text-align property is only used in cases where the length of the child element is less than the width of the parent (or containing element). If the length of the child element is wider than its parent / containing element then the text-align property isn't used. I get what you are trying to do with text-align: justify, but it doesn't work in this case as the paragraph elements lengths are actually extending past the 200px that you set for the container element.
So, in order to vertically align them, you just need to set a width and add it to the .text class.
Adding
width: 180px to your .text class should do the trick to set a width for the paragraph elements which will get it much closer.
However you'll still have the odd instance where they cut off at different characters and they won't align exactly as you expect, so you can do the below, which is a bit more tricky as you'll have to add a wrapper to each of the paragraph elements. It can potentially look a bit naff, but this would be how you do it:
.container {
display: block;
border: 1px solid;
width: 200px;
}
.text{
text-overflow: clip;
font-size: 20px;
height: 20px;
width: 170px;
overflow: hidden;
word-break: break-all;
}
.ellipsis{
position: relative;
}
.ellipsis::after {
content: "...";
position: absolute;
left: 170px;
top:0;
font-size: 20px;
}
.ellipsis:first-of-type::after {
top: 20px;
}
<div class="container">
<span class="ellipsis"><p class="text">
This is text1 that has very very very long string.
</p></span>
<span class="ellipsis"><p class="text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p></span>
<span class="ellipsis"><p class="text">
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p></span>
<span class="ellipsis"><p class="text">
This is text1 that has very very very long string.
</p></span>
</div>
Up to you which of those you use though :)
I think it's possible with css tricks.
.container {
border: 1px solid;
width: 200px;
}
.text {
position: relative;
overflow: hidden;
font-size: 20px;
height: 1.5em;
padding-right: 1em;
text-align: justify;
word-break: break-all;
}
.text::after {
content: '...';
position: absolute;
right: 0;
top: 0;
z-index: 1;
width: 1em;
text-align: center;
background: white;
}
<div class="container">
<p class="text">
<span>This is text1 that has very very very long string.</span>
</p>
<p class="text">
<span>The ellipses of the line is not vertically aligened with the above one.</span>
</p>
<p class="text">
<span>This is text3 that has very very very long string.</span>
</p>
<p class="text">
<span>Lorem ipsum dolor sit amet consectetur adipisicing elit.</span>
</p>
</div>
But with this method, if 'text' is smaller than 'container' it looks weird, so apply'::after' to a specific class.
For example,'.overflow::after' and apply the 'overflow' class when 'text' is larger than 'container' via JavaScript.
window.onload = function() {
var tmpContainer = document.getElementsByClassName('container');
var tmpContainerWidth = tmpContainer[0].clientWidth;
var tmpText = document.getElementsByClassName('text');
var tmpTextLen = tmpText.length;
for (var i = 0; i < tmpTextLen; i++) {
var tmpTextWidth = tmpText[i].children[0].offsetWidth;
var tmpTextFontSize = parseFloat(getComputedStyle(tmpText[i]).fontSize);
if (tmpTextWidth + tmpTextFontSize >= tmpContainerWidth) tmpText[i].classList.add('overflow');
}
}
.container {
border: 1px solid;
width: 500px;
}
.text {
position: relative;
overflow: hidden;
font-size: 20px;
height: 1.5em;
padding-right: 1em;
text-align: justify;
word-break: break-all;
}
.overflow::after {
content: '...';
position: absolute;
right: 0;
top: 0;
z-index: 1;
width: 1em;
text-align: center;
background: white;
}
<div class="container">
<p class="text">
<span>This is text1 that has very very very long string.</span>
</p>
<p class="text">
<span>The ellipses of the line is not vertically aligened with the above one.</span>
</p>
<p class="text">
<span>This is text3 that has very very very long string.</span>
</p>
<p class="text">
<span>Lorem ipsum dolor sit amet consectetur adipisicing elit.</span>
</p>
</div>
Related
I am working on a project that includes pages to view and select actions on multiple different entities of the same category set. Those entities are presented in the form of cards (e.g. employees, projects etc). On each card there is a classic gear button that expands a hidden drop menu so that the user can select actions (which might be the same or different for each entity/card). In order to avoid z-index inheritance issues (e.g. drop menu appearing on top of the parent but behind the sibling card) I implemented the solution presented in the link below (minimal representation).
The solution seems to be working fine, but i am wondering if it is elegant / acceptable in the context of implementing it on a production environment. If someone could comment on it or propose other solutions if this is not "acceptable" that would be great.
function getFixedElementPosition(el, menuid) {
var dropmenu = document.getElementById(`dropmenu${menuid}`);
if (dropmenu.style.display === "" || dropmenu.style.display === "none") {
var pos = el.getBoundingClientRect();
dropmenu.style.top = `${pos.bottom}px`;
dropmenu.style.left = `${pos.right}px`;
dropmenu.style.display = "block";
} else {
dropmenu.style.display = "none";
}
};
.card {
width: 225px;
height: 295px;
background: #fff;
float: left;
padding: 20px;
margin: 15px;
box-shadow: rgba(100, 100, 111, 0.2) 0px 0px 20px 0px;
transition: all 0.25s linear 0s;
position: relative;
border: 2px solid transparent;
backface-visibility: hidden;
-webkit-font-smoothing: subpixel-antialiased;
}
.card:hover {
box-shadow: rgb(38, 57, 77) 0px 2px 30px -10px;
}
.gear {
cursor: pointer;
float: right;
}
.dropmenu {
display: none;
position: absolute;
background: #fff;
box-shadow: rgba(100, 100, 111, 0.2) 0px 0px 20px 0px;
width: 80px;
padding: 10px;
z-index: 10;
}
a {
text-decoration: none;
display: block;
transition: all 0.2s linear;
}
a:hover {
color: #003a95;
}
p {
margin: 35px 10px;
}
<div class="wrapper">
<div class="card">
<img class="gear" onclick="getFixedElementPosition(this, 1)" src="https://www.indivstock.com/static35/preview2/stock-vector-gear-icon-illustrated-in-vector-on-white-background-520258.jpg" width="34">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt</p>
</div>
<!-- /Card #1 -->
<div id="dropmenu1" class="dropmenu" onmouseleave="this.style.display='none';">
View
Edit
Delete
</div>
<div class="card">
<img class="gear" onclick="getFixedElementPosition(this, 2)" src="https://www.indivstock.com/static35/preview2/stock-vector-gear-icon-illustrated-in-vector-on-white-background-520258.jpg" width="34">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt</p>
</div>
<!-- /Card #2 -->
<div id="dropmenu2" class="dropmenu" onmouseleave="this.style.display='none';">
View
Edit
Delete
</div>
<div class="card">
<img class="gear" onclick="getFixedElementPosition(this, 3)" src="https://www.indivstock.com/static35/preview2/stock-vector-gear-icon-illustrated-in-vector-on-white-background-520258.jpg" width="34">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt</p>
</div>
<!-- /Card #3 -->
<div id="dropmenu3" class="dropmenu" onmouseleave="this.style.display='none';">
View
Edit
Delete
</div>
</div>
https://codepen.io/t3l3machus/pen/GRvbjzM
P.S. The numbered ids (dropmenu1, dropmenu2..) are created by a template engine on document load.
There are a couple of things you could change and also some bugs.
The dropdown only works once because you're setting display = "none" but comparing it with empty string.
You can wrap this on a <div> that has position:absolute and then use position:relative to position the dropdown relative to the wrapper. This way you only have to set the display value.
Check out my updated version:
https://codepen.io/samura_lesi/pen/MWvMpKX
This question already has answers here:
Generating ellipsis AND "Read more" link with CSS
(3 answers)
Closed 2 years ago.
I have a text that I have truncated using the css - height and overflow property.
.container {
width: 600px;
border: 1px solid #888;
padding: 0.5em;
line-height: 1.2em;
margin-bottom: 10px;
}
.summary {
height: 47px;
/* adjust based on line-height * rows desired */
overflow: hidden;
}
.ellipsis {
height: 0;
}
.ellipsis span {
background: white;
padding-left: 0.5em;
position: relative;
top: -1.2em;
left: 3.2em;
color: blue;
}
<div class="container">
<div class="summary">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
</p>
<ul>
<li>test</li>
<li>test333</li>
</ul>
<strong>testststststs</strong>
</div>
<div class="ellipsis"><span>... Read more</span></div>
</div>
however my issue is when I try placing the "...read more" using he position relative property it does not work: ex below:
http://jsfiddle.net/ctges45w/3/
As you can see the above fiddle the text is floating in middle of the sentence.
http://jsfiddle.net/ctges45w/5/
how can I make sure that the "read more" text always get placed at the end of the line where its truncated regardless of the width of the content?- something like this:
http://jsfiddle.net/ctges45w/4/
I have tried looking up at sources on stack overflow with similar issue and those havent or partially worked for me: ex here:how to place " ...view more " text next to the truncated sentence in a container-Javscript
any ideas?
Forget all the relative positioning. Just simply put the readmore at the end of the text and style it as you wish.
let summary = document.querySelector('.summary')
if (summary.textContent.length > 100){
let truncated = summary.textContent.substring(0, 100)
summary.innerHTML = `<p style="margin:0;">${truncated}<span class='ellipsis'>... Read more</span></p>`
}
.container {
width: 600px;
border: 1px solid #888;
padding: 0.5em;
line-height: 1.2em;
margin-bottom: 10px;
}
.summary{
overflow: hidden;
height: 47px;
padding: 0;
}
.ellipsis{
color: blue;
}
<div class="container">
<div class="summary">
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
</p>
<ul>
<li>test</li>
<li>test333</li>
</ul>
<p>
<strong>testststststs</strong>
<span class="ellipsis">... Read more</span>
</p>
</div>
</div>
I am trying to close tabs by clicking on the tab label.
I added a click event listener to the label but this doesn't work.
What di I need to do to get this working?
const labels = document.querySelectorAll('label');
const tabs = document.querySelectorAll('[role="tab"]');
labels.forEach(label => label.addEventListener('click', handleLabelClick ))
function handleLabelClick(event) {
tab = event.currentTarget.previousElementSibling;
if (tab.checked = "checked") {
closeAllTabs();
}
}
function closeAllTabs() {
tabs.forEach(tab => {
tab.checked = false;
});
}
/**
* Tabs
*/
.dc-tabs {
display: flex;
flex-wrap: wrap;
}
.dc-tabs label {
order: 1;
display: block;
padding: 1rem 2rem;
margin-right: 0.2rem;
cursor: pointer;
background: #90CAF9;
font-weight: bold;
transition: background ease 0.2s;
}
.dc-tabs .dc-tab {
order: 99;
flex-grow: 1;
width: 100%;
display: none;
padding: 1rem;
background: #fff;
}
.dc-tabs input[type="radio"] {
display: none;
}
.dc-tabs input[type="radio"]:checked + label {
background: #fff;
}
.dc-tabs input[type="radio"]:checked + label + .dc-tab {
display: block;
}
#media (max-width: 20em) {
.dc-tabs .dc-tab,
.dc-tabs label {
order: initial;
}
.dc-tabs label {
width: 100%;
margin-right: 0;
margin-top: 0.2rem;
}
}
/**
* Generic Styling
*/
body {
background: #eee;
min-height: 100vh;
box-sizing: border-box;
padding-top: 10vh;
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
line-height: 1.5;
max-width: 60rem;
margin: 0 auto;
font-size: 112%;
}
<div class="dc-tabs">
<input type="radio" role="tab" name="tabs" id="tabone" checked="checked">
<label for="tabone">Tab One</label>
<div class="dc-tab">
<h1>Tab One Content</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>
</div>
<input type="radio" role="tab" name="tabs" id="tabtwo">
<label for="tabtwo">Tab Two</label>
<div class="dc-tab">
<h1>Tab Two Content</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
<input type="radio" role="tab" name="tabs" id="tabthree">
<label for="tabthree">Tab Three</label>
<div class="dc-tab">
<h1>Tab Three Content</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>
</div>
<script src='./accordion.js'></script>
You need to prevent the default behaviour of the event in handleLabelClick when tabs have to be closed. Otherwise it just closes them and then the click event continues on its way triggers the default behaviour, which is to open the tab for the clicked label.
function handleLabelClick(event) {
const tab = event.currentTarget.previousElementSibling;
if (tab.checked) {
event.preventDefault();
closeAllTabs();
}
}
Notice also that if you want to compare values, you need to use == or ===, the docs will give you more insight.
Additionally, tab.checked returns a boolean, so it is by itself enough for the conditional check.
Finally, I also made tab a constant, in order to avoid it being set as global variable.
This question already has answers here:
Can't scroll to top of flex item that is overflowing container
(12 answers)
Closed 3 years ago.
I am a very new developer attempting to make a presentable photo-gallery-esque type thing to practice a bit. I have been leaning on CSS grid heavily for my layout...and I am pretty proud of what I have thus far.
I have four cards each containing an image thumbnail, a header, and some text. When the user hovers over any card they have the option to "view" the image which brings up a full screen modal. Everything works as I have intended...however...when I decrease the screen size some cards end up disappearing off screen!
I am very new to CSS grid and I have tried just about everything I know at this point. Please help me cross the finish line!
The code below works perfectly if just copy-pasted into the html portion on codepen.io.
Thank you in advance for any help you may offer!
const buttons = document.querySelectorAll('button');
const modal = document.querySelector('.modal');
const image = modal.querySelector('img');
buttons.forEach(button => {
button.addEventListener('click', handleButtonClick);
});
function handleButtonClick(event) {
const card = event.currentTarget.closest('.card');
const chosenImage = card.querySelector('img').src;
image.src = chosenImage;
modal.classList.add('open');
}
document.addEventListener('click', function(event) {
const target = event.target;
const isModal = target.classList[0] === 'modal';
if (isModal) {
modal.classList.remove('open');
}
});
body {
margin: 0;
height: 100vh;
display: grid;
align-content: center;
background: linear-gradient(0deg, rgba(130, 109, 118, 1) 0%, rgba(172, 52, 52, 1) 100%);
}
.wrapper {
display: grid;
grid-gap: 40px;
justify-content: center;
grid-template-columns: repeat(auto-fit, 300px);
grid-template-rows: 450px;
grid-auto-rows: 450px;
}
.card {
border: solid 5px #ac3434;
border-radius: 0.8rem;
overflow: hidden;
background: #3a363670;
display: grid;
grid-gap: 4px;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(8, 1fr);
}
.img-wrapper {
grid-column: 2 / span 3;
grid-row: 2 / span 3;
display: grid;
}
.img-wrapper img {
height: 100%;
width: 100%;
object-fit: cover;
border: solid 3px #ac3434;
border-radius: 50%;
}
.card-body {
grid-column: 1 / -1;
grid-row: 5 / -1;
padding: 0 10px 0;
font-family: 'Ubuntu', sans-serif;
}
.card-body h2 {
font-family: 'Anton', sans-serif;
}
.card-overlay {
grid-column: 1 / -1;
grid-row: 1 / -1;
background: #ac34347a;
display: grid;
place-items: center center;
transform: translateY(100%);
transition: 0.4s;
}
.card:hover .card-overlay {
transform: translateY(0%);
}
.card-overlay button {
background: none;
color: white;
text-transform: uppercase;
position: relative;
bottom: 78px;
border: solid 3px white;
border-radius: 0.4rem;
font-family: 'Ubuntu', sans-serif;
}
.modal {
height: 100vh;
width: 100vw;
position: fixed;
background: #0000008f;
display: grid;
place-items: center center;
/* Make modal invisible until triggered */
opacity: 0;
/* Makes it so the modal does not log click
events */
pointer-events: none;
}
.open {
/* Displays the modal */
opacity: 1;
pointer-events: all;
}
.modal-inner {
width: 500px;
}
.modal-inner img {
width: 100%;
height: 100%;
object-fit: contain;
}
<div class="wrapper">
<div class="card">
<div class="img-wrapper">
<img src="https://picsum.photos/500">
</div>
<div class="card-body">
<h2>Sunny Walls</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim cupiditate molestias sed ea sit, dolore quos itaque consectetur doloribus at. Dolor accusamus consequuntur perspiciatis! Deserunt?
</p>
</div>
<div class="card-overlay">
<button>View ➜</button>
</div>
</div>
<div class="card">
<div class="img-wrapper">
<img src="https://picsum.photos/500">
</div>
<div class="card-body">
<h2>Kit-the-Kat</h2>
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dignissimos quaerat veritatis nobis voluptas minus exercitationem.
</p>
</div>
<div class="card-overlay">
<button>View ➜</button>
</div>
</div>
<div class="card">
<div class="img-wrapper">
<img src="https://picsum.photos/500">
</div>
<div class="card-body">
<h2>Sass in the City</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo accusantium consectetur vel ullam assumenda corrupti id ratione odio, nisi adipisci?
</p>
</div>
<div class="card-overlay">
<button>View ➜</button>
</div>
</div>
<div class="card">
<div class="img-wrapper">
<img src="https://picsum.photos/500">
</div>
<div class="card-body">
<h2>City Things</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint culpa suscipit libero consequatur quod non dolore neque aperiam nihil beatae? Dolores, deserunt.
</p>
</div>
<div class="card-overlay">
<button>View ➜</button>
</div>
</div>
</div>
<div class="modal">
<div class="modal-inner">
<img>
</div>
</div>
You need to use media tags in the css.
Your site is not responsive and when you change screen size it does not resize the components.
https://www.w3schools.com/css/css_rwd_mediaqueries.asp
I'm creating a testimonials page for my site, and some of the testimonials I have are rather long. As such I want to cut out anything longer than 3 lines.
To read the rest, the user clicks a button and the text expands to reveal the rest.
I've managed to do the above with line-clamp, however I'd like to have the ... clickable and styled with a different text. Is this possible?
I couldn't find a way to do that, so I tried a workaround. First I find out which elements have overflow so the [Read More] can be inserted. However it gets inserted at the end of the text, and not before the overflow.
I'm clueless as to how to solve this.
Desired result:
Current result (jsfiddle)
HTML
<div class="testimonial container-fluid">
<div class="col-xs-3 rating-container">
<div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
</div>
</div>
<div class="col-xs-9">
<div class="title">Title</div>
<div class="content">Lorem ipsum dolor sit amet, vero essent gubergren ad pro, regione epicuri contentiones ea mea. Decore omnium id vim, pro et malis molestie, et porro nostro vis. Ei libris debitis reprehendunt est. Te per delenit scaevola scripserit. Partem erroribus rationibus ea vel, nihil euismod ei vim.
His sonet antiopam cotidieque ea, eu unum melius conclusionemque his. Ferri iisque sanctus pri ei. Ut ius tantas nonumy intellegam. Et per solum aliquam, melius elaboraret at qui.</div>
<div class="name_loc" id="demo">Name, London</div>
</div>
</div>
CSS
.testimonial {
width: 920px;
display: -webkit-box;
background-color: #EFEFEF;
color: #333;
padding: 8px;
margin: 0 0 20px;
border: 3px solid #506790;
}
.testimonial:nth-of-type(even) {
background-color: #EFEFEF;
}
.testimonial .rating-container {
height: 144px;
display: table;
}
.testimonial .rating-container > div {
display: table-cell;
vertical-align: middle;
}
.testimonial .star {
width: 32px;
height: 32px;
display: inline-block;
background-repeat: no-repeat;
background-image: url('http://www.timelinecoverbanner.com/cliparts/wp-content/digital-scrapbooking/lemon-star-md1.png');
background-size: cover;
}
.testimonial .title {
font-size: 18pt;
text-align: left;
font-weight: bold;
}
.testimonial .content {
-webkit-box-orient: vertical;
display: -webkit-box;
margin: 5px 0 10px;
font-size: 12pt;
text-align: left;
overflow: hidden;
height: 68px;
}
.testimonial .name_loc {
font-style: italic;
font-size: 12pt;
text-align: right;
}
JS
$('#demo').on('click', function() {
var t = $(this).closest('.testimonial');
var c = t.find('.content');
var h3 = c[0].scrollHeight;
c.animate({'height':h3},500);
});
$('.testimonial').each(function() {
var c = $(this).find('.content');
if (c.height() < c[0].scrollHeight) {
c.css('text-overflow', 'ellipsis');
//also tried:
c.css('line-clamp', '3');
}
});
You could do this with the ::after pseudo-element. I was playing with your fiddle and it seemed to work.
First the .content class needs to be positioned relative. Then add two new CSS classes:
.testimonial .content.collapsed {
/* styles removed from .content */
overflow: hidden;
height: 68px;
}
.testimonial .content.collapsed::after {
display: inline-block;
content: "...";
position: absolute;
bottom: 0;
right: 5px;
}
You could add the click handler to the pseudo element in the JS (I didn't try this part), and then remove the "collapsed" class after the click. You could also style the pseudo element any way you want, or change the content to "Read more..." or whatever.
Try fiddling with your revised JsFiddle. It uses:
$('.testimonial').on(
'click',
'.content',
function(e) {
if (this.scrollHeight > this.offsetHeight) {
$(this).removeClass('clipped')
.addClass('unclipped')
.css('height', this.scrollHeight+'px');
} else {
$(this).removeClass('unclipped')
.addClass('clipped')
.css('height', '');
}
}
);
and 2 extra css-classes: clipped and unclipped:
.clipped:after {
position: absolute;
content: '>';
top: 2.9em;
color: red;
font-weight: bold;
cursor: pointer;
}
.unclipped:after {
position: absolute;
content: '<';
top: 2.9em;
color: green;
font-weight: bold;
cursor: pointer;
}
What about this: http://jsfiddle.net/6nb0wwc3/1/
I added a "readme" div, which on click, shows the complete text.
<div class="name_loc" id="demo">Name, London</div>
<div class="readmore">Read more</div>
$('.readmore').each(function() {
$(this).click( function() {
$(this).parent().find('.content').css('height','100%');
$(this).hide();
});
});
Hope it helps