Rotating table header text with CSS transforms - css

This looks like it should be possible with the following:
.verticalText
{
/* IE-only DX filter */
writing-mode: tb-rl;
filter: flipv fliph;
/* Safari/Chrome function */
-webkit-transform: rotate(270deg);
/* works in latest FX builds */
-moz-transform: rotate(270deg);
}
This works in IE.
It goes wrong in a bizarre way in Safari, Chrome and FX - the cell's size is calculated before the text is rotated!
Here is a demo: http://jsfiddle.net/HSKws/
I'm using dynamic images as a workaround, although that also has its problems. I'm happy with that as a fall-back, but it seems like there should be a way to make this CSS work - it's almost there.
Anyone know a way to make the cells fit the content after the transform has been applied?

‘transform’ alters the orientation of the entire element you declare it on, not the text content inside it. It's more like IE's ‘matrix’ property than ‘writing-mode’.
Crucially, transforming an element doesn't change how its content size is calculated (or how its parent's layout is affected by that size). CSS's algorithms for vertical and horizontal sizing are different and difficult enough to get right to being with; there's no real consistent way they could accomodate content with arbitrary rotation. So ‘transform’ is like using ‘position: relative’: it changes where the content is rendered, but not anything to do with layout size.
So if you want to include one in a table you'll need to set the cell's ‘height’ explicitly to accomodate the expected rotated ‘width’. If you don't know that in advance you could potentially hack it up with JavaScript, perhaps.
FWIW: for me on Fx3.1b3 the span is also rotated like the others. However on Windows with its horizontal-only anti-aliasing (ClearType) the rendering doesn't look great... a well-rendered image could come out considerably better.

It's possible using inline SVG in a XHTML document (I only tested Safari and Firefox):
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<table border="1">
<tr>
<td> </td>
<td>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="150">
<text id="thetext" transform="rotate(270, 12, 0) translate(-140,0)">Example column header</text>
</svg>
</td>
<td>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="150">
<text id="thetext" transform="rotate(270, 12, 0) translate(-140,0)">Example column header</text>
</svg>
</td>
<td>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="150">
<text id="thetext" transform="rotate(270, 12, 0) translate(-140,0)">Example column header</text>
</svg>
</td>
</tr>
<tr>
<td>Example row header</td>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
</table>
</body>
</html>
Unfortunately, you do have to explicitly set the width and height of your table cells and the translation of the text rendered using SVG. Also, the file extension must be xhtml.

Webkit has added:
-webkit-writing-mode:vertical-rl;
Which you can apply to a div.

As I answered on a similar question, I solved it this using a jQuery plugin by David Votrubec and the comment by Mike below the blog post.
Put this in a .js-file:
(function ($) {
$.fn.rotateTableCellContent = function (options) {
/*
Version 1.0
7/2011
Written by David Votrubec (davidjs.com) and
Michal Tehnik (#Mictech) for ST-Software.com
*/
var cssClass = ((options) ? options.className : false) || "vertical";
var cellsToRotate = $('.' + cssClass, this);
var betterCells = [];
cellsToRotate.each(function () {
var cell = $(this)
, newText = cell.text()
, height = cell.height()
, width = cell.width()
, newDiv = $('<div>', { height: width, width: height })
, newInnerDiv = $('<div>', { text: newText, 'class': 'rotated' });
newInnerDiv.css('-webkit-transform-origin', (width / 2) + 'px ' + (width / 2) + 'px');
newInnerDiv.css('-moz-transform-origin', (width / 2) + 'px ' + (width / 2) + 'px');
newDiv.append(newInnerDiv);
betterCells.push(newDiv);
});
cellsToRotate.each(function (i) {
$(this).html(betterCells[i]);
});
};
})(jQuery);
And this at the top of your page:
<script src="rotatetablecellcontent.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$('.yourtableclass').rotateTableCellContent();
});
</script>
And this in your CSS:
/* Styles for rotateTableCellContent plugin*/
table div.rotated {
-webkit-transform: rotate(270deg);
-moz-transform: rotate(270deg);
writing-mode:tb-rl;
white-space: nowrap;
}
thead th {
vertical-align: top;
}
table .vertical {
white-space: nowrap;
}
Then make sure your table has the class "yourtableclass", and that all the TDs you want rotated have the class "vertical".
Here's a demo running in a jsFiddle.
Hope it helps someone, even though I'm two years late!

In order to have rotated text inside your table headers:
Place the header content inside divs - rotate these divs rather than the header itself
Set the position:relative on the table headers th, position:absolute on the rotated divs.
Set height of th headers too
Done.
You can see it here:
Which you can see on this page if you make your window skinny - less than 1000 pixels and it rotates the table headers - http://www.rugbydata.com/
Here's the code I used:
div.rotatabletext {
-webkit-transform: rotate(-90deg);
/* Firefox */
-moz-transform: rotate(-90deg);
/* IE */
-ms-transform: rotate(-90deg);
/* Opera */
-o-transform: rotate(-90deg);
/* Internet Explorer */
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
width:0px;
text-align:left;
height:0px;
margin-left:0px;
margin-top:0px;
bottom:2px;
position:absolute;
}
table.tournamentresults > * > tr > td {
padding:1px;
}
table.tournamentresults > thead > tr:nth-child(1) > th:nth-child(1) {
height:70px;
position:relative;
}
table.tournamentresults > thead > tr:nth-child(2) th {
height:70px;
position:relative;
}

This tool did all the thinking for me...
http://www.boogdesign.com/examples/transforms/matrix-calculator.html

Related

CSS blur only in one direction (motion blur)

I need to dynamically blur an image on my page, but only along one axis (Y specifically). So here are my requirements:
Has to be done "live" (I can't pre-render a blurred version of the image)
Like I said, only on the Y axis (like a motion blur, but vertical)
Needs to animate in
Should work in IE9+
My first thought was to use a simple CSS filter:
img {
filter: blur(20px);
}
I can animate that by adding a transition (transition: filter 0.2s linear), but it only creates Gaussian blurs, which isn't the effect I want. The syntax doesn't support something like filter: blur(0 10px); to restrict the blur only to one axis.
Then I read that the blur filter (amongst others) is really just a shorthand for an SVG filter, which you can write manually if you want. So I created an SVG called filter.svg that specifies a 20px blur only along the Y axis (0 20):
<?xml version="1.0" standalone="no"?>
<svg width="1" height="1" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 20" />
</filter>
</defs>
</svg>
And applied it like this:
img {
filter: url("filter.svg#blur");
}
And that works perfectly...but only in Firefox. Safari/Chrome don't support url() as a value for filter. Plus, I can't animate it because the value is a URL rather than a number, so transition doesn't work.
On top of all that, I don't think either of these approaches work in IE9.
So: is there any way to do what I'm trying to do? I've looked into using canvas as an alternative, but can't find any examples of a blur that only goes in one direction.
If I'm understanding the question right it can be donewith JQuery.
CSS3 does have it's limits and it's very limited in interactive values.
Jquery also adds cross-platform stability.
JQuery
$(document).ready(function() {
var $img = $('.image')
$img.hide();
$img.show().animate({
opacity: 100,
paddingTop: '+=80'
}, 500)
});
Here is an example of how it could work with javacript with a little
fooling around on opacity.
function myMove() {
var elem = document.getElementById("animate");
var pos = 0;
var id = setInterval(frame, 5);
function frame() {
if (pos == 150) {
clearInterval(id);
} else {
pos++;
elem.style.left = pos + 'px';
}
}
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<p>
<button onclick="myMove()">Click Me</button>
</p>
<div id="container">
<div id="animate"></div>
</div>

How do I change one div's transparency by hovering its parent div using CSS?

I'm attempting to toggle the transparency (effectively, from invisible to visible) of a title/date div (#post_h3_container) over the snippet of the post on a blog rollup page on mouseover of the parent div (#text_post_body). I've managed to make this work when hovering the #post_h3_container div only.
I've tried various selectors between the divs including +, ~, > (and using :hover) and even no selectors at all and can't seem to create the desired effect. I've matched my code to several answers addressing this on StackOverflow, but still no dice. I've starred the CSS rule that doesn't seem to be doing anything.
Any idea what it is I'm missing? This is for Tumblr, if that makes a difference.
Here's the site: http://bookishmatt.tumblr.com/
The CSS:
#text_post_body {
max-width: 460px;
margin-top: -15px;
}
#post_h3_container {
position: absolute;
width: 450px;
max-height: 120px;
background-color:rgba(51,51,51,0.8);
padding: 0 5px 0 5px;
opacity: 0;
}
#post_h3_container:hover {
opacity: 1;
-webkit-transition: opacity .4s;
}
**#text_post_body:hover ~ #post_h3_container {
opacity: 1;
-webkit-transition: opacity .4s;
}**
The HTML:
<div id="post">
<div id="text_post">
{block:Text}
{block:Permalink}{block:Title}<div id="perma_post"><h3>{Title}</h3></div>{/block:Title}
<div id="post_date_perma">{block:Date}<h2>{Month} {DayOfMonth}{DayOfMonthSuffix}, {Year} at {12Hour}:{Minutes} {AmPm}</h2>{/block:Date}</div><div id="by_container_perma">By +Matt Albrecht
{/block:Permalink}
{block:IndexPage}<div id="post_h3_container">{block:Title}<h3>{Title}</h3>{/block:Title}
<div id="post_date">{block:Date}<h2>{Month} {DayOfMonth}{DayOfMonthSuffix}, {Year} at {12Hour}:{Minutes} {AmPm}</h2>{/block:Date}</div><div id="by_container">By +Matt Albrecht
</div> {/block:IndexPage}
</div>
</div>
<div id="text_post_body">{Body}{block:More} Read more... {/block:More}</div>
<div id="notes">
<p>
<div id="socialcomments">
{block:IndexPage}{block:IfDisqusShortname}<a class="dsq-comment-count" href="{Permalink}#disqus_thread">Comments</a>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'bookishmatt'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function () {
var s = document.createElement('script'); s.async = true;
s.type = 'text/javascript';
s.src = 'http://' + disqus_shortname + '.disqus.com/count.js';
(document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
}());
</script>
{block:IfDisqusShortname}
{/block:IndexPage}
<span st_url='{Permalink}' st_title='{Title}' class='st_facebook_hcount' displayText='Facebook'></span><span st_url='{Permalink}' st_title='{Title}' class='st_twitter_hcount' displayText='Tweet'></span><span st_url='{Permalink}' st_title='{Title}' </span>
</div>
{/block:Text}
</div>
Any insights welcome. If jquery is needed, I'll admit outright that this is over my head, so I may need a really dumbed down walkthrough for how to implement the code, if that's the case.
EDIT: On the other hand, maybe you're of the opinion that the current hover options are alright on their own. If you don't think the whole snippet should reveal the title/date, I value your opinion on that matter, too.
CSS hover can only affect the object itself or its descendants. In this case, post_h3_container is a child of a sibling.
You could organize this better and:
HTML:
create an element .container that wraps both #by_container_perma and #text_post_body
CSS:
.container:hover #post_h3_container {
opacity: 1
}
If you don't like that, I will give you some jQuery, but it seems excessive.
Also, you mentioned this is a blog... be careful of your id's. They should not be used for repeated content.
#text_post_body:hover #post_h3_container {
opacity: 1.0;
}
Instead of your #post_h3_container:hover properties. Also you can apply the transition property to just plain #post_h3_container

Placeholder background/image while waiting for full image to load?

I have a few images on my page. I'm finding that the page starts to render before the images have been loading (which is good), but that the visual effect is not great. Initially the user sees this:
--------hr--------
text
Then a few milliseconds later the page jumps to show this:
--------hr--------
[ ]
[ image ]
[ ]
text
Is there a simple way that I can show a grey background image of exactly the width and height that the image will occupy, until the image itself loads?
The complicating factor is that I don't know the height and width of the images in advance: they are responsive, and just set to width: 100% of the containing div. This is the HTML/CSS:
<div class="thumbnail">
<img src="myimage.jpeg" />
<div class="caption">caption</div>
</div>
img { width: 100% }
Here's a JSFiddle to illustrate the basic problem: http://jsfiddle.net/X8rTB/3/
I've looked into things like LazyLoad, but I can't help feeling there must be a simpler, non-JS answer. Or is the fact that I don't know the height of the image in advance an insurmountable problem? I do know the aspect ratio of the images.
Instead of referencing the image directly, stick it within a DIV, like the following:
<div class="placeholder">
<div class="myimage" style="background-image: url({somedynamicimageurl})"><img /></div>
</div>
Then in your CSS:
.placeholder {
width: 300;
height: 300;
background-repeat: no-repeat;
background-size: contain;
background-image: url('my_placeholder.png');
}
Keep in mind - the previous answers that recommend using a div background approach will change the semantic of your image by turning it from an img into a div background. This will result in things like no indexing of these images by a search crawler, delay in loading of these images by the browser (unless you explicitly preload them), etc.
A solution to this issue (while not using the div background approach) is to have a wrapper div to your image and add padding-top to it based on the aspect ratio of the image that you need to know in advance. The below code will work for an image with an aspect ratio of 2:1 (height is 50% of width).
<div style="width:100%;height:0; padding-top:50%;position:relative;">
<img src="<imgUrl>" style="position:absolute; top:0; left:0; width:100%;">
</div>
Of course - the major disadvantage of this approach is that you need to know the aspect ratio of the image in advance.
There is a really simple thing to check before you start looking into lazy-loading and other JavaScript. Make sure the JPEG images you are loading are saved with the 'progressive' option enabled!
This will cause them to load the image iteratively, starting with a placeholder that is low-resolution and faster to download, rather than waiting for the highest resolution data before rendering.
It's very simple...
This scenario allows to load a profile photo that defaults to a placeholder image.
You could load multi CSS background-image into an element. When an avatar photo fails, the placeholder image appears default of div.
If you're using a div element that loads via a CSS background-image, you could use this style:
#avatarImage {
background-image: url("place-holder-image.png"), url("avatar-image.png");
}
<div id="avatarImage"></div>
Feel free to copy this:
<script>
window.addEventListener("load", function () {
document.getElementById('image').style.backgroundColor = 'transparent';
});
</script>
<body>
<image src="example.example.example" alt="example" id="image" style="background-color:blue;">
</body>
I got this from here: Preloader keeps on loading and doesnt disappear when the content is loaded.
Apart from all solutions already mentioned, the last solution would be to hide the document until everything is loaded.
window.addEventListener('load', (e) => {
document.body.classList.add('loaded');
});
body {
opacity: 0;
}
body.loaded {
opacity: 1;
}
<div id="sidebar">
<img src="http://farm9.staticflickr.com/8075/8449869813_1e62a60f01_b.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-1.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-2.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-3.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-4.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-5.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-6.jpg" />
</div>
Or show some animation while everything is loading:
window.addEventListener('load', (e) => {
document.body.classList.add('loaded');
});
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 70px;
height: 70px;
-webkit-animation: spin 2s linear infinite;
/* Safari */
animation: spin 2s linear infinite;
position: absolute;
left: calc(50% - 35px);
top: calc(50% - 35px);
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
body :not(.loader) {
opacity: 0;
}
body .loader {
display: block;
}
body.loaded :not(.loader) {
opacity: 1;
}
body.loaded .loader {
display: none;
}
<div class="loader"></div>
<div id="sidebar">
<img src="http://farm9.staticflickr.com/8075/8449869813_1e62a60f01_b.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-1.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-2.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-3.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-4.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-5.jpg" />
<img src="https://www.nla.gov.au/sites/default/files/pic-6.jpg" />
</div>
The only thing I can think of, to minimize the jump effect on your text, is to set min-height to where the image will appear, I would say - set it to the "shorter" image you know of. This way the jump will be less evident and you won't need to use lazyLoad or so... However it doesn't completely fix your problem.
Here's one naive way of doing it,
img {
box-shadow: 0 0 10px 0 rgba(#000, 0.1);
}
You can manipulate the values, but it creates a very light border around the image that doesn't push the contents. Images can load at whatever time they want, and you get a good user experience.
Here is what I did with Tailwind CSS, but it's just CSS:
img {
#apply bg-no-repeat bg-center;
body.locale-en & {
background-image: url("data:image/svg+xml;utf8,<svg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'><text x='50%' y='50%' style='font-family: sans-serif; font-size: 12px;' dominant-baseline='middle' text-anchor='middle'>Loading…</text></svg>");
}
body.locale-fr & {
background-image: url("data:image/svg+xml;utf8,<svg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'><text x='50%' y='50%' style='font-family: sans-serif; font-size: 12px;' dominant-baseline='middle' text-anchor='middle'>Chargement…</text></svg>");
}
}
You can find the width and height of the images in the developer tools console, for example in Chrome you can click the cursor icon in the developer tools console and when you hover on the page it will highlight all the properties of the elements in the page.
This will help you find the width and height of the images, because if you hover on top of your images it will give you the dimensions of the image and other more properties. You can also make an individual div for each image and make the div relative to the images width and height. You can do it like this:
The main div will contain the images and also the background-div which is below the image.
HTML:
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div class=".mainDiv">
<div class="below"></div>
<img src="https://imgix.bustle.com/uploads/image/2020/2/13/da1a1ca4-95ec-40ea-83c1-4f07fac8b9b7-eqb9xdwx0auhotc.jpg" width="500"/>
</div>
</body>
</html>
CSS:
.mainDiv {
position: relative;
}
.below {
position: absolute;
background: #96a0aa;
width: 500px;
height: 281px;
}
img {
position: absolute;
}
The result will be that .below will be below the image and so when the image has trouble loading the user will instead see the grey .below div. You cannot see the .below div because it is hidden below the image. The only time you will see this is when the loading of the image is delayed. And this will solve all your problems.
I have got a way. But you will need to use JavaScript for it.
The HTML:
img = document.getElementById("img")
text = document.getElementById("text")
document.addEventListener("DOMContentLoaded", () => {
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAA1BMVEWIiIhYZW6zAAAASElEQVR4nO3BgQAAAADDoPlTX+AIVQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwDcaiAAFXD1ujAAAAAElFTkSuQmCC";
text.innerHTML = "Loaded but image is not";
});
window.onload = function() {
img.src = "https://media.geeksforgeeks.org/wp-content/uploads/20190913002133/body-onload-console.png";
text.innerHTML = "Image is now loaded";
};
#img {
width: 400px;
height: 300px;
}
<hr>
<img id="img" src="https://media.geeksforgeeks.org/wp-content/uploads/20190913002133/body-onload-console.png">
<p>Here is the Image</p>
<p id="text">Not Loaded</p>

Vertically rotate text inside an HTML table header cell

I am using the following css to rotate the text inside table header cells but the header cells is of the same width as if the text were horizontal.How can I just rotate the text and the width will reduce automatically..
table#MyTable tr th a{
color: #FFFFFF;
display: block;
/*Firefox*/
-moz-transform: rotate(-90deg);
/*Safari*/
-webkit-transform: rotate(-90deg);
/*Opera*/
-o-transform: rotate(-90deg);
/*IE*/
writing-mode: tb-rl;
filter: flipv fliph;
padding: 60px 1px;
}
If you need to adjust just the width of the cells and they contain only one line of text each you can do this: http://jsfiddle.net/sSP8W/3/ — set width of an element to it's line-height.
The problem with CSS3-transforms is that they work like as CSS' position: relative: their original box stays the same, so rotating, skewing etc. don't cause the changes in the element's dimensions. So: there is really no perfect CSS solution, you can use JS to adjust the dimensions, or try to find hackety workarounds. So if you have only links in a table, you can do something like that: http://jsfiddle.net/sSP8W/4/ — rotating the table itself.
If your case have another content that you don't want to rotate — update the post, so we could try to find a better solution.
upd: Just found out a solution to the rotated text in tables: using some magic with vertical paddings we could make cells stretch to the content, so look at this almost final example: http://dabblet.com/gist/4072362
I solved it this using a jQuery plugin by David Votrubec and the comment by Mike below the blog post.
Put this in a .js-file:
(function ($) {
$.fn.rotateTableCellContent = function (options) {
/*
Version 1.0
7/2011
Written by David Votrubec (davidjs.com) and
Michal Tehnik (#Mictech) for ST-Software.com
*/
var cssClass = ((options) ? options.className : false) || "vertical";
var cellsToRotate = $('.' + cssClass, this);
var betterCells = [];
cellsToRotate.each(function () {
var cell = $(this)
, newText = cell.text()
, height = cell.height()
, width = cell.width()
, newDiv = $('<div>', { height: width, width: height })
, newInnerDiv = $('<div>', { text: newText, 'class': 'rotated' });
newInnerDiv.css('-webkit-transform-origin', (width / 2) + 'px ' + (width / 2) + 'px');
newInnerDiv.css('-moz-transform-origin', (width / 2) + 'px ' + (width / 2) + 'px');
newDiv.append(newInnerDiv);
betterCells.push(newDiv);
});
cellsToRotate.each(function (i) {
$(this).html(betterCells[i]);
});
};
})(jQuery);
And this at the top of your page:
<script src="rotatetablecellcontent.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
$('.yourtableclass').rotateTableCellContent();
});
</script>
And this in your CSS:
/* Styles for rotateTableCellContent plugin*/
table div.rotated {
-webkit-transform: rotate(270deg);
-moz-transform: rotate(270deg);
writing-mode:tb-rl;
white-space: nowrap;
}
thead th {
vertical-align: top;
}
table .vertical {
white-space: nowrap;
}
Then make sure your table has the class "yourtableclass", and that all the TDs you want rotated have the class "vertical".
Here's a demo running in a jsFiddle.
Hope it helps someone, even though I'm a year late!
In IE and Chrome (Blink and Webkit), you can put the text in a child with a vertical writing mode rather than using a transform. Saves you all the CSS and JavaScript tricks. Chrome has a minor display bug at the moment (Chrome 37), but it's been reported.
.vertical {
-webkit-writing-mode:vertical-rl;
-ms-writing-mode:tb-rl;
writing-mode:vertical-rl;
}
<td><span class="vertical">This text should be vertical.</span></td>
I recommend using white-space:nowrap on the vertical text within the table.
The solution by jobjorn above now breaks in Chrome, because it supports writing-mode as of early 2016, so essentially tries to rotate the text twice, and ends up with mispositioned horizontal text. From my understanding, Firefox and Safari will probably support this soon too, which will cause them to break too. After some banging my head against the wall, I fixed it by changing the CSS to:
#supports not (writing-mode:vertical-rl) {
table div.rotated {
-webkit-transform: rotate(270deg);
-moz-transform: rotate(270deg);
white-space: nowrap;
}
}
#supports (writing-mode:vertical-rl) {
table div.rotated {
writing-mode:vertical-rl;
-webkit-transform: rotate(180deg) translate(8px,0px);
-moz-transform: rotate(180deg) translate(8px,0px);
white-space: nowrap;
}
}
The 8 pixel translation was what it took for the alignment to look right in my particular example. You mileage may vary. The 180 degree rotation was to make the top of the text face left instead of right. If you don't need that, you can probably skip the transforms in the second part.
In the JavaScript, you also need to make sure that you don't move the origin for those last transforms, as that was only needed if you were using a transform to do the vertical orientation itself, so I wrapped that part in a conditional:
var supportsWM = CSS.supports('writing-mode', 'vertical-rl');
if (!supportsWM) {
newInnerDiv.css('-webkit-transform-origin', (width / 2) + 'px ' + (width / 2) + 'px');
newInnerDiv.css('-moz-transform-origin', (width / 2) + 'px ' + (width / 2) + 'px');
}
Mods: sorry that this post "responds to an answer". But I don't have the karma necessary to comment on it. Catch-22.
The code in #jobjorn's answer and fiddl depends on cell width to calculate header height, which leads to erroneous heights when any cell is wide. This modified fiddle uses the header text width instead: http://jsfiddle.net/marvmartian/76z82/
cellsToRotate.each(function () {
var cell = $(this);
var s = '<div id="killme" style="position:absolute; top:-10000px; left:-10000px;"><span id="string_span" style="font-weight: bold; font-size: 1em">'+
cell.text()+'</span></div>';
$(window).append(s);
var width = $('#string_span').width();
var newText = cell.text()
, height = cell.height()
//, width = cell.width()
, newDiv = $('<div>', { height: width, width: height })
, newInnerDiv = $('<div>', { text: newText, 'class': 'rotated' });
Get rid of padding:60px 1px; (unless you need it for some other reason...but that's what's causing the problem).
Example: http://jsfiddle.net/purmou/sSP8W/
This is now possible without any browser specific transforms used in the other answers. Note that the wrapping span is required as of 2022 in order to get firefox in particular to center the rotated text within the column (webkit does this by default).
<style type="text/css">
#myTable td {
text-align: right;
}
th.r span {
transform: rotate(185deg);
writing-mode: vertical-lr;
}
</style>
<table id="myTable" border="1" cellpadding="2" style="border-collapse:collapse;">
<tr>
<th class='r'><span>Display</span></th>
<th class='r'><span>Year made (TV?)</span></th>
<th class='r'><span>Native Res</span></th>
</tr>
<tr>
<td style="color:rgb(17, 85, 204);">Dell U2410 (game) </td>
<td>2010</td>
<td>1080p</td>
</tr>
<tr>
<td style="color:rgb(17, 85, 204);">Dell U2410 (sRGB)</td>
<td>2010</td>
<td>1080p</td>
</tr>
<tr>
<td style="color:rgb(17, 85, 204);"> Sony 40VL130 (game)</td>
<td style="color:rgb(0, 0, 255);">2008</td>
<td>1080p</td>
</tr>
</table>
<style type="text/css">
#myTable td {
text-align: right;
}
th.r span {
transform: rotate(185deg);
writing-mode: vertical-lr;
}
</style>
<table id="myTable" border="1" cellpadding="2" style="border-collapse:collapse;">
<tr>
<th class='r'><span>Display</span></th>
<th class='r'><span>Year made (TV?)</span></th>
<th class='r'><span>Native Res</span></th>
</tr>
<tr>
<td style="color:rgb(17, 85, 204);">Dell U2410 (game) </td>
<td>2010</td>
<td>1080p</td>
</tr>
<tr>
<td style="color:rgb(17, 85, 204);">Dell U2410 (sRGB)</td>
<td>2010</td>
<td>1080p</td>
</tr>
<tr>
<td style="color:rgb(17, 85, 204);"> Sony 40VL130 (game)</td>
<td style="color:rgb(0, 0, 255);">2008</td>
<td>1080p</td>
</tr>
</table>

CSS text justify with letter spacing

Is there a way to automatically justify words using letter spacing, each in its row, to a defined width, using CSS?
For example, "Something like this" would look, well, something like this:
Is there a non-obtrusive way to apply such styling to my text? I believe pure CSS doesn't have this option (at least not with CSS versions before 3, CSS3 seems to have a text-justify property, but it's not well supported yet), so js solutions would be fine also.
Here's a script which can do it. It isn't pretty, but maybe you can hack it to meet your needs. (Updated to handle resizing)
function SplitText(node) {
var text = node.nodeValue.replace(/^\s*|\s(?=\s)|\s*$/g, "");
for (var i = 0; i < text.length; i++) {
var letter = document.createElement("span");
letter.style.display = "inline-block";
letter.style.position = "absolute";
letter.appendChild(document.createTextNode(text.charAt(i)));
node.parentNode.insertBefore(letter, node);
var positionRatio = i / (text.length - 1);
var textWidth = letter.clientWidth;
var indent = 100 * positionRatio;
var offset = -textWidth * positionRatio;
letter.style.left = indent + "%";
letter.style.marginLeft = offset + "px";
//console.log("Letter ", text[i], ", Index ", i, ", Width ", textWidth, ", Indent ", indent, ", Offset ", offset);
}
node.parentNode.removeChild(node);
}
function Justify() {
var TEXT_NODE = 3;
var elem = document.getElementById("character_justify");
elem = elem.firstChild;
while (elem) {
var nextElem = elem.nextSibling;
if (elem.nodeType == TEXT_NODE)
SplitText(elem);
elem = nextElem;
}
}
#character_justify {
position: relative;
width: 40%;
border: 1px solid red;
font-size: 32pt;
margin: 0;
padding: 0;
}
#character_justify * {
margin: 0;
padding: 0;
border: none;
}
<body onload="Justify()">
<p id="character_justify">
Something<br/> Like
<br/> This
</p>
</body>
The css only solution is text-justify: distribute https://www.w3.org/TR/css-text-3/#text-justify but the support is still very poor.
A small experiment using text-align-last: justify and adding spaces between letters.
div{
display:inline-block;
text-align: justify;
text-align-last: justify;
letter-spacing: -0.1em;
}
<div>
S o m e t h i n g<br>
l i k e<br>
t h i s
</div>
I know this is an old topic, but I faced this the other night. And found a suitable solution using tables.
Every letter shall be put into a <td> </td> I know it looks tedious, but if you wanna do this, it would be for a word or two, right? Or you always can use JS to fill it if is too much. However, this is only CSS and very versatile solution.
Using letter-spacing the letters get distributed properly. You should play around with it, depending on the width of the table.
#justify {
width: 300px;
letter-spacing: 0.5em;
}
<table id="justify">
<tbody>
<tr>
<td>J</td>
<td>U</td>
<td>S</td>
<td>T</td>
<td>I</td>
<td>F</td>
<td>Y</td>
</tr>
</tbody>
</table>
See the example here
Crossbrowser safe, virtually nothing shall differ. Is just CSS.
I used it in My website which is in english and spanish.
the subtitle under my name in spanish has an additional letter and it will step out the width. Using the tables explained above, it gets distributed to the same width automatically. Spacing it manually I'd had to define a whole condition for each language to go around that.
Here is an other aproach using a jQuery snippet I wrote for this question : Stretch text to fit width of div :
DEMO
HTML :
<div class="stretch">Something</div>
<div class="stretch">Like</div>
<div class="stretch">This</div>
jQuery :
$.fn.strech_text = function () {
var elmt = $(this),
cont_width = elmt.width(),
txt = elmt.html(),
one_line = $('<span class="stretch_it">' + txt + '</span>'),
nb_char = elmt.text().length,
spacing = cont_width / nb_char,
txt_width;
elmt.html(one_line);
txt_width = one_line.width();
if (txt_width < cont_width) {
var char_width = txt_width / nb_char,
ltr_spacing = spacing - char_width + (spacing - char_width) / nb_char;
one_line.css({
'letter-spacing': ltr_spacing
});
} else {
one_line.contents().unwrap();
elmt.addClass('justify');
}
};
$(document).ready(function () {
$('.stretch').each(function () {
$(this).strech_text();
});
});
Needed this too, so I've bundled the suggested technique in a simple to use jquery-plugin you can find here: https://github.com/marc-portier/jquery-letterjustify#readme.
It uses the same procedure at its core, and adds some options to tweak.
Comments welcome.
Found another way to achieve this with pure CSS, alas you need to spell out your words.
In my situation, this was the only solution that worked (some letters had classes), plus it also produced the straightest right-alignment among answers here, without using hacks.
.box {
width: min-content;
border: solid red;
}
.word {
display: flex;
justify-content: space-between;
}
<div class="box">
<div class="word">
<span>S</span>
<span>o</span>
<span>m</span>
<span>e</span>
<span>t</span>
<span>h</span>
<span>i</span>
<span>n</span>
<span>g</span>
<span> </span>
<span>w</span>
<span>i</span>
<span>c</span>
<span>k</span>
<span>e</span>
<span>d</span>
</div>
<div class="word">
<span>t</span>
<span>h</span>
<span>i</span>
<span>s</span>
<span> </span>
<span>w</span>
<span>a</span>
<span>y</span>
</div>
<div class="word">
<span>c</span>
<span>o</span>
<span>m</span>
<span>e</span>
<span>s</span>
</div>
</div>
Again, I know this is REALLY old, but why not just put a space between each letter and then text-align:justify? Then each letter would be regarded as a 'word' and justified accordingly
An alternate way to handle this might be to use the "vw" sizing unit. This unit type can be used in font size properties and represents a percent of the window's width.
Disclaimer: It is not exactly what you are looking for, but requires no scripting. It does adjust the text size, but will also scale to the width of your page.
For example,
.heading {
font-size: 4vw;
}
will make the width of one character in the current font 4% of the window width.
You could then use media queries if you wish to lock the font size to a minimum size based on the window's width.
#media only screen and (max-width: 768px) {
font-size: 2rem;
}
Use the browser inspector to play with the font-size property and tweak the value to what makes sense for your application.
The "vw" unit works in IE9+, iOS 8.3+ and Android 4.4+ and all other mainstream browsers. I wouldn't worry about the mobile support too much, as you can use media queries to put the right sizing for these devices as described above.
http://caniuse.com/#feat=viewport-units
https://css-tricks.com/viewport-sized-typography/
Viewport units are a powerful way to scale many different aspects of your site with little code.
I just made a JQuery script from table's Tony B approach.
Here is the JSFiddle https://jsfiddle.net/7tvuzkg3/7/
This script creates a table with each char in a row. This works with full sentence.
I'm not sure this is fully optimized.
justifyLetterSpacing("sentence");
function justifyLetterSpacing(divId) {
// target element
domWrapper = $('#' + divId).clone().html('');
// construct <td>
$('#' + divId).contents().each(function(index){
// store div id to td child elements class
var tdClass = divId ;
// split text
$textArray = $(this).text().split('');
// insert each letters in a <td>
for (var i = 0; i < $textArray.length; i++) {
// if it's a 'space', replace him by an 'html space'
if( $textArray[i] == " " ) {
$('<td>')
.addClass(tdClass)
.html(" ")
.appendTo(domWrapper);
}// if it's a char, add it
else{
$('<td>')
.addClass(tdClass)
.text($textArray[i])
.appendTo(domWrapper);
}
}
});
// create table
table =
$( "<table id='"+divId+"'/>" ).append(
$( "<tbody>" ).append(
$( "<tr>" ).append(
( domWrapper ).children('td')
)
)
);
// replace original div
$('#' + divId).replaceWith( table );
}
#sentence {
width : 100%;
background-color : #000;
color : #fff;
padding : 1rem;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="sentence">LOREM IPSUM DOLOR</div>
I usually try to write my answer on time. and this is exactly the same time (after 10 years) =)
myText.innerHTML = myText.textContent
.split(/\s+/g)
.map((line) => line.trim().split("").join(" "))
.join("<br>");
#myText {
display: inline-block;
text-align: justify;
text-align-last: justify;
letter-spacing: -0.125em;
font-family: sans-serif;
}
<div id="myText">Something like this</div>

Resources