Wednesday, November 3, 2021

Javascript/CSS: Add Line Number to HTML Source Code Block

 When I shared the source code in my blog before, I used to reference source code by highlighting the code lines with HTML tag. It was not an issue until I had many references to different lines of a long clode block. It could take too much of my time to highlight or mark the reference when I post an article. Therefore, I added the line number to the source code in my posts. The line-numbered code block looks like,

CREATE TABLE part_time_employees (
    empno NUMBER(8),
    name VARCHAR2(30),
    hourly_rate NUMBER (7,2)
    )   
    SEGMENT CREATION DEFERRED;
There are many ways to implement this function. Most straight way is to code with html element (e.g. <table>), but I do not want to deal with the html code every time when I post program code. I am going to use pure Javascript and CSS to do that, it will be done once and automatically apply to all my subsequent posts.

Step 1, create CSS style
<style type="text/css">
  .dpw-code {
      white-space: pre-wrap; /* Wrap long lines */
      word-break: break-all;
      counter-reset: linecounter; /* Create line counter variable */
      overflow: auto;
      border-width: 1px;
      border-style: solid;
      border-color: #c4d1e6;
      padding: 5px;
      font-family: "Courier New",Courier,monospace;
      background-color: #e0eaf1;
  }
  .dpw-code span.line {
    counter-increment: linecounter;
  }
  .dpw-code span.line::before {
    content: counter(linecounter);
    width: 20px;
    text-align: right;
    display: inline-block;
    padding-right: 5px;
    border-right: 1px solid #ccc;
    margin-right: 5px;
    background-color: #d8e7f1;
  }
</style>
Line 2-13 defines a css style for all html elements which class are "dpw-code". In this style, line 5 is the key line, the property "counter-reset" creates a variable for counting lines. The roles of other properties are literal as their names.

Line 14-16 defines style for "span" elements which are inside element which class is "dpw-code" and the "span" class has to be "line". The style explictly defines only one property "counter-increment", it increases the value of variable "linecounter" by 1. 1 is default incremental number, you can specify other incremental number (for example 2) following variable "linecounter" and separate variable and number with space.

Line 17-26 defines style for a field before each "span" element which class is "line", and also the element has to be within the element which class is "dpw-code". The property "content" (line 18) tells browser that the field should display value of variable "linecounter". Here, function counter() returns the value of variable "linecounter" as a string.

Step 2, create Javascript script to convert normal source code (usually plan text within "pre" element) to line-numbered block,
<script type="text/javascript">
  window.addEventListener("load", function () {
    var codes = document.getElementsByClassName("dpw-code");
    for (var i = 0; i < codes.length; i++) {
        lineToSpan(codes[i]);
    }
  }, false);
  function lineToSpan (code) {
    var lines = code.innerHTML.split("\n");
    while(code.childNodes.length > 0) {
        code.removeChild(code.childNodes[0]);
    }
    for(var i = 0; i < lines.length; i++) {
        var span = document.createElement("span");
        span.className = "line";
        span.innerHTML = lines[i];
        code.appendChild(span);
        code.appendChild(document.createTextNode("\n"));
    }
  };
</script>
Line 8-20 creates a function "lineToSpan". The function input parameter "code" accepts HTML element which containers the code block, usually it is "pre" element. The funciton convert each text line to "span" element (enclose text line with span tag).

Line 15 sets new-created "span" element class to "line". This is important because the style defined in step 1 is based on the class of span. In another word, span and line created by this function correspond with the element and class in the css style defined in step 1.

Technically, property "innerHTML" can be replaced with "innerText", but it is not recommended. There are no difference if your source code block is plan text, but if you want emphasize some by html-formatting (e.g. red-print words as I did in this post), "innerText" option will not display the format properly (print in red).

Line 2-7 register a listener for window load event, it makes client browser call an anonymous function (line 3-5) when the window is loaded. 

Line 3 lists all elements which class are "dpw-code". Line 4-5 is a for-loop which call function "lineToSpan" for each element found in line 3.

Step 3, copy the css style created in at step 1 and Javascript script created at step 2 to "head" section of the web page. For me, it is home page header of my blog. Now, we are ready to type the souce code within the blog post as following
<pre class="dpw-code">
 <<< type your code here>>>>
</pre>
Note: as soon as you set class of "pre" element to "dpw-code", the css style defined in step 1 and Javascript created in step 2 will help you format your code.

No comments: