Reputation: 623
I would like to set up tab stops in html5 and be able to align text to them, just like in Word. For my application I can't use tables. Is there a way to do this? Do I have to use Javascript?
Upvotes: 21
Views: 28434
Reputation: 394
You can use the CSS property
p { text-indent:50px; }
You can use css classes for each indent like
h1 { text-indent: 10px; }
h2 { text-indent: 14px; }
h3 { text-indent: 18px; }
p { text-indent: 20px; }
p.notice { text-indent: 24px; }
and do the HTML like this
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<h3>Heading 3</h3>
<p>Text 1</p>
<p class="notice">Text 2</p>
Because you can only indent one line with this property there's another way to support multiline-indent:
h1 { padding-left: 10px; }
h2 { padding-left: 14px; }
h3 { padding-left: 18px; }
p { padding-left: 20px; }
p.notice { padding-left: 24px; }
And finally a fiddle.
Upvotes: 2
Reputation: 1721
Briefly thought I might need something similar recently, so mocked up the following, using the current accepted answer as a basis. Original answer looked like it might be fine for small text values (e.g. aligning a list of values), but longer text didn't reflow if you resized and/or would wrap inside the inline block area.
<html>
<head>
<style type="text/css">
/* decorative styles for demo only */
html, body {
margin: 0;
padding: 0;
}
p {
background-color: lightyellow;
}
div#ruler {
border-bottom: 1px solid gray;
overflow: hidden;
white-space: nowrap;
}
div#ruler>span {
display: inline-block;
text-align: right;
font-size: xx-small;
color: gray;
border-right: 1px solid gray;
overflow: hidden;
box-sizing: border-box;
}
span.tabbed, br.tab {
background-color: lightcyan;
}
/* seems to be needed if text-indent used for spacing; no harm for padding/margin... */
span.tabbed::before {
display: inline-block;
content: '';
}
/* doesn't work universally, was hoping to use for better semantics -- maybe revisit... */
br.tab {
content: '';
display: inline;
}
</style>
<script type="text/javascript">
// for rate-limiting resize/scroll event firing
function debounce(func, wait, immediate) {
// ref: https://davidwalsh.name/javascript-debounce-function
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
// decorative; used for drawing a ruler at the top of the page only
function applyTabRuler(pid, tabstops) {
var parent = document.getElementById(pid);
if (!parent) {
parent = document.createElement('div');
document.body.insertBefore(parent, document.body.firstChild);
}
for (var s = 1, slen = tabstops.length; s < slen; s++) {
var width = tabstops[s] - tabstops[s - 1];
var tab = document.createElement('span');
tab.appendChild(document.createTextNode(s));
tab.style.width = tab.style.maxWidth = tab.style.minWidth = '' + width + 'px';
parent.appendChild(tab);
}
}
// call this to align elements matching className to pixel-posiitoned tabstops
// tabstops should be an array of monotonically-increasing pixel positions
function applyTabStops(className, tabstops) {
// try also paddingLeft or textIndent (may need a ::before{display:inline-block})
var styleProp = 'marginLeft';
var tabbed = document.getElementsByClassName(className);
var bodyRect = document.body.getBoundingClientRect();
var inlineMarker = document.createElement('span');
for (var t = 0, tlen = tabbed.length; t < tlen; t++) {
tabbed[t].style[styleProp] = '0px';
tabbed[t].insertBefore(inlineMarker, tabbed[t].firstChild);
var tabRect = inlineMarker.getBoundingClientRect();
var tabstop = 0;
for (var s = 0, slen = tabstops.length - 1; s < slen; s++) {
if (tabRect.left >= tabstops[s] && tabRect.left < tabstops[s + 1]) {
tabstop = tabstops[s + 1];
break;
}
}
if (tabstop > 0) {
var width = tabstop - tabRect.left + bodyRect.left;
tabbed[t].style[styleProp] = '' + width + 'px';
}
}
if (inlineMarker && inlineMarker.parentNode) {
inlineMarker.parentNode.removeChild(inlineMarker);
}
}
</script>
</head>
<body>
<div>
<div id="ruler"></div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc cursus congue purus vel blandit.
<span class="tabbed">Cras non justo vitae orci vehicula tincidunt. Aliquam convallis cursus nisi,
eu varius odio rutrum ut. Morbi id accumsan velit. Proin commodo consequat urna aliquam
imperdiet.</span> Curabitur laoreet est ut venenatis suscipit. Sed id vulputate enim.
<span class="tabbed">Etiam libero massa, suscipit eget interdum non, vulputate nec eros. In hac
habitasse platea dictumst.</span>
Ut vestibulum venenatis ante, at vestibulum ex varius eu. Nam lorem
turpis, euismod a aliquam vel, placerat iaculis mauris. Integer et eros turpis. Ut quis volutpat
urna, eu fermentum magna. Phasellus nunc turpis, accumsan nec velit eget, pretium semper urna.
</p>
<p>
<span class="tabbed">Suspendisse nibh nibh, ultrices vitae odio aliquam, facilisis euismod dui.</span>
Quisque dignissim felis in luctus faucibus. Sed at finibus leo. Suspendisse potenti. Nullam
commodo eleifend porttitor. Nam id dolor pretium felis rutrum posuere. Vivamus maximus lorem
mauris, sit amet pulvinar diam luctus nec. Praesent ac euismod lectus.
<span class="tabbed">Fusce tempor metus eget posuere vehicula.</span>
Vestibulum porttitor vitae magna non consectetur. Ut nibh massa, molestie in
est nec, pellentesque rutrum purus. Nam sagittis felis gravida odio blandit, in tincidunt velit
ornare. Etiam congue tellus eros, at molestie risus luctus iaculis. Nulla et vehicula enim.
Integer pellentesque nunc augue, in scelerisque magna eleifend id. Etiam ut dapibus nulla, in
tincidunt justo.
</p>
<p>Sed iaculis enim fermentum arcu gravida tempus.
<span class="tabbed">Sed ipsum ante, scelerisque eget tellus eget, sagittis pellentesque odio.</span></p>
<p>Curabitur vestibulum felis non arcu cursus vehicula.
<span class="tabbed">Nunc blandit neque et imperdiet efficitur.</span></p>
<p>
Quisque malesuada cursus porttitor.
<span class="tabbed">Vestibulum porttitor libero massa, quis lacinia elit tempus vel.</span>
<br/>
Suspendisse laoreet sapien nec nulla placerat, vel dapibus nulla auctor.
<span class="tabbed">Phasellus ut dictum dolor, sit amet feugiat tellus.</span>
</p>
</div>
<script type="text/javascript">
// random tabstops, for testing...
var trun = 0;
var tabstops = [];
for (var t = 0, tlen = 50; t < tlen; t++) {
tabstops[t] = trun;
trun += ((200 * Math.random()) + 20);
}
// fixed tabstops...
//var tabstops = [0, 200, 300, 450, 500, 600, 700, 800, 900, 1000];
console.log(tabstops);
applyTabRuler("ruler", tabstops);
applyTabStops("tabbed", tabstops);
var reapplyTabStops = debounce(function() { applyTabStops("tabbed", tabstops); }, 100);
window.addEventListener('resize', reapplyTabStops);
window.addEventListener('scroll', reapplyTabStops);
</script>
</body>
</html>
(Smoke-tested in FF94, Edge95, IE11)
Managed to get the requirement to go away in the end (it is OK to present tabular data in tables, after all), but I thought I'd offer it up here in case it helps anyone else.
It's not perfect:
margin-inline
may be enough)<br>
) rather than <span>
containing the tabbed content...but maybe it is enough for someone else to get it over the line...
Upvotes: 1
Reputation: 11
Actually, I had a similar situation, and I did it quite simply.
Use the <span>
within the <p>
property, and float it appropriately.
css:
p.main-text { /* these properties don't matter much */
margin: 0;
text-indent: 0;
font-size: 1em;
line-height: 1.2;
text-align:justify;
}
span.column-width { /*this defines the width of the first column */
width: 33%;
float: left;
}
html:
<p class="main-text"><span class="column-width">Diary Date: 2016 April 01 —</span>This is the text of the diary entry. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fringilla pharetra metus id blandit. Integer molestie sed mauris eget gravida. Fusce aliquam diam eu arcu imperdiet vehicula. Fusce fermentum molestie aliquet. Phasellus sodales, mauris sed ornare accumsan, dolor ligula vehicula magna, vel pulvinar sapien lorem condimentum purus. Etiam vulputate commodo mattis. Etiam non tincidunt leo, eget ultricies mauris. Fusce rhoncus ultrices purus. Nunc id scelerisque nisi, quis congue turpis.</p>
fiddle: http://jsfiddle.net/Q3ruh/44/
Upvotes: 1
Reputation: 9398
I know the question said no tables, and the previous JS answer was unpopular, but someone should find this useful. It doesn't do true tab stops, but that's not possible even with CSS.
If you don't want to do a bunch of manual span
or table
tags, this JS automatically turns all elements of class "tabs" into a table when the page loads, using tab characters as a guide.
JS Fiddle: https://jsfiddle.net/s7m6zggp/7/
Edit: On second thought, maybe not the best approach. The regex is throwing my brain for a loop. But I'll keep the Fiddle up in case anyone wants to use it.
Upvotes: 0
Reputation: 306
You can divide your data into <div>
s using classes that give them all the same fixed width. That way, the columns will all line up.
<style>
div.col-1 {
width: 200px
}
div.col-2 {
width: 500px
}
</style>
First row is:
<div class="col-1">Some text here </div><div class="col-2">And here </div>
...
Second row is:
<div class="col-1">And here </div><div class="col-2">And here </div>
Upvotes: 1
Reputation: 1041
Despite the assertions of the other posters to the contrary, there are valid reasons to want to do what the OP asked and many ways to do it using modern CSS (and more in the draft specification process as I write this). Here is just one way.
<!DOCTYPE HTML>
<html>
<head>
<title>Tabs with css</title>
<style>
{body: font-family: arial;}
div.row span{position: absolute;}
div.row span:nth-child(1){left: 0px;}
div.row span:nth-child(2){left: 250px;}
div.row span:nth-child(3){left: 500px;}
</style>
</head>
<body>
<div class="row"><span>first row data before first tab</span><span>data left aligned with first tab</span><span>data aligned with second tab</span></div><br>
<div class="row"><span>second row data before first tab</span><span>data left aligned with first tab</span><span>data aligned with second tab</span></div><br>
<div class="row"><span>third row data before first tab</span><span>data left aligned with first tab</span><span>data aligned with second tab</span></div><br>
<div class="row"><span>fourth row data before first tab</span><span>data left aligned with first tab</span><span>data aligned with second tab</span></div><br>
<div class="row"><span>fifth row data before first tab</span><span>data left aligned with first tab</span><span>data aligned with second tab</span></div><br>
</body>
</html>
See sample results here: http://jsfiddle.net/Td285/
Another example, with overflow clipped: http://jsfiddle.net/KzhP8/
Upvotes: 19
Reputation: 623
The solution seems to be to use Javascript. Here is a simple example: http://jsfiddle.net/A6z5D/1 :
<p>Hello bla bla bla<span id="tab1"></span>world</p>
<script>
function do_tab(id, tabstops)
{
var tab1 = document.getElementById(id);
var rect = tab1.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);
var tabstop = 0;
for (var i = 0; i < tabstops.length - 1; ++i)
{
if (rect.left >= tabstops[i] && rect.left < tabstops[i+1])
{
tabstop = tabstops[i+1];
}
}
if (tabstop > 0)
{
var width = tabstop - rect.left;
tab1.style.display = "inline-block";
tab1.style.width = width + "px";
}
}
do_tab("tab1", new Array(0, 100, 200, 300, 400));
</script>
Upvotes: 0