请马上登录,朋友们都在花潮里等着你哦:)
您需要 登录 才可以下载或查看,没有账号?立即注册
x
CSS的visibility属性极具兼容性:
自 2015年7月 起,visibility 属性已在主流浏览器中得到支持,可在大多数设备和浏览器版本中正常使用。
该属性显示或隐藏元素而不更改文档的布局。并且,当主元素通过该属性设置为隐藏时,其子元素可通过设置该属性为显示依然得到正常渲染。
根据 visibility 上述特性,我们可以不再拐弯抹角地为 textarea 创建行号了,也无需处理软换行垂直占位问题。具体思路是:在一个父元素的规范下垂直叠加、左右略微错开的两个元素,按文档流顺序安排,一为 div,二为 textarea,二者创建相同的影响排版布局的核心CSS属性值。div 根据 textarea 的输入动态添加 p 标签,p 标签 visibility 设置为 hidden,::before 或 ::after 伪元素用来显示行号、设置 visibility 为 visible,这样行号可以完美实现,且通过浏览器的 Ctrl+F 组合键查找关键字时查找结果不会成倍统计,因为 visibility 设为 hidden 的元素其上的文本浏览器会直接忽略。到目前止,这样的思路网上还没有人提到过。
说明:通过 visibility 设置 p 标签及其伪元素还有一个好处:可以不用特别定位伪元素,这种情形之下使用 ::before 伪元素比较自然,它将出现在 p 段落的开头处。当然,如果需要精准定位水平方向行号显示的位置,那么 p 标签及其伪元素应做定位管理。
以下是实现代码:
<style>
/* 编辑器父元素 */
.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; }
</style>
<!-- 编辑器HTML结构,行号容器将由JS生成 -->
<div class="ed-container">
<textarea id="editor"></textarea>
</div>
<script>
// 函数:生成行号容器(参数 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 => {
const value = computedStyle.getPropertyValue(prop);
lineNumberBox.style[prop] = 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) => {
const p = document.createElement('p');
p.className = 'lnum'; // 使用CSS预设的类名
p.innerText = line ? line : '<br>'; // 空行使用换行标签(避免坍塌)
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) => {
mkLineNum(editor, lnum);
});
// 编辑器滚动事件:保持行号同步翻滚
editor.addEventListener('scroll', () => {
lnum.scrollTop = editor.scrollTop;
});
// 页面初始化时加行号
document.addEventListener('DOMContentLoaded', () => {
mkLineNum(editor, lnum);
});
</script>
上述基于textarea的行号案例:
- 当前字号下支持最多4位数的行号显示,多了需要调整编辑框左移和行号底色占位尺寸;
- 适用于小型项目,处理一千行上下的代码应该无压力;
- 行号div容器也可以在CSS和HTML中维护但不建议,尽量在JS中复刻以确保容器和编辑器两个元素的核心属性相一致;
- 案例演示仅实现了核心行号功能,其它功能需要自行加入;
- 若用于生产环境,建议对编辑器对象进行更完备的封装。
|