马黑黑 发表于 2026-5-21 13:04

利用p标签的visibility属性给textarea加行号

<style>
        .artBox { font: normal 18px/1.5 sans-serif; overflow: auto; position: relative; }
        .artBox p { margin: 10px 0; }
        .artBox h1, .artBox h2 { margin: 8px 0; }
        .artBox code, .artBox pre { background: #f7f4f3; padding: 2px 6px; tab-size: 4; }
        .artBox pre { padding: 10px 20px; white-space: pre-wrap; word-wrap: break-word; }
        .artBox pre code { padding: 0; background: none; }
        .artBox blockquote { margin: 10px 20px; padding: 2px 15px; border-left: 3px solid skyblue; background: #e7e5e3; }
</style>

<div class="artBox">
    <p>CSS的visibility属性极具兼容性:</p>
    <blockquote>
      <p>自 2015年7月 起,visibility 属性已在主流浏览器中得到支持,可在大多数设备和浏览器版本中正常使用。</p>
    </blockquote>
    <p>该属性显示或隐藏元素而不更改文档的布局。并且,当主元素通过该属性设置为隐藏时,其子元素可通过设置该属性为显示依然得到正常渲染。</p>
    <p>根据 visibility 上述特性,我们可以不再拐弯抹角地为 textarea 创建行号了,也无需处理软换行垂直占位问题。具体思路是:在一个父元素的规范下垂直叠加、左右略微错开的两个元素,按文档流顺序安排,一为 div,二为 textarea,二者创建相同的影响排版布局的核心CSS属性值。div 根据 textarea 的输入动态添加 p 标签,p 标签 visibility 设置为 hidden,::before 或 ::after 伪元素用来显示行号、设置 visibility 为 visible,这样行号可以完美实现,且通过浏览器的 Ctrl+F 组合键查找关键字时查找结果不会成倍统计,因为 visibility 设为 hidden 的元素其上的文本浏览器会直接忽略。到目前止,这样的思路网上还没有人提到过。</p>
    <blockquote>
      <p>说明:通过 visibility 设置 p 标签及其伪元素还有一个好处:可以不用特别定位伪元素,这种情形之下使用 ::before 伪元素比较自然,它将出现在 p 段落的开头处。当然,如果需要精准定位水平方向行号显示的位置,那么 p 标签及其伪元素应做定位管理。</p>
    </blockquote>
    <p>以下是实现代码:</p>

        <div class="codebox" data-prev="1">
&lt;style&gt;
    /* 编辑器父元素 */
    .ed-container { margin: 30px auto; width: 800px; height: 450px; border: 1px solid gray; background: linear-gradient(90deg, #ddd 50px, white 50px); padding: 0; position: relative; }
    /* 行号p标签类选择器及其伪元素(其父元素属性设置交给JS完成) */
    .lnum { margin: 0; padding: 0; visibility: hidden; }
    .lnum::before { content: attr(data-num); width: 50px; color: #888; padding: 0; visibility: visible; }
    /* 编辑器 */
    #editor { position: absolute; left: 50px; width: calc(100% - 50px); height: 100%; box-sizing: border-box; padding: 8px; font-size: 18px; background: #fff; border: none; outline: none; resize: none; }
&lt;/style&gt;
&lt;!-- 编辑器HTML结构,行号容器将由JS生成 --&gt;
&lt;div class="ed-container"&gt;
    &lt;textarea id="editor"&gt;&lt;/textarea&gt;
&lt;/div&gt;
&lt;script&gt;
    // 函数:生成行号容器(参数 pa 是 textarea 的父元素)
    function createLnumBox(pa, textarea) {
      // 创建行号div元素
      const lineNumberBox = document.createElement('div');
      // 核心CSS属性
      const keyProperties = [
            'font', 'line-height', 'padding', 'margin', 'overflow',
            'width', 'height', 'white-space', 'word-break',
            'overflow-wrap', 'box-sizing', 'position'
      ];
      // 读取编辑器元素CSS属性
      const computedStyle = window.getComputedStyle(textarea);
      // 将核心CSS属性复刻到行号容器
      keyProperties.forEach(prop =&gt; {
            const value = computedStyle.getPropertyValue(prop);
            lineNumberBox.style = value;
      });
      pa.prepend(lineNumberBox); // 行号容器加到 textarea 之前
      return lineNumberBox; // 返回容器(便于后续操作)
    }
    // 函数:动态生成行号
    function mkLineNum(textarea, lnumDiv) {
      lnumDiv.innerHTML = ''; // 清空行号容器内容
      // 获取编辑器内容并按行拆分
      const lines = textarea.value.split('\n');
      const frg = new DocumentFragment(); // 创建文档碎片
      // 每一行都生成p标签加入到行号容器中
      lines.forEach((line, key) =&gt; {
            const p = document.createElement('p');
            p.className = 'lnum'; // 使用CSS预设的类名
            p.innerText = line ? line : '&lt;br&gt;'; // 空行使用换行标签(避免坍塌)
            p.dataset.num = key + 1; // p标签伪元素数据:行号
            frg.appendChild(p); // p标签加入到文档碎片
      });
      lnumDiv.appendChild(frg); // 文档碎片加入到行号div容器
    }
    const editor = document.getElementById('editor'); // 获取编辑器
    const container = document.querySelector('.ed-container'); // 获取编辑器父元素
    const lnum = createLnumBox(container, editor); // 创建行号容器
    // 编辑器输入事件:触发行号生成
    editor.addEventListener('input', (e) =&gt; {
      mkLineNum(editor, lnum);
    });
    // 编辑器滚动事件:保持行号同步翻滚
    editor.addEventListener('scroll', () =&gt; {
      lnum.scrollTop = editor.scrollTop;
    });
    // 页面初始化时加行号
    document.addEventListener('DOMContentLoaded', () =&gt; {
      mkLineNum(editor, lnum);
    });
&lt;/script&gt;
        </div>

    <p>上述基于textarea的行号案例:</p>
    <ul>
      <li>当前字号下支持最多4位数的行号显示,多了需要调整编辑框左移和行号底色占位尺寸;</li>
      <li>适用于小型项目,处理一千行上下的代码应该无压力;</li>
      <li>行号div容器也可以在CSS和HTML中维护但不建议,尽量在JS中复刻以确保容器和编辑器两个元素的核心属性相一致;</li>
      <li>案例演示仅实现了核心行号功能,其它功能需要自行加入;</li>
      <li>若用于生产环境,建议对编辑器对象进行更完备的封装。</li>
    </ul>
</div>

<script>
   function loadLineNumFile() {
       const script = document.createElement('script');
       script.charset = 'utf-8';
       script.src = 'https://638183.freep.cn/638183/web/helight/linenumber-2026.js';
       script.defer = true;
       script.onload = () => addLineNumber();
       document.head.appendChild(script);
   }

   loadLineNumFile();
</script>

梦江南 发表于 2026-5-21 13:14

谢谢黑黑老师辛苦,学习了。

霜染枫丹 发表于 2026-5-21 18:47

这天书看不懂就不看了,进来祝马老师小满快乐~~{:4_204:}{:4_190:}

红影 发表于 2026-5-21 21:55

这个是设计行号的么,感觉有些复杂呢。
辛苦黑黑,学习了{:4_187:}

马黑黑 发表于 2026-5-22 19:58

红影 发表于 2026-5-21 21:55
这个是设计行号的么,感觉有些复杂呢。
辛苦黑黑,学习了

这个应该是简单的

马黑黑 发表于 2026-5-22 19:59

霜染枫丹 发表于 2026-5-21 18:47
这天书看不懂就不看了,进来祝马老师小满快乐~~

{:4_180:}

马黑黑 发表于 2026-5-22 19:59

梦江南 发表于 2026-5-21 13:14
谢谢黑黑老师辛苦,学习了。

{:4_190:}

梦江南 发表于 2026-5-22 20:24

马黑黑 发表于 2026-5-22 19:59


黑黑老师晚上嚎!

红影 发表于 2026-5-22 23:16

马黑黑 发表于 2026-5-22 19:58
这个应该是简单的

嗯嗯,也许因为不熟悉,所以感觉复杂{:4_173:}

马黑黑 发表于 2026-5-23 12:02

红影 发表于 2026-5-22 23:16
嗯嗯,也许因为不熟悉,所以感觉复杂

首先是UI设计:

行号容器+编辑器叠加在一个共同的父元素里面,编辑器右移一定距离;

其次是实现代码,步骤是:

一、动态生成和编辑器一样的尺寸、一样的核心CSS属性的div容器,它就是行号容器;
二、动态生成行号 p 标签,p标签的内容时编辑器里的每一行,p 标签不可见,伪元素则是显示行号,伪元素可见。p 标签添加到行号 div 容器里。
三、处理滚动:编辑器滚动时,行号同步滚动。

马黑黑 发表于 2026-5-23 12:02

梦江南 发表于 2026-5-22 20:24
黑黑老师晚上嚎!

中午嚎!

红影 发表于 2026-5-23 22:39

马黑黑 发表于 2026-5-23 12:02
首先是UI设计:

行号容器+编辑器叠加在一个共同的父元素里面,编辑器右移一定距离;


黑黑真熟悉,随口就来。
感谢黑黑的耐心讲解{:4_187:}

马黑黑 发表于 2026-5-24 20:44

红影 发表于 2026-5-23 22:39
黑黑真熟悉,随口就来。
感谢黑黑的耐心讲解

这个是我设计的,当然了然于胸

红影 发表于 2026-5-24 22:58

马黑黑 发表于 2026-5-24 20:44
这个是我设计的,当然了然于胸

厉害,太赞了{:4_199:}

马黑黑 发表于 2026-5-25 12:07

红影 发表于 2026-5-24 22:58
厉害,太赞了

自己设计的自己清楚,正常,谈不上厉害
页: [1]
查看完整版本: 利用p标签的visibility属性给textarea加行号