Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

html: How to add line numbers to a source code block

Tags:

html

css

I want to show source code with line numbers, in such a way that:

  1. The code can be copied and pasted without the line numbers, respecting newlines, spaces and tabs.
  2. The numbers are right-justified.
  3. The code can wrap around.

I've tried the following approaches.

Ordered list

<style type="text/css">
    ol.code > li {
        white-space: pre-wrap;
    }
</style>
...
<ol class="code">
    <li>first line</li>
    <li>   indented line followed by empty line</li>
    <li></li>
    <li>the following line consists of one single space</li>
    <li> </li>
    <li>this line has a space at the end </li>
    <li>&#9;and one leading and ending tab&#9;</li>
    <li>fill to</li>
    <li>ten</li>
    <li>lines</li>
</ol>

This works for the most part, but when copying and pasting, the leading spaces and tabs in the lines are not copied in my browser (Iceweasel). So #1 fails with this approach.

Pre with counter

<style type="text/css">
    pre.code {
        white-space: pre-wrap;
    }
    pre.code:before {
        counter-reset: listing;
    }
    pre.code code {
        counter-increment: listing;
    }
    pre.code code:before {
        content: counter(listing) ". ";
        width: 8em;         /* doesn't work */
        padding-left: auto; /* doesn't work */
        margin-left: auto;  /* doesn't work */
        text-align: right;  /* doesn't work */
    }
</style>
...
<pre class="code">
<code>first line</code>
<code>   indented line followed by empty line</code>
<code></code>
<code>the following line consists of one single space</code>
<code> </code>
<code>this line has a space at the end </code>
<code>&#9;and one leading and ending tab&#9;</code>
<code>fill to</code>
<code>ten</code>
<code>lines</code>
</pre>

This almost works, but #2 fails. It has an additional problem that wrapped lines continue under the line number (next to the left margin), not under the line start (tabulated).

Sync lines and pre

This was a suggestion I found in https://www.sitepoint.com/best-practice-for-code-examples/ but it explicitly says that line wrapping must be disabled, therefore #3 fails. It also makes me very uneasy, as I've seen these blocks break in some sites.

Is there a way to do this without JavaScript?

like image 980
Pedro Gimeno Avatar asked Dec 23 '16 19:12

Pedro Gimeno


People also ask

How do I add line numbers in HTML?

We can use the built-in counter function in CSS in combination with counter-increment to add the line numbers. The counter function expects an arbitrary name that we define for counter-increment . This is going to add the content inside a ::before pseudo-element.

How do I add line numbers to a text?

Add line numbers to a section or to multiple sectionsClick Line Numbering Options, and then click the Layout tab. In the Apply to list, click Selected sections. Click Line Numbers. Select the Add line numbering check box, and then select the options that you want.

How do you add a block code in HTML?

For multi-line (“block”) code presentation, the most common approach is to use the <pre> element, and then put the <code> element inside that.

How do you show lines in HTML?

The <hr> tag defines a thematic break in an HTML page (e.g. a shift of topic). The <hr> element is most often displayed as a horizontal rule that is used to separate content (or define a change) in an HTML page.


3 Answers

You need to add display: inline-block to your ::before pseudo-element:

eg.

pre.code code::before {   content: counter(listing) ". ";   display: inline-block;   width: 8em;   padding-left: auto;   margin-left: auto;   text-align: right; } 

Then all your other styles will work.


Explanation: By default, ::before and ::after pseudo-elements have a display style of inline.

You need to explicitly declare a display style of block or inline-block if you want to start setting width, text-align etc.


Example:

pre.code {   white-space: pre-wrap; } pre.code::before {   counter-reset: listing; } pre.code code {   counter-increment: listing; } pre.code code::before {   content: counter(listing) ". ";   display: inline-block;   width: 8em;         /* now works */   padding-left: auto; /* now works */   margin-left: auto;  /* now works */   text-align: right;  /* now works */ }
<pre class="code"> <code>first line</code> <code>   indented line followed by empty line</code> <code></code> <code>the following line consists of one single space</code> <code> </code> <code>this line has a space at the end </code> <code>&#9;and one leading and ending tab&#9;</code> <code>fill to</code> <code>ten</code> <code>lines</code> </pre>

Added: An alternative CSS approach using floats and clears which means that when lines wrap, they do not wrap underneath the line number:

pre.code {   white-space: pre-wrap;   margin-left: 8em; }  pre.code::before {   counter-reset: listing; }  pre.code code {   counter-increment: listing;   text-align: left;   float: left;   clear: left; }  pre.code code::before {   content: counter(listing) ". ";   display: inline-block;   float: left;   height: 3em;   padding-left: auto;   margin-left: auto;   text-align: right; } 
like image 84
Rounin - Glory to UKRAINE Avatar answered Oct 16 '22 21:10

Rounin - Glory to UKRAINE


While technically my question is already answered by Rounin (thanks), truth is I was not fully satisfied with the result, because I didn't state a couple other requisites that I felt were important after posting the question, and they fail with that solution:

  1. When a line wraps around, it should go underneath the above line, not underneath the line numbers.
  2. Both the numbers and the text columns should be style-able (notably, the background of the whole column should be easily changeable).

I tried a solution with another inline-block, but it failed #5, and had problems with adjusting to any width.

<OL> was quite close, but not quite there. An alternative to <OL> that I knew wouldn't fly was to use a table. It wouldn't fly, because the browser needs a <PRE> element in order to copy/paste spaces and tabs properly.

Then all of a sudden, a solution clicked in my head. Forcing an element that is not a table to behave as if it was a table, is the exact purpose of the table-xxxx display values!

So here we go. Tested in Iceweasel 24.7.0 and Chromium 35.0.1916.153. The demo includes extra styling as an example of the versatility of the method.

The CSS:

/* Bare bones style for the desired effect */ pre.code {     display: table;     table-layout: fixed;     width: 100%; /* anything but auto, otherwise fixed layout not guaranteed */     white-space: pre-wrap; } pre.code::before {     counter-reset: linenum; } pre.code span.tr {     display: table-row;     counter-increment: linenum; } pre.code span.th { /* used for line numbers */     display: table-cell;     user-select: none;     -moz-user-select: none;     -webkit-user-select: none; } pre.code span.th::before {     content: counter(linenum);     text-align: right;     display: block; } pre.code span.th {     width: 4em; /* or whatever the desired width of the numbers column is */ } pre.code code {     display: table-cell; } 

And the HTML:

<pre class="code"> <span class="tr first-row"><span class="th"></span><code>   indented line</code></span> <span class="tr"><span class="th"></span><code>unindented line</code></span> <span class="tr"><span class="th"></span><code>&#9;line starting and ending with tab&#9;</code></span> <span class="tr"><span class="th"></span><code></code></span> <span class="tr"><span class="th"></span><code>the above line should be empty</code></span> <span class="tr"><span class="th"></span><code>overlong line that wraps around or so I hope because it's really long and should overflow the right sided margin of the web page in your browser</code></span> <span class="tr"><span class="th"></span><code>fill up to ten</code></span> <span class="tr"><span class="th"></span><code>lines to check</code></span> <span class="tr"><span class="th"></span><code>alignment</code></span> <span class="tr"><span class="th"></span><code>of numbers</code></span> </pre> 

Demo with extra styling

Update: Since I posted the question, learned that the GeSHi syntax highlighter has an option to use the following schema, which also meets all requisites and may be more acceptable to those that are allergic to tables:

<ol>   <li><pre>code</pre></li>   <li><pre>code</pre></li>   <li><pre>...</pre></li> </ol> 
like image 43
Pedro Gimeno Avatar answered Oct 16 '22 21:10

Pedro Gimeno


I'd recommend using codemirror to do this instead of writing it yourself.

You get the functionality you describe for free and lots of other great stuff:

https://codemirror.net/doc/manual.html#usage

CodeMirror.fromTextArea(document.getElementById("myDiv"), {
    lineNumbers: true,
    mode: "htmlmixed"
  });
like image 20
Mr Giggles Avatar answered Oct 16 '22 22:10

Mr Giggles