Multiline text overflow (dotdotdot): wrap only word - javascript

I have such example of my code:
<div class="container">
<div class="item n1">Proe Schugaienz</div>
<div class="item n2">Proe Schugaienz</div>
</div>
and i use such jQuery code:
$('.item').dotdotdot({
wrap: 'word',
fallbackToLetter: false
})
and css:
.item {
margin: 5px;
background: red;
padding: 2px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.n1 {
width: 8px;
}
.n2 {
width: 80px;
}
but as result i get:
as result i want achieve this:
is it possible with pure css or with dotdotdot.js?
if word (sentence) cannot match it's parent: then show only default one-line-text-overflow
if word (sentence) is able to fit parent word-by-word - then match it, no letter-hyphenation!
so i don't wanna my container to be extented by height (i have a lot of data, it's only an example, i could not hardcode some blocks)
https://plnkr.co/edit/IxS0CReJicRfDdeGpoPo?p=preview

you can use flex
<div class="container">
<span></span>
<em></em>
</div>
.container {
display: flex;
justify-content: center; /* centers content horizontally*/
align-items: center /* centers content vertically*/
}

Here is a proposal that prevents the word break - there are changes in the markup as well:
I'm using flexbox container item for the div inner that has dotdotdot applied - this ensures two things:
a. inner takes the width of the content
b. word-wrap: break-word applied by dotdotdot will not break the word
Now it is possible to detect when word break or overflow can happen - this would be when inner width exceeds item width.
So we can detect this in the callback of dotdotdot!
When words starts breaking, you need ellipsis - so replace the text with ... just like dotdotdot does.
See demo below:
$('.inner').dotdotdot({
wrap: 'word',
watch: 'window',
fallbackToLetter: false,
callback: function(isTruncated) {
// this detects 'break-word'
if($(this).outerWidth() > $(this).closest('.item').outerWidth()) {
$(this).text('...');
}
}
});
.item {
margin: 5px;
background: red;
padding: 2px;
display:flex;
height: 100px;
}
.n1 {
width: 12px;
}
.n2 {
width: 80px;
}
.n3 {
width: 150px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery.dotdotdot/1.7.4/jquery.dotdotdot.js"></script>
<div class="container">
<div class="item n1">
<div class="inner">Proe Schugaienz.</div>
</div>
<div class="item n2">
<div class="inner">
Proe Schugaienz. More text here. More text here. More text here.
</div>
</div>
<div class="item n3">
<div class="inner">
Proe Schugaienz. More text here. More text here. More text here.
</div>
</div>
</div>

You should set display: inline-block; and line-height: 26px; (or any suitable value which you find suitable) to .n1 class and also increase the width to 16px (i.e. enough width to accomodate two letters). So the final css should be as follows:
.item {
margin: 5px;
background: red;
padding: 2px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.n1 {
line-height: 26px;
width: 16px;
}
.n2 {
width: 80px;
}
Also remove the lines of in script.js which u were using to achieve the same result. It should work for you.

I think easiest solution for this is css "text-overflow:elipsis".
You can use below css snippet for desired result.
.item.n1 {width: 20px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;}
Note that; width is your turning point for dots.

To the element you want to show with a ... Apply the following code
.example-element{
max-width: example-width;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
You can adjust the width at the maximum you want.

Since you are already using JS, this is how I would do it without any library/plugin.
const items = document.querySelectorAll('.item')
const log = document.querySelector('#log')
const MIN_WIDTH = 32
// Check if the elements are smaller than the MIN_WIDTH
// and apply a class to hide the text and show an ellipsis.
for (let item of items) {
if (item.clientWidth < MIN_WIDTH) {
item.classList.add('hidden')
}
}
.item {
background-color: red;
text-overflow: ellipsis;
overflow: hidden;
margin-bottom: 0.5rem;
padding: 0.2rem;
}
.n1 {
width: 1.5rem; // 24px
}
.n2 {
width: 6rem;
}
/*
This will hide the text and display an ellipsis instead
*/
.hidden {
position: relative;
white-space: nowrap;
}
.hidden::before {
content: '...';
display: block;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: red;
text-align: center;
}
<div class="container">
<div class="item n1">Proe Schugaienz</div>
<div class="item n2">Proe Schugaienz</div>
</div>
If you plan on changing the size of the elements, you can wrap the loop inside a function, and then call it when needed.

$.fn.overflow = function () {
return this.each(function () {
if ($(this)[0].scrollWidth > $(this).innerWidth()) {
$(this).css('overflow', 'hidden').css('text-overflow', 'ellipsis').css('white-space', 'nowrap').css('min-width', '16px');
}
});
};
$(function () {
$('.item').overflow();
});
.item {
margin: 5px;
background: red;
padding: 2px;
overflow: auto;
word-wrap: break-word;
}
.n1 {
width: 8px;
}
.n2 {
width: 80px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="item n1">Proe Schugaienz</div>
<div class="item n2">Proe Schugaienz</div>
</div>

I agree to the answers provided before adding text-overflow: ellipsis; to the div's does work even if content does not overflow. I believe that you want to add the ellipsis at the beginning of the content which can be achieved by "reverse ellipsis" and without the help of any js code you can achieve this kind of output Here's a plnkr that I have created for you:
https://plnkr.co/edit/TwotVdtGMaTi6SWaQcbO?p=preview
.box {
width: 250px;
border: 1px solid silver;
padding: 1em;
margin: 1em 0;
}
.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.reverse-ellipsis {
/* Your move. */
text-overflow: clip;
position: relative;
background-color: #FFF;
}
.reverse-ellipsis:before {
content: '\02026';
position: absolute;
z-index: 1;
left: -1em;
background-color: inherit;
padding-left: 1em;
margin-left: 0.5em;
}
.reverse-ellipsis span {
min-width: 100%;
position: relative;
display: inline-block;
float: right;
overflow: visible;
background-color: inherit;
text-indent: 0.5em;
}
.reverse-ellipsis span:before {
content: '';
position: absolute;
display: inline-block;
width: 1em;
height: 1em;
background-color: inherit;
z-index: 200;
left: -.5em;
}
body {
margin: 1em;
}
HTML
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<p>The goal would be to add ellipsis on the beginning and show the end of the content. Any idea?</p>
<div class="box ellipsis reverse-ellipsis"><span>Here is some long content that doesn't fit.</span></div>
<div class="box ellipsis reverse-ellipsis"><span>Here is some that does fit.</span></div>
</body>
</html>
All you need to do is add an extra element within .reverse-ellipsis (here a span); I hope this might help you.

Related

HTML - Fitting images in multiple rows to fit viewport

I am developing a website, in which I display N amount of images. A good example of how I would like to display them is how DeviantArt does it. Images are shown in rows in such a way that it fills the width of the current viewport and does not deform any images.
My attempt was the following:
#CSS
.item {
display: inline-block;
vertical-align: top;
position: relative;
overflow: hidden;
padding: 0.5em;
background: lightcoral;
border: black solid 1px;
}
.item img{
max-height: 200px;
object-fit: scale-down;
}
HTML
<div style="display: block; width: 100%">
<!-- A vue directive, used in this example to render this element n amount of times per images -->
<div class="item" v-for="(i, index) in images" :key="index">
<img :src="i.url">
<div style="display: flex; flex-direction: column;">
<div>{{i.title}}</div>
<div>By User1234</div>
</div>
</div>
</div>
Which results in the following:
As you can see, there are gaps left at the end of each row. Id like for each row to be able to fit all possible images so that the grid fits the viewport, like this:
Im very interested to know how I can achieve this, either by using pure HTML / CSS or Javascript if needed.
You probably want to use flexbox with flex-grow.
CSS-Tricks has a great article on this here: https://css-tricks.com/piecing-together-approaches-for-a-css-masonry-layout/
Here's a codepen from the article:
codepen
body {
margin: 0;
padding: 1rem;
}
.masonry-with-columns {
display: flex;
flex-wrap: wrap;
div {
height: 150px;
line-height: 150px;
background: #EC985A;
color: white;
margin: 0 1rem 1rem 0;
text-align: center;
font-family: system-ui;
font-weight: 900;
font-size: 2rem;
flex: 1 0 auto;
}
#for $i from 1 through 36 {
div:nth-child(#{$i}) {
$h: (random(400) + 70) + px;
width: $h;
}
}
}

Make content overflow

I'm trying to make something where I need to duplicate all the entries (multiple times) and then later I would like to make it spin and land on a colour slowly, etc. I'm now just getting stuck at duplicating the colours, how can I make it so the new colours are overflowing, without doubling the width?
I want it so that the colours go out of the wrapper div. Now they are just distributing themselves.
Any ideas?
$(document).on("click", ".duplicate", function() {
var $wrapper = $('.wrapper .inner');
$wrapper.find('.color').each(function() {
$wrapper.append($(this).clone());
});
});
.wrapper {
width: 75%;
margin: 12px auto;
height: 26px;
border-radius: 6px;
overflow: hidden;
position: relative;
}
.wrapper .inner {
width: 100%;
height: 100%;
position: absolute;
display: flex;
}
.wrapper .color {
width: 100%;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="inner">
<div class="color" style="background:red;width:231%"></div>
<div class="color" style="background:purple;width:111%"></div>
<div class="color" style="background:orange;width:91%"></div>
</div>
</div>
<button class='duplicate'>
Duplicate
</button>
In order to have two items in the same position in document flow you need to wrap them in a parent with position:relative and give one of them position:absolute; top:0;left:0. Also note that if your element doesn't have any content, you might need to define it's height and width. To make it same size as parent, you can give it top:0;bottom:0;left:0;right:0;.
Here's a demo started from your fiddle. You might want to inspect DOM after you press "Duplicate". I made it revert to original, so you can do it multiple times.
But do note your question is currently unclear. I'm afraid you lost me at "to make it spin and land on a colour slowly". It's truly poetic, but won't get you very far on SO...
I guess you are simply over complicating this. All what you need is a reapeated linear-gradient like this:
.wrapper {
width: 75%;
margin: 12px auto;
position: relative;
}
.wrapper .inner {
width: 100%;
height: 25px;
display: flex;
border-radius: 6px;
overflow: hidden;
}
.wrapper .color {
width: 100%;
height: 100%;
}
.new {
margin-top:5px;
height:25px;
border-radius: 6px;
background-image:linear-gradient(to right,red,red 54%,purple 54%, purple 80%,orange 0);
background-size:100% 100%;
animation:change 5s linear infinite alternate;
}
#keyframes change {
from {
background-position:0 0;
}
to {
background-position:-1000px 0;
}
}
<div class="wrapper">
<div class="inner">
<div class="color" style="background:red;width:231%"></div>
<div class="color" style="background:purple;width:111%"></div>
<div class="color" style="background:orange;width:91%"></div>
</div>
<div class="new"></div>
</div>

How can i get 2 classes by id inside the same function

I need to target two div elements and toggle their classes simultanouesly.
I understand that I can get multiple divs "by ID" by using .querySelectorAll
but when I get to .classlist.toggle ("NewClassName"); how can I target two classes??
So here's some code:
#small-div{
background-color:#aaaaaa;
border: 3px solid #aaaaaa;
padding: 0px 0px 0px 0px;
margin: auto 10px auto auto;
border-radius: 10px;
overflow: auto;
}
.tobetoggled{
width: 45%;
float: left;
}
#small-div2{
background-color:#aaaaaa;
border: 3px solid #aaaaaa;
padding: 0px 0px 0px 0px;
margin: auto 10px auto auto;
border-radius: 10px;
overflow: auto;
}
.tobetoggled2{
width: 45%;
float: right;
}
.toggletothis{
width: 100%;
float: left;
position: fixed;
display: block;
z-index: 100;
}
.toggletothis2{
width: 100%;
float: left;
position: fixed;
display: block;
z-index: 100;
}
.whensmalldivistoggled{
display: none;
}/* when small-div is clicked, small-div toggles to class "tobetoggled" while small-div 2 simultaneously toggles to class "whensmalldivistoggled" (the display none class) */
<div id="container">
<div class="tobetoggled" onclick="function()" id="small-div">
</div>
<div class="tobetoggled2" onclick="separatefunction()" id="small-div2">
</div>
</div> <!-- end container -->
<script>
function picClicktwo() {
document.querySelectorAll("small-div, small-div2").classList.toggle("toggletothis, whensmalldivistoggled");
}
</script>
So as you can see one div is on the right, the other is on the left, each set to 45% width. So if I toggle one div to 100% width the browser still respects the other divs space instead of taking the whole 100%.
So I'm thinking if I can get the div on the right ,for example, to not display when the div on the left is toggled, it will be out of the way so the left div can take all 100%
Maybe im going about this the wrong way. Any help is welcome. Thanks.
You can create a single javascript function that sets appropriate classes on each element. Since you have only two elements it is not too complex.
HTML
<div id="container">
<div id="lefty" onclick="toggle('lefty', 'righty')">Lefty</div>
<div id="righty" onclick="toggle('righty', 'lefty')">Righty</div>
</div>
JS
function toggle(target, other)
{
var t = document.getElementById(target);
var o = document.getElementById(other);
if (!t.className || t.className == "inative")
{
t.className = "active";
o.className = "inactive";
}
else
{
t.className = "";
o.className = "";
}
}
CSS
#container {
background-color: lightgreen;
padding: 15px 0;
}
#container div {
color: white;
width: 45%;
display: inline-block;
}
#lefty {
background-color: blue;
}
#righty {
background-color: purple;
}
#container div.active {
width: 90%;
}
#container div.inactive {
display:none;
}
https://jsfiddle.net/dLbu9odf/1/
This could be made more elegant or capable of handling more elements with something like toggle(this) and then some DOM traversal and iteration in javascript, but that's a bit beyond scope. If that were the case I would recommend jQuery.

Show one element if another is above a certain height

I have a following HTML:
<span class="day-number">{{day-number}}</span>
<div class="event-box">
<div class="event-container">
</div>
<div class="more-events">more ...</div>
</div>
Event-container is filled with an unknown number of .event elements like the following:
<div class="event">{{event-name}}</div>
I want to show or hide the .more element based on if the .event-container has a height of over 76px (equal to the height of four .event elements stacked).
The styling for the above elements:
.event {
text-align: left;
font-size: .85em;
line-height: 1.3;
border-radius: 3px;
border: 1px solid #3a87ad;
background-color: #3a87ad;
font-weight: normal;
color: whitesmoke;
padding: 0 1px;
overflow: hidden;
margin-bottom: 2px;
cursor: pointer;
}
.event-box {
max-height: 76px;
overflow: hidden;
position:relative;
}
.event-box .more-events {
height: 10px;
position: absolute;
right: 0;
bottom: 10px;
display: none;
z-index: 5;
}
No styling for .event-container
I can do what I want with Javascript (jQuery):
$(".event-box").each(function(){
var $this = $(this);
if($this.children(".event-container").height() > 76){
$this.children(".more-events").css("display", "block");
} else {
$this.children(".more-events").css("display", "");
}
});
And run that every time a make a change, but I'd rather do it with CSS.
Is this possible? Maybe with pseudo elements or media queries or something?
JSFIDDLE: http://jsfiddle.net/pitaj/LjLxuhx2/
If changing the markup is acceptable there is a possibility to achieve a somewhat similarly looking page without using JavaScript to show or hide, here is the Fiddle
I have removed <div class="more-events">more ...</div> line and made elements of event class to get hide when it is necessary I also made them to appear when hovering over more ... .
The CSS I have added:
.event:nth-child(n){
display: none;
}
.event:nth-child(1),.event:nth-child(2),.event:nth-child(3),.event:nth-child(4){
display: block;
}
.event:nth-child(5){
text-indent: -9999px;
position: relative;
display: block;
color: black;
border: none;
background-color: #FFF;
}
.event:nth-child(5)::before{
position: absolute;
text-indent: 0px;
content: "more ...";
display: block;
}
.event:nth-child(5):hover{
position: static;
text-indent: 0;
border: 1px solid #3a87ad;
background-color: #3a87ad;
color: whitesmoke;
}
.event:nth-child(5):hover::before{
display:none;
}
.event:nth-child(5):hover ~ .event:nth-child(n){
display: block;
}
And for .event-box class I have commented out max-height: 76px; because in my browser 76px was not equal to the height of four .event elements stacked. Also removed update function.
I dont think it's possible using css only. but for better approach in what you are trying to do.instead of using max-height for .event-box I use this css which is add display:none to +4.event on your event container:
.event-box .event-container .event:nth-child(n+5){
display: none;
}
and now when it's more than 4 .event your more text appears. FIDDLE
UPDATE:
HERE I make little change in you js as well and make it more professional,
while you are using template to render the page, maybe you can do it as follow
<div class="event-container">
{{#each events}}
<div class="event">{{event-name}}</div>
{{/each}}
</div>
{{#if canshowmore}}
<div class="more-events">more ...</div>
{{/if}}
and
function canshowmore() {
return events.length >= 4;
}

How do I scale this layout?

Been working on this layout for some time now and each way I take I run into some sort of obstacle (v1 of this here: https://stackoverflow.com/questions/14572569/how-can-i-contain-pos-abs-div-within-specific-area)
What I'm trying to do now is to have the size of .spread adapt to the browser windows width and height, so it'll never exceed what the user currently can see in their browser (.spread currently have fixed width/height, for demo purposes). The ideal would to be able to resize on the fly and it adapts instantly (i.e. no media queries).
It works as it should in the v1 version I link to above, but then I had problems with the fade effect due to that .spread lacked an actual width/height.
Here's the new demo:
http://jsbin.com/uciguf/1
UPDATE: The markup can be changed as long as it works as described.
<div class="scrollblock" id="scroll_spread-1">
<div class="action"><!-- --></div>
<!-- -->
</div>
<div class="scrollblock" id="scroll_spread-2">
<div class="action"><!-- --></div>
<!-- -->
</div>
<div class="contentblock" id="spread-1">
<div class="inner windowwidth windowheight">
<div class="content">
<span></span>
<div class="spread">
<div class="fade"><!-- --></div>
<div class="left centerimage">
<img src="http://s7.postimage.org/8qnf5rmyz/image.jpg">
</div>
<div class="right centerimage">
<img src="http://s7.postimage.org/kjl89zjez/image.jpg">
</div>
</div>
</div>
</div>
</div>
<div class="contentblock" id="spread-2">
<div class="inner windowwidth windowheight">
<div class="content">
<span></span>
<div class="spread">
<div class="fade"><!-- --></div>
<div class="left centerimage">
<img src="http://s7.postimage.org/5l2tfk4cr/image.jpg">
</div>
<div class="right centerimage">
<img src="http://s7.postimage.org/fjns21dsb/image.jpg">
</div>
</div>
</div>
</div>
</div>
html {
height: 100%;
}
body {
background: #eee;
line-height: 1.2em;
font-size: 29px;
text-align: center;
height: 100%;
color: #fff;
}
.scrollblock {
position: relative;
margin: 0;
width: 100%;
min-height: 100%;
overflow: hidden;
}
.contentblock {
margin: 0;
width: 0;
min-height: 100%;
overflow: hidden;
position: fixed;
top: 0;
right: 0;
}
.contentblock .inner {
z-index: 2;
position: absolute;
right: 0;
top: 0;
background: #eee;
}
.fade {
width: 100%;
height: 100%;
position: absolute;
right: 0;
top: 0;
background-color: #000;
opacity: 0;
z-index: 3;
}
.content {
height: 100%;
}
.content span {
height: 100%;
vertical-align: middle;
display: inline-block;
}
.content .spread {
vertical-align: middle;
display: inline-block;
}
#spread-1 {
color: #000;
z-index: 105;
}
#spread-2 {
z-index: 110;
}
.spread {
max-height: 800px;
max-width: 1130px;
position: relative;
}
.spread .left {
position: relative;
width: 50%;
float: left;
text-align: right;
height: 100%;
}
.spread .right {
position: relative;
width: 50%;
float: left;
text-align: left;
height: 100%;
}
div.centerimage {
overflow: hidden;
}
div.centerimage img {
max-width: 100%;
max-height: 100%;
}
div.centerimage span {
height: 100%;
vertical-align: middle;
display: inline-block;
}
div.centerimage img {
vertical-align: middle;
display: inline-block;
}
P.S. The title is really bad, don't know what I'm looking for, but please change to something more informative if you can think of anything better.
Three-Quarters of the Way to a Full Solution
This is not quite a full solution yet, as it cannot accommodate a super narrow width window size (like your old version did). However, it is a good step toward what you seek.
Here is the example.
The key things that have been changed:
Added
.spread { height: 93%; } /* You had originally wanted a height difference */
Removed
overflow: hidden from div.centerimage.
width: 50% from .left and .right.
maybe you could just pin your .spread divisor
.spread {
bottom: 11px;
left: 11px;
right: 11px;
top: 11px;
position: absolute;
/* ... */
}
This way, it will be resized the same of the viewport area.
Here a jsFiddle to demonstrate.
Carry on
I know you were probably looking for a solely CSS/HTML solution, but really you're probably best off using some Javascript. There's no way to be clean and precise just using CSS & HTML.
But if you run a tiny bit of JavaScript on page load and window-resize, then your divs can have actual height/width values and scale cleanly.
The trick is to have the outside div get its width/height set by the JavaScript, and then all its children use % dimensions so they grow appropriately.
Here's the basics using some JQuery:
<script type="text/javascript">
//Function to get the current window dimensions.
function get_window_dims() {
var dims = [];
if (parseInt(navigator.appVersion)>3) {
if (navigator.appName=="Netscape") {
dims[0] = window.innerWidth;
dims[1] = window.innerHeight;
}
if (navigator.appName.indexOf("Microsoft")!=-1) {
dims[0] = document.body.offsetWidth;
dims[1] = document.body.offsetHeight;
}
}
return dims;
}
function ResizeDivs {
var dims = get_widnow_dims();
var div_width = Math.floor(dims[0] * 0.93); // calculating the div width to be 93% of the window width
$('div.spread').css('width',div_width+'px');
}
$(function() {
ResizeDivs();
$(window).resize(function(){
ResizeDivs();
});
});
</script>
You could easily clean up this code to be more concise, but I figured I'd put it out here this way for you to see all the parts.
If you wanted to spend the extra time, you could even add more JQuery to animate the divs when the window resizes.
Hope this helps.
Have you considered using a responsive framework to solve your issue? You can set width's and heights to percentages and have min-width, min-height.

Categories

Resources