I have a user-variable string, which can range from one word to a couple sentences (and might contain any valid Unicode character), which I'd like to display within a variable width box.
In code, I'd like HTML that looks like this w/ any other CSS or JS:
<div style="width: 100%; height: 80%" id="text">
<!--<some more divs or something>-->
{{content}}
<!--</some more divs or something>-->
</div>
{{content}} should get bigger when it can be, up to some maximum font size (variable); smaller when it's longer down to some minimum (variable) and then just get cut off after that point.
In either case, I need it to be visually centered and words longer than the box should get hyphenated.
I've tried hacking something together with a combination of flexboxes and JavaScript, but couldn't figure out how to get all the bugs worked out.
Browser support doesn't really matter aside from the latest versions of mobile/desktop Chrome/Safari/Firefox.
Alright I believe this is what you were wanting to accomplish. Code is below with descriptions in the comment blocks. In chrome you'll be using the -webkit-line-clamp property, in firefox you'll be using the fadeout method since firefox doesn't support the clamp property. You can adjust the fadeout in the css to your liking. The "..." at the cutoff point will also still be present in firefox (see the .clamp:after property in the css).
Here is the updated jsFiddle
HTML (To see the changes, just remove the text until one line is shown in the div)
<div id="textparent">
<div id="text">
{{content}} adkf kfjg; ;akdfg fbfbf egdf hajkh
kajfhdg lakjfg kafd gjkahf jahfkjadlfh alkgj akjdhg fkafg
</div>
</div>
CSS
Note: -webkit-line-clamp:3; ( this is the amount of lines you want to be shown)
#text{
width:100%;
position:relative;
height:auto;
text-overflow:ellipsis;
font-size:25px;
line-height:1.1;
display:block;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp:3;
overflow:hidden;
margin:0 auto;
box-sizing:border-box;
}
#textparent{
margin:0 auto;
width:300px;
background:#eee;
top:50px;
padding:10px;
height:auto;
text-align:center;
position:relative;
height:100px;
display:-webkit-box;
-webkit-box-pack:center;
-webkit-box-align:center;
}
/*FIREFOX will make use of the clamp class*/
.clamp:after {
background: linear-gradient(to right, rgba(255, 255, 255, 0), #eeeeee 50%) repeat scroll 0 0 rgba(0, 0, 0, 0);
bottom: 0;
content: "...";
padding: 0 5px 1px 25px;
position: absolute;
right: 0;
}
.clamp {
height: 5.6em;
line-height: 1.4em;
overflow: hidden;
position: relative;
}
Javascript/JQUERY: The main variable you might want to change or play around with is [min_font_size] and [num_line_to_show] although [num_line_to_show] is already set in the CSS.
var t = $('#text');
// get the font-size of the div
var font_size = Number(t.css('font-size').replace('px', ''));
// get the line-height of the div (Note: in Chrome this returns the actual height)
var line_height = Number(t.css('line-height').replace('px', ''));
// minimum height of #text div
//
// Note: if you were in a browser and the line-height var didn't return the full
// height as it does in chrome, you would need to do this:
// var min_h = font-size * line_height
var min_h = line_height;
// number of lines to show. basically just retrieving the "-webkit-line-clamp"
// property in the css, otherwise will default to 3, which you can change.
var num_line_to_show = Number(t.css('-webkit-line-clamp')) || 3;
// the maximum height for the #text div. (the added 5 at the end is just
// personal preference)
var max_h = line_height * num_line_to_show * font_size + 5;
// get the height of the div
var h = $('#text').height();
// set this if you want the font to be set at a minimum size
// when the text is longer than one line
var min_font_size = 20;
Note: you could also try setting the minimum font size dynamically, something like this:
// change this to make the font smaller
var shrink_rate = 3;
var min_font_size = font_size - (Math.round((h/min_h)) * shrink_rate;
Continuing:
// for detecting firefox
var is_ff = navigator.userAgent.toLowerCase().indexOf('firefox');
// if the height of the div is larger than the minimum height, meaning there
// is more than one line now, the font size of the div becomes smaller.
if (h > min_h){
t.css({'font-size' : min_font_size});
// if in Firefox browser
if(is_ff > -1){
// get the new max height of #text based on the number of lines
// with the new minimum font-size
var txt_max_h = ((line_height-font_size) / num_line_to_show) * min_font_size * num_line_to_show;
// the new height is greater than the maximum height allowed for the
// smaller font size
if (t.height() > txt_max_h){
// reset the height of #text div to a fixed height
t.height((min_font_size * num_line_to_show) + 5);
// add the clamp class and css will the rest
t.addClass('clamp');
}
}
}
// if firefox, always run this to center the #text div based on its height
if(is_ff > -1){
t.css({top: ($('#textparent').height() - t.height()) / 2});
}
Hope this helps!
just in time.
See this Fiddle.
I think I succeed to do what you want. It works with Chrome, Firefox and Safari.
HTML :
<div id="container">
<div id="text">my Text !!</div>
</div>
JS :
var maxFontSize=68; // I think we cannot have bigger than that.
var minFontSize=12;
$('#text').on({
// setting an event to resize text
resize:function(e){
// if no text => return false;
if (!$(this).html().trim()) return;
// if already running => return false;
if (this.running) return;
this.running = true;
// get max-height = height of the parent element
var h = $(this).parent().height();
// clone the text element and apply some css
var clone = $(this).clone()
.removeAttr('id')
.css({'font-size':0,
'width':$(this).width(),
'opacity':0,
'position':'fixed',
'left':-1000})
.appendTo($('body'));
// Set the max font size for the clone to fit the max height;
var fontSize = minFontSize;
do {
$(this).css('font-size', fontSize+'px');
fontSize=fontSize+1;
clone.css('font-size', fontSize+'px');
} while (h > clone.height() && maxFontSize > fontSize) ;
// Set the '...' if still bigger
//start by setting back the good size to the clone.
fontSize=fontSize-1;
clone.css('font-size', fontSize+'px');
// while max-height still bigger than clone height
if (h < clone.height() && minFontSize == fontSize) {
var content = clone.html();
// try to remove the last words, one by one.
while (h < clone.height()) {
content = content.replace(/(\s[^\s]*)$/g,'...');
clone.html(content);
}
// then replace the #text content
$(this).html(clone.html());
}
// then remove the clone
clone.remove();
this.running = false;
}
})
.trigger('resize');
There is a cross-browser (IE9+) css centered text and hyphenated for webkit, codepen:
HTML:
<div class="box">
<p>
You can also position your element only in the vertical or horizontal.
This work in IE9+. This text can be also hyphenated.
</p>
</div>
CSS:
.box {
border: #3071a9 solid 1px;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
color: #222;
font-size: 26px;
font-family: arial;
height: 50%;
padding: 20px;
width: 50%;
}
.box p {
text-overflow:ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp:3;
overflow: hidden;
position: absolute;
top: 50%;
-webkit-transform: translate(0, -50%);
-ms-transform: translate(0, -50%);
-o-transform: translate(0, -50%);
transform: translate(0, -50%);
}
Jquery Textfill Plugin by Russ Painter can come handy.
Here is the Fiddle.
<div>
<div>
<label for="dyntext">Content:</label>
<input type="text" id="dyntext" value="Hello!"></input>
</div>
<div>
<label for="maxsize">Maximal font size in pixels?</label>
<input type="text" id="maxsize" value="0"></input>
</div>
<hr />
<div class="content">
<div class="jtextfill">
<span class="dyntextval">Hello!</span>
</div>
</div>
function update() {
var size = parseInt($('#maxsize').val(), 10);
if (!isNaN(size)) {
$('.dyntextval').html($('#dyntext').val());
$('.jtextfill').textfill({debug: true, maxFontPixels: size});
}
}
$(function () {
$('#maxsize').keyup(update);
$('#dyntext').keyup(update);
update()
});
.content .jtextfill {
width: 150px;
height: 100px;
background-color: #fff;
text-align: center;
border:1px solid #333;
padding-top:40px;
padding-bottom:40px;
}
The center part is really easy, you can do this with flexbox, display:table-cell, etc
The font-size part is tricky but it's been answered in the past here: https://stackoverflow.com/a/6112914/1877754
Related
I am trying to create a tooltip element that has a min width of 50px and a max width of 200px. I place the tooltip element inside another element so that I can easily control when the tooltip appears or disappears when there is a hover event on the parent.
The problem that I have is that the tooltip element's width appears to be controlled by the parent's width even though I specified that the child(tooltip) has an absolute position.
let p = document.getElementById( 'parent' );
let b = true;
setInterval( ()=> {
b = !b;
let w = 10;
if( b ) {
w = 300;
}
p.style.width = `${w}px`
}, 5000 );
#parent {
background-color: cyan;
width: 100px;
height: 25px;
position: relative;
transition: width 2s;
}
#tooltip {
position: absolute;
top: calc( 100% + 5px );
left: 5px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="parent">
<div id="tooltip">
My long tooltip text that wraps to multiple lines as needed.
</div>
</div>
I would like the tooltip (yellow div) to keep it's size at 200px in this example, but we can see that when the parent changes width, the tooltip width also changes. Why?
Is there a way to fix this problem?
Clarification: In this example: https://codepen.io/anon/pen/ePPWER we see that the tooltip text looks nice on one line. I don't want the tooltip's div to change its width when the parent changes width, because it forces the tooltip text to wrap onto 2 lines which is undesirable.
If we check the specification related to the width of absolutely positioned element we can read this:
'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
So in your case the width of your element is shrink to fit:
Calculation of the shrink-to-fit width is similar to calculating the
width of a table cell using the automatic table layout algorithm.
Roughly: calculate the preferred width by formatting the content
without breaking lines other than where explicit line breaks occur,
and also calculate the preferred minimum width, e.g., by trying all
possible line breaks. CSS 2.1 does not define the exact algorithm.
Thirdly, calculate the available width: this is found by solving for
'width' after setting 'left' (in case 1) or 'right' (in case 3) to 0.
Then the shrink-to-fit width is: min(max(preferred minimum width,
available width), preferred width).
To make it easy, and without considering the min/max-width, the width of your element will try to fit the content without exceding the width of its parent container (containing block). By adding min/max-width you simply add more constraint.
One idea of fix it to remove positon:relative from the parent element so that it's no more the containing block of the position:absolute element (it will be the initial containing block which is wide enough to avoid the available width constraint).
Then use margin instead of top/left to control the position:
let p = document.getElementById( 'parent' );
let b = true;
setInterval( ()=> {
b = !b;
let w = 10;
if( b ) {
w = 300;
}
p.style.width = `${w}px`
}, 5000 );
#parent {
background-color: cyan;
width: 100px;
height: 25px;
transition: width 2s;
}
#tooltip {
position: absolute;
margin-top: 30px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="parent">
<div id="tooltip">
My long tooltip text that wraps to multiple lines as needed.
</div>
</div>
ID Tooltip is being used under Parent. When parent's width changes, it also suggest that tooltip's total width is changed. Since you have used mix-width and max-width it will expand till it reaches max-width. If you want it to be fixed then simple use width.
It is because the .parent has a position: relative. This will keep all children (position: absolute included) as confined by the parent div.
Not sure if this will work for you because it is pulling the tooltip out of the parent and making it's own with span wrapping the text. Alternatively, you'll need to change the parent from being relative otherwise it'll continually affect the child.
let p = document.getElementById('parent');
let b = true;
setInterval(() => {
b = !b;
let w = 10;
if (b) {
w = 300;
}
p.style.width = `${w}px`
}, 5000);
#parent {
background-color: cyan;
width: 100px;
height: 25px;
transition: width 2s;
position: relative;
}
#root {
position: relative;
}
#tooltip {
width: 100%;
}
#tooltip span {
position: absolute;
top: calc( 100% + 5px);
left: 5px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="root">
<div id="parent"></div>
<div id="tooltip">
<span>My long tooltip text that wraps to multiple lines as needed.</span>
</div>
</div>
There is a div, which has styles:
min-width:100px
min-height:100px
When I add some text to the div(using JavaScript), it changes its size. How to get the new size?
I tried
element.offsetHeight
But it doesn't work as expected. I get size larger then I need in the first time. But after some time, if I get value of this propetry again, I get real size.
Maybe it's because there is some time between setting a text and fitting it into a div? I don't know.
<div class="parent"><div class="popup"></div></div>
<style>
* {
box-sizing:border-box;
}
.parent {
position: absolute;
z-index: 0;
}
.popup {
position: absolute;
min-width: 100px;
min-height: 100px;
padding: 20px;
border-radius: 12px;
background:#000;
color:#fff;
visibility:hidden;
}
.popup:before, .popup::before, .popup:after, .popup::after {
content:"";
width:0px;
height:0px;
display: block;
position: absolute;
}
</style>
<script>
var popup = document.getElementsByClassName('popup')[0];
popup.innerHTML = 'любой текст от 3 до NNN строк';
console.log(popup.offsetHeight);
</script>
You can get the borders of the element, top, bottom, right and left and then subtract them in order to get the width and the height. This accounts for the actual pixels on the page after it has been modified:
var width = [element].getBoundingClientRect().right - [element].getBoundingClientRect().left ;
var height = [element].getBoundingClientRect().bottom - [element].getBoundingClientRect().top ;
Change this in your script. Notice that I added more text in innerHTML.
<script>
var popup = document.getElementsByClassName('popup')[0];
console.log("Before: "+popup.offsetHeight);
popup.innerHTML = 'любой текст от 3 до NNN строк<br><br>moretext';
console.log("After: "+popup.offsetHeight);
</script>
And change in your CSS the min-height to smaller value to see the difference
min-height:10px;
output in console is:
Before: 40
After: 180
Here is an example that works (open console to see the output): https://jsfiddle.net/_jserodio/q78dLz8h/
I'm trying to gradually fade in the scroll-bar. Currently, how I am making the scroll-bar appear is by adding a class to the body that changes the overflow to auto, but it looks very jerky / abrupt.
Here is the JS code that abruptly adds the class that shows the scroll-bar:
var bodywidth = $('body').width();
var scrollwidth = 10;
$('body').mousemove(function(e){
var x = e.pageX - this.offsetLeft;
if(x>bodywidth-scrollwidth)
$('body').addClass("auto");
else
$('body').removeClass("auto");
});
And here is the CSS corresponding to those clases:
body
{
margin:0;
overflow:hidden;
}
.auto
{
overflow:auto;
}
How can I make this transition less abrupt? Is there a better way to do it that adding the class and removing the class.
The scrollbars can be customized via -webkit-scrollbar, but this can not be animated (or at least I didn't succeded at it), and support in other browser is poor.
An alternative is to set a div just over the scrollbar, make it the same color than the base div, and make it gradually transparent to show the scrollbar
the html is:
<div class="container">
<div class="base">
<p>aaa aaaaa aaaaa aaaaaaaa aaaa aaa aaa aaaa bbbbbb bbbbbb cccc cccc cccc
</p>
</div>
<div class="hide">
</div>
</div>
the CSS is:
.base {
width: 100px;
background-color: white;
top: 0px;
position: absolute;
height: 130px;
overflow: hidden;
padding-right: 20px;
}
.base.clipped {
overflow: auto;
}
.hide {
position: absolute;
width: 19px;
height: 100%;
right: 0px;
background-color: white;
top: 0px;
-webkit-transition: all 2s;
z-index: 10;
}
.hide.clipped {
background-color: transparent;
}
I am keeping the class of the elements all the time, but adding a second class clipped to both. I set a padding in the element that will have the scrollbars so that there space for it without rearranging the layout. The hide element can be transitioned with css, the overflow not.
the javascript is
$("*").click(function(){
var obj = $(".base");
var hid = $(".hide");
if (obj.hasClass("clipped")) {
hid.removeClass("clipped");
setTimeout(function() {
obj.removeClass("clipped");
}, 2000);
} else {
hid.addClass("clipped");
obj.addClass('clipped');
}
});
demo
how to display a small number (left top border) above a border around an element?
basically I am looking for a way to highlight elements and identify them with a number.
UPDATE:
Basically the elements highlight on mouseover by having border property defined. on mouseout, border is transparent (disappears).
what I'd like to do is how to display a number outside of the border highlight selected?
Solution I just thought about is using a custom generated numbered image border and just use border-image dynamically
Assuming you want to insert this number dynamically for any element without having to worry about relatives and absolutes here is a solution.
Code:
<div id="box"></div>
#box{
background:red;
height:140px;
width:250px;
margin:30px;
}
.number{
background:#ccc;
display:block;
font-weight:bold;
border:1px solid #000;
font-size:10px;
}
// Create an span element that will have the number
var number = $("<span />").text("5").addClass("number");
// Set the number width and height
var numberWidth = 10;
var numberHeight = 10;
// Get the target element
var element = $("#box");
// Get width, height and position
var elementWidth = element.width();
// These two are only necesary if you want to position
// the number in a different corner (e.g bottom, right)
var elementHeight = element.height();
var elementPosition = element.offset();
// Apply css to the number element
// Position is based on the target element position
number.css({
position: "absolute",
left: elementPosition.left - numberWidth,
top: elementPosition.top - numberHeight,
width: numberWidth,
height: numberHeight
});
// Insert the number to the body
number.appendTo("body");
If the identification numbers are constant and you don't want to include them in the markup as content, you can include them as attribute values and use :before pseudo-elements:
CSS:
p { position : relative }
p:hover:before {
content : attr(title);
position : absolute;
left : 0;
top : -1em
}
HTML:
<p title="1">blah blah</p>
<p title="2">blah blah</p>
<p title="3">blah blah</p>
If the identification numbers can be generated each time the page is rendered, you can use an automatic counter instead:
CSS:
p {
position : relative;
counter-increment : idnum
}
p:hover:before {
content : counter(idnum);
position : absolute;
left : 0;
top : -1em
}
See here for more information: http://www.w3.org/TR/CSS2/generate.html
What do you mean by "highlight"? Mouseover? You can provision for such visual effects by first wrapping your element with another div which will hold your number. And example:
<style>
.container:hover .label {
visibility: visible;
}
.label {
visibility: hidden;
}
.content {
margin: 1px;
border: none;
}
.content:hover {
margin: 0px;
border: 1px solid black;
}
</style>
<div class="container">
<div class="label">
123
</div>
<div class="content">
My Element
</div>
</div>
Edited to only display border on mouse over.
In this example <img> is re-sizing as I want
Edit: re-added http://jsfiddle.net/jitendravyas/yBkFL/
But I also want to re-size Discount tag and text. if I re-size the browser window to small size then Discount tag 20% and text "Good Product" should also re-size.
I need compatibility with IE8 and 9 and latest firefox, Chorme and iPad safari
I would like to do with CSS only but if it's not possible then use of javascript/jquery is ok. Compatibility is important.
Important part of HTML and CSS
HTML (edited)
<ul>
<li>
<span class="twenty-percent">
<span class="rotate">20%</span>
</span>
<img src="image.jpg">
<span class="description">Good Product</span>
</li>
</ul>
CSS (Edited:)
li {
float: left;
width: 18%;
padding: 0;
position: relative;
float: left;
margin: 15px;
background: yellow;
display: inline }
img { width: 100%; }
li img { display: block; }
.twenty-percent {
content: '';
background:url(http: //i.imgur.com/9cfK5.png) no-repeat;
position: absolute;
right: -15px;
top: -2px;
width: 45px;
height: 45px; }
.description {
display: block;
font-size: 120%; }
.rotate {
color: #FFFFFF;
font-size: 16px;
font-weight: bold;
padding-right: 10px;
position: absolute;
right: -5px;
text-shadow: 0 1px 1px #000000;
top: 9px; }
I don't know of a pure CSS way to do it, but with a bit of jQuery, it's possible as follows:
/* Original width of the description element */
var origWidth = $('.description').width();
/* Function to set the font size of description. Based on its current width
compared to its original width. The * 70 is a factor you can use to increase
or decrease the calculated font size. */
var setFontSize = function(){
var $desc = $('.description');
$desc.css({fontSize: $desc.width()/origWidth * 70 + "%"});
}
/* Set the font size on load and when the viewport resizes */
setFontSize();
$(window).resize(setFontSize);
Here's a demo of it.
Notes
Above assumes all .description elements are same width.
You could improve the appearance of the script by hiding the text until the font size was calculated on load. This would prevent the font size jump you see before the first call to setFontSize().
If you wanted to improve the font size ratio (rather than rely on the hardcoded * 70 factor), you could use a function on load that calculated the maximum possible space for text in your .description. Then you could use this value to determine the factor.
Updated: New version of the code that resizes the green percentage discount text and image as well.
HTML
<ul>
<li>
<!-- Green circle background has been changed to an <img>. Now it
can be scaled as the size changes. It is absolutely positioned
behind the percentage text. -->
<span class="percent">
<img src="http://i.imgur.com/9cfK5.png"/>
<span class="rotate">20%</span>
</span>
<img src="http://lorempixum.com/100/200/">
<span class="description">Good Product</span>
</li>
<!-- Remaining items -->
</ul>
CSS
/* Container that holds percent text and green circle <img> */
.percent {
content: '';
position:absolute;
right:-15px;
top:-2px;
height: 45px;
width: 45px;
/* Vertically center percent text */
line-height: 45px;
font-size: 100%;
}
/* Green circle image */
.percent img {
/* Grow/shrink as .percent size changes */
width: 100%;
height: auto;
/* Absolutely position behind percentage text */
position: absolute;
z-index: 1;
top: 0;
left: 0;
}
.rotate {
/* Positions percent text above green circle <img> */
position: relative;
z-index: 2;
/* Keeps text centered as size changes */
display: block;
text-align: center;
/* remaining rules */
}
jQuery
/* Get common element references */
var $desc = $('.description');
var $percent = $('.percent');
/* Store the original values. We'll use these to smoothly change the size */
var orig = {
description: $desc.width(),
percent: $percent.width(),
right: parseInt($percent.css('right'), 10)
};
/* Sets font sizes and changes green circle image size and position */
var setFontSize = function(){
/* Get the percent of size change */
var change = $desc.width()/orig.description;
/* Update the .description font-size */
$desc.css({
fontSize: change * 100 + "%"
});
/* Update the following .percent properties:
1. font-size
2. width: to grow/shrink green circle image
3. line-height: to keep percent text vertically centered
4. right: keeps .percent container in the same proportional position */
$percent.css({
fontSize: change * 100 + "%",
width: orig.percent * change + "px",
lineHeight: orig.percent * change + "px",
right: orig.right * change + "px"
});
}
/* Set the font size on load and when the viewport resizes */
setFontSize();
$(window).resize(setFontSize);
Demo of it in action.
You could create two css classes that describe the two sizes/versions that you want. You can use jquery to detect the browser size and add and remove the appropriate css classes to your liking.
Background images do not rescale themselves like images (<img> tags). Change the 20% logo to an <img>, then size it as you do the larger image. Fonts can be sized as percents, so try setting the font-size to 120%.
To resize text, create the block of whichit is contained within in CSS. for example: Body, Main, Footer etc. then simply use the font-size command. Like so
body {
Font-size:1.1EM;
}
For the image, simply set the dimension which the want the image to be, although I recommend making the image file smaller, since this will save memory space.