21

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?

7
  • What do you mean, "tab stops"? Do you expect something to happen based on the tab character (ASCII 9)? Commented Aug 22, 2013 at 0:44
  • What I mean is that inside some element (e.g. a div) I would like to say "tab stops are at 1 inch, 2 inches, 4 inches, etc". Then I would like to have some way, maybe not a tab character but something in the text stream, to say "advance to the next tab stop after the current horizontal position". This is a very common thing in word processing and MS Word has had it since day 1. All I see in discussions about doing this in html is "use a table" but this will not work for my case. Commented Aug 22, 2013 at 2:21
  • HTML is not a word processing format. It's a presentation format. Commented Aug 22, 2013 at 2:25
  • @WayneChristopher: You could do that with position: absolute, but overflow will cause lots of trouble. Commented Aug 22, 2013 at 2:29
  • 2
    In 2016, the main browsers do support the use of tab characters in elements that have white-space:pre. See fiddle. Support for the tab-size property is still wonky though. Commented May 2, 2016 at 10:08

9 Answers 9

20

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/

Sign up to request clarification or add additional context in comments.

7 Comments

The difficulty with this approach is that the container will not expand vertically to accommodate the absolutely-positioned spans. Is there a workaround for this?
The short answer is yes, but the other methods of doing it are not "workarounds". There are many ways to do what the OP asked. Each has it's own strengths and shortcomings. My message to readers was do not be put off by those that say it can not be done. It can. I did not take the time to document every possible way to do it. When I wrote that 3 years ago, there were powerful tools to do what was requested in draft specs. I am quite sure that those have started to show up in browsers by now. Each persons requirements will lead to a choosing a different solution.
Can you fork my fiddle, load it withe sample data that illustrates the short coming that you want to overcome and tell me the target environment? If it is a corporate intranet where we know what browser(s) are being used. I can choose from more CSS tools than it if is going to be deployed on the intranet and you want to work in every browser going back to netscape 1.0. I exaggerate to illustrate the need for the additional clarification of what you want to do.
Is there a way to use this with inline style tags?
@tukusejssirs I would argue that the example used inline style tags because they were not in a style sheet (out of line). The technique works the same, if you use style tags or if you use a style sheet. Now perhaps you are not asking about style tags at all but rather style attributes on each individual element? Of course you could do that but that is exactly what the OP wanted to avoid doing. If you want to add style to every element I would encourage you to ask your own question and document what you want to do and perhaps why u want to. Avoiding that is what this question is about.
|
2

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.

4 Comments

Thanks, but you are describing indentation instead of tab stops. See youtube.com/watch?v=vvZxO93MApE
Tab stops don't exist in HTML. You have to use tables (which is horrible) or use simple CSS. You can also use inch for declaring the 'tab stop' size p { padding-left: 2in; } if you like to.
Or, and this is really just a joke, you can do it like Word. When you use tab stops and export the page to html it creates a lot of whitespace to display something like a tabstop. <p class=MsoNormal>Normal</p> <p class=MsoNormal>                                                                                                     Indent</p> You have to accept that HTML is not a program like Word and it does not have features like Word. If you write text, mark it and click on bold, Word does something like <b>Your text</b> for you. It's just displayed as bold text, like the result in html.
The problem with using spaces to simulate tabs is that you need to know exactly what your horizontal position is on the page, and when you are generating the html you don't have the necessary font metrics to do that. Maybe word does have that information so it can output the appropriate number of nbsp's... I am thinking the only way is to use Javascript to determine the exact width of a span at rendering time...
1

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> 

Comments

1

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 &mdash;</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/

Comments

1

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:

  • there are a couple of places where it doesn't line up exactly (I guess possible margin/padding issues)
  • reflow isn't great on resizing
  • no effort done for right-to-left languages (margin-inline may be enough)
  • I'd prefer to define tabs as breaks (i.e. <br>) rather than <span> containing the tabbed content
  • only left-aligned: couldn't align right, centred or decimal-point to the "tabstop", which is a significant feature of MS Word's tabstops

...but maybe it is enough for someone else to get it over the line...

Example render

Comments

0

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> 

Comments

0

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.

Comments

0

My take:

transforms this

<div class="tab_stops"> Lorem ipsum: A B C D C Lorem ipsum dolor sit amet: A B C B C Lorem: A B C D E </div> 

into this:

transformed into table

using this js:

for (let elem of document.querySelectorAll(".tab_stops")) { let table = document.createElement('table') for (let line of elem.textContent.trim().split(/\n/)) { let row = document.createElement('tr') row.append(...line.trim().split(/(?:\t+| {2,})\s*/).map(cell => Object.assign(document.createElement('td'), { textContent: cell.trim() || '\xA0' }))) table.append(row) } elem.replaceWith(table) } 

Inside .tab_stops you can use tabs or more than one spaces to delimit columns

https://jsfiddle.net/dto1h6sk/9/

Comments

0

It's not exactly what the OP asked for but might still be useful. I wanted to add a right-aligned tabstop with date and used span for this. Note, the span width needs to be larger than the actual text you type, otherwise the text will wrap in the span box:

CSS:

span { float: right; text-align: right; width: 300px } 

Your HTML:

<p>This is left-aligned text. <span>Date: 1/Jan/1970</span></p> 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.