分享

为网页中的源代码自动设置行号

 zhuoyue图书馆 2012-05-10
分类: CSSJavaScriptWebDev635人阅读评论(0)收藏举报

本文演示了如何为网页中的源代码块自动设置行号。

现在,我们准备在网页中显示C++的Hello world。这段著名的代码如下所示:

  1. #include <iostream>  
  2.   
  3. int main()  
  4. {  
  5.     std::cout << "Hello world";  
  6.     return 0;  
  7. }  

在HTML中,一般使用<pre>来显示源代码。

  1. <pre class="sourceCode">#include <iostream>  
  2. int main()  
  3. {  
  4.   std::cout << "Hello world";  
  5.   return 0;  
  6. }</pre>  

第一行中,因为“<”及“>”是HTML标签的标记,因此,我们需要将其转义,分别对应于“<”及“>”。虽然使用HTML表示这两个符号较怪,但在浏览器中就可以显示出正确的符号。(注:在Dreamweaver或其他HTML编辑器中,我们在设计视图中直接输入这两个符号,这些软件会自动为我们转义。)

在网页中显示效果如下:

这段源代码没有格式,也没有行号,因此看起来很不舒服。本文的目标,是将上面的源代码在网页中的显示转换为下面的形式:

其特点是:

  1. 自动缩进;
  2. 自动生成行号;
  3. 奇偶行以不同颜色显示;
  4. 高亮显示鼠标所指之行。

现在让我们开始这段奇幻之旅。

1. 稍加粉饰

先用CSS给它化点小妆。

  1. <style type="text/css">  
  2.   pre.sourceCode {  
  3.     background-color#9FB5DE;  
  4.     border1px solid black;  
  5.     margin-left2em;  
  6.   }  
  7. </style>  

效果:


背景为蓝色,黑边,且缩进。

2. Javascript前来整容

在HTML中,OL的每一个LI可以自动编号,可以将每行代码都转换为OL中的LI后可解决每行的自动编号问题。至于每行的不同颜色,可为LI设置不同的className属性。由于行号需要与代码区分开来,可为代码内容增加一个SPAN。因此,问题可细化为:

  1. 取出<pre>中的每一行代码
  2. 将每行代码转化为<li>,为此<li>设置代表奇偶行的className,且将代码内容置于<span>中
  3. 将每个生成的<li>置于<ol>之下
  4. 删除<pre>节点下的所有子节点
  5. 在<pre>节点之下增加<ol>子节点

通过Javascript中的DOM,我们可以轻松地实现以上步骤。

[javascript] view plaincopy?
  1. <script type="text/javascript">  
  2. window.onload = function () {  
  3.     var preElements = document.getElementsByTagName("pre");  
  4.     for (var i = 0; i < preElements.length; i++) {  
  5.         var target = preElements.item(i);  
  6.         if (target.className != "sourceCode") {  
  7.             continue;  
  8.         }  
  9.         var content = target.firstChild.nodeValue;  
  10.         var linesArray = content.split(String.fromCharCode(13));  
  11.         if (linesArray.length == 1) {  
  12.             linesArray = content.split(String.fromCharCode(10));  
  13.         }  
  14.   
  15.         var olContainer = document.createElement("ol");  
  16.         var index = 1;  
  17.         for (var j = 0; j < linesArray.length; j++) {  
  18.             var liContainer = document.createElement("li");  
  19.             liContainer.className = index++ % 2 ? "odd" : "even";  
  20.             var spanContainer = document.createElement("span");  
  21.             var aText = document.createTextNode(linesArray[j]);  
  22.             spanContainer.appendChild(aText);  
  23.             liContainer.appendChild(spanContainer);  
  24.             olContainer.appendChild(liContainer);  
  25.         }  
  26.         while (target.firstChild) {  
  27.             target.removeChild(target.firstChild);  
  28.         }  
  29.   
  30.         target.appendChild(olContainer);  
  31.     }  
  32. }  
  33. </script>  

效果如下:

Javascript代码的长度貌似吓人,但每一步骤都较清晰。

第2行,使用一个匿名函数,在页面加载完毕时自动执行所有任务。

第3行,取得所有<pre>标签。第5行至第36行,对每个<pre>标签进行加工。

第6行,将每个<pre>标签存至一个名为target的变量。第7至第9行,如果其className不是“sourceCode”,则不是我们所关心的内容,直接跳过。注意className这个非常特殊的属性(attribute),我们不能使用getAttribute()来获得此属性值,而应直接通过该节点的className属性(property)来取出。Web标准还管不管了?

由于每个<pre>下面只有一个文本型的子节点,因此在第11行,可通过其firstChild.nodeValue来快速取得其内容。

第12至15行开始对其内容进行断行。换行符在IE中表现为代码为13的字符,而在Firefox中却只为10。因此,先应对IE后,再来对付Firefox。Web标准路漫漫,连CharCode都暗藏杀机!断行后,linesArray变量成为存储每行内容的数组。

对每行内容,第21行根据其奇偶性分别设置“even”及“odd”的className,第24行为每行内容创建一个文本节点,第26至28行,文本->span->li>ol。这样,第17行所创建的<ol>就布满了所需的内容。但此<ol>目前只存在于内存中,还看不见。

第31至33行,将<pre>原来的内容全部删除,第35行换上加工完毕的<ol>。移形换位,不是吗?

此时,查看页面源文件,我们看不出经过幕后整容后的真实DOM。Firefox可通过DOM Inspector来查看DOM结构。IE8也赶了新潮流。在IE8中按F12,调出开发人员工具,按Ctrl + G,就可以让其现形了。例如,对于源代码的第2行,其DOM结构如下:

  1. <LI class="odd">  
  2.   <SPAN>int main()</SPAN>  
  3. </LI>  

在第2步中,章子怡(CSS)刚一露脸,就被张艺谋喊停:“停!舞台未妥,急甚急甚?”。现在既然张艺谋已经布置好舞台,章子怡兴高采烈地重新粉墨登场了。

3. CSS现身化妆

序号 CSS 目前效果 解析
1
pre.sourceCode ol {
  padding: 0;
  margin: 0;
  margin-left: 0;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}

我们先设置<ol>的样式。根据第2步的第35行,在DOM层级中,现在<ol>是<pre>的直接子节点。

先将<ol>的padding及margin先设为0。我们发现一个比较有趣的现象:<ol>所在的box不包含<li>的序号。并且随着<ol>的margin-left的值越大,<ol>所在区域将逐渐<li>的标号包含进来。

下面我们观察margin-left的值分别设为0、5、10、20、35时的不同情况。

当margin-left等于0时的情况

pre.sourceCode ol {
  padding: 0;
  margin: 0;
  margin-left: 5px;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}
当margin-left等于5px时的情况
pre.sourceCode ol {
  padding: 0;
  margin: 0;
  margin-left: 10px;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}
当margin-left等于10px时的情况
pre.sourceCode ol {
  padding: 0;
  margin: 0;
  margin-left: 20px;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}
当margin-left等于20px时的情况
pre.sourceCode ol {
  padding: 0;
  margin: 0;
  margin-left: 35px;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}

当margin-left等于35px时的情况。对于百行之内的源代码,35px的值应该是较好的选择。

原因:<ol>有一个名为“list-sytle-position”的属性,其值可设为inside及outside。当该值设为outside时,<ol>所在box将不包含其下<li>序号。这是默认值。当该值设为inside时,<ol>所在box将包含其下<li>序号。上面的实验说明了一点:在该属性为outside时,随着<ol>的margin-left值增大,可以实现inside的效果。

当<ol>的list-style-position之值设为inside时,更易实现本文的目标。但由于其值默认为outside,故本文不厌其烦地选择outside作为实现方式。

2
pre.sourceCode li
{
  padding: 2px 10px;
  border-left: 2px groove silver;
  color: #333;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}

开始设置<li>。

padding设置行距。

border-left将序号及代码内容分开。

颜色设为稍淡点,以免分散读者阅读主文的注意力。

3
pre.sourceCode li.odd {
  background-color: #dbdbdb;
}
pre.sourceCode li.even {
  background-color: #d4d4d4;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}

设置<li>的奇偶行。只需分别设置不同的背景色即可。

4
pre.sourceCode li:hover
{
  background-color: #9FB5DE;
}
#include <iostream>

int main()
{
  std::cout << "Hello world";
  return 0;
}

高亮显示鼠标所在行。IE8已经支持非链接标签的hover伪类,喜讯。现在,左右两边的效果已经完全一致了。

结语

就像张艺谋与章子怡这对好搭档经常获得电影节大奖一样,Javascript与CSS的完美组合,可收到出乎意料之外的效果。解决问题时,如果我们能充分发挥Javascript及CSS的长处,可弥补现在HTML中Web组件的匮乏。

(本文通过IE8/WinXP的环境下的测试)

(注:因为CSDN的博客不鼓励博主在发表文章时使用CSS及JavaScript,因此本文的一些需要CSS及JavaScript支持的特点无法显示出来。因此,拟将效果转换为图片的形式表现出来。本文尚在转化中。 2011-7-27)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约