我有一个像下面代码中的 <textarea>
元素。如何在左边距上显示行号?
<TEXTAREA name="program" id="program" rows="15" cols="65" ></TEXTAREA>
我有一个像下面代码中的 <textarea>
元素。如何在左边距上显示行号?
<TEXTAREA name="program" id="program" rows="15" cols="65" ></TEXTAREA>
Alan Williamson编写了一个基于jQuery的Lined TextArea插件(链接已失效,请参考存档),适用于jQuery版本1.3或更高版本。
MIT许可证。
textarea.numbered {
background: url(http://i.imgur.com/2cOaJ.png);
background-attachment: local;
background-repeat: no-repeat;
padding-left: 35px;
padding-top: 10px;
border-color:#ccc;
}
<textarea cols="50" rows="10" class="numbered"></textarea>
这里有人推荐了CodeMirror,我强烈推荐使用!但是这个答案没有提供任何技术细节。
其他的解决方案:我试过的所有其他方案都存在行号与行不匹配的问题。我认为这是因为我的显示器DPI(每英寸像素数)是120%,而这些解决方案没有考虑这一点。
那么怎样使用CodeMirror呢?很容易!只需查看21000字的文档!我希望在一页或两页内就能解答你对它的99%的问题。
这是一个100%工作的演示,并且在StackOverflow沙箱中完美运行:
var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
lineNumbers: true,
mode: 'text/x-perl',
theme: 'abbott',
});
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js"></script>
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/perl/perl.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.css"></link>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/theme/abbott.min.css"></link>
<textarea id="code" name="code">
if($cool_variable) {
doTheCoolThing(); # it's PRETTY cool, imho
}</textarea>
将以下内容添加到您的 <head>
块中...
<script language="javascript" type="text/javascript" src="/static/js/codemirror-5.62.0/lib/codemirror.js"></script>
<link rel="stylesheet" type="text/css" href="/static/js/codemirror-5.62.0/lib/codemirror.css"></link>
如果想要额外的括号颜色匹配,也请加载此内容:
<script language="javascript" type="text/javascript" src="/codemirror-5.62.0/addon/edit/matchbrackets.js"></script>
检查 /codemirror-5.62.0/mode/
文件夹以查看与您要编写代码的语言匹配的语言。在这个领域有广泛的支持。
将以下内容添加到您的<head>
区块中...
<script language="javascript" type="text/javascript" src="/static/js/codemirror-5.62.0/mode/perl/perl.js"></script>
有一些要使用的文本区域....
<textarea id="code" name="code"></textarea>
在JS中初始化和设置您的CodeMirror。您需要使用Mimetype来指示您想要使用的模式,在这种情况下,我正在指示Perl Mimetype...
var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
lineNumbers: true,
mode: 'text/x-perl',
matchBrackets: true,
});
选择你喜欢的一些主题,'liquibyte'
、'cobalt'
和'abbott'
这几个主题都是相当不错的暗色模式风格。在定义editor
之后运行以下代码...
editor.setOption('theme', 'cobalt');
就是这样了!
simplemode
插件。 - mvreijn//
// desc: demonstrates textarea line numbers using canvas paint
// auth: nikola bozovic <nigerija@gmail>
//
var TextAreaLineNumbersWithCanvas = function() {
var div = document.getElementById('wrapper');
var cssTable = 'padding:0px 0px 0px 0px!important; margin:0px 0px 0px 0px!important; font-size:1px;line-height:0px; width:auto;';
var cssTd1 = 'border:1px #345 solid; border-right:0px; vertical-align:top; width:1px; background: #303030';
var cssTd2 = 'border:1px #345 solid; border-left:0px; vertical-align:top;';
var cssButton = 'width:120px; height:40px; border:1px solid #333 !important; border-bottom-color: #484!important; color:#ffe; background-color:#222;';
var cssCanvas = 'border:0px; background-color:#1c1c20; margin-top:0px; padding-top:0px;';
// LAYOUT (table 2 panels)
var table = document.createElement('table');
table.setAttribute('cellspacing', '0');
table.setAttribute('cellpadding', '0');
table.setAttribute('style', cssTable);
var tr = document.createElement('tr');
var td1 = document.createElement('td');
td1.setAttribute('style', cssTd1);
var td2 = document.createElement('td');
td2.setAttribute('style', cssTd2);
tr.appendChild(td1);
tr.appendChild(td2);
table.appendChild(tr);
// TEXTAREA
var ta = this.evalnode = document.getElementById('mytextarea');
// TEXTAREA NUMBERS (Canvas)
var canvas = document.createElement('canvas');
canvas.width = 48; // must not set width & height in css !!!
canvas.height = 500; // must not set width & height in css !!!
canvas.setAttribute('style', cssCanvas);
ta.canvasLines = canvas;
td1.appendChild(canvas);
td2.appendChild(ta);
div.appendChild(table);
// PAINT LINE NUMBERS
ta.paintLineNumbers = function() {
try {
var canvas = this.canvasLines;
if (canvas.height != this.clientHeight) canvas.height = this.clientHeight; // on resize
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#303030";
ctx.fillRect(0, 0, 42, this.scrollHeight + 1);
ctx.fillStyle = "#808080";
ctx.font = "11px monospace"; // NOTICE: must match TextArea font-size(11px) and lineheight(15) !!!
var startIndex = Math.floor(this.scrollTop / 15, 0);
var endIndex = startIndex + Math.ceil(this.clientHeight / 15, 0);
for (var i = startIndex; i < endIndex; i++) {
var ph = 10 - this.scrollTop + (i * 15);
var text = '' + (1 + i); // line number
ctx.fillText(text, 40 - (text.length * 6), ph);
}
} catch (e) {
alert(e);
}
};
ta.onscroll = function(ev) {
this.paintLineNumbers();
};
ta.onmousedown = function(ev) {
this.mouseisdown = true;
}
ta.onmouseup = function(ev) {
this.mouseisdown = false;
this.paintLineNumbers();
};
ta.onmousemove = function(ev) {
if (this.mouseisdown) this.paintLineNumbers();
};
// make sure it's painted
ta.paintLineNumbers();
return ta;
};
var ta = TextAreaLineNumbersWithCanvas();
ta.value = TextAreaLineNumbersWithCanvas.toString();
#mytextarea {
width: auto;
height: 500px;
font-size: 11px;
font-family: monospace;
line-height: 15px;
font-weight: 500;
margin: 0;
padding: 0;
resize: both;
color: #ffa;
border: 0;
background-color: #222;
white-space: pre;
overflow: auto;
}
/* supported only in opera */
#mytextarea {
scrollbar-arrow-color: #ee8;
scrollbar-base-color: #444;
scrollbar-track-color: #666;
scrollbar-face-color: #444;
/* outer light */
scrollbar-3dlight-color: #444;
/* inner light */
scrollbar-highlight-color: #666;
/* outer dark */
scrollbar-darkshadow-color: #444;
/* inner dark */
scrollbar-shadow-color: #222;
}
/* chrome scrollbars */
textarea::-webkit-scrollbar {
width: 16px;
background-color: #444;
cursor: pointer;
}
textarea::-webkit-scrollbar-track {
background-color: #333;
cursor: pointer;
}
textarea::-webkit-scrollbar-corner {
background-color: #484;
-webkit-box-shadow: inset 0 0 6px rgba(255, 255, 255, 0.3);
}
textarea::-webkit-scrollbar-thumb {
background-color: #444;
-webkit-box-shadow: inset 0 0 6px rgba(255, 255, 255, 0.3);
cursor: pointer;
}
<div id="wrapper">
<textarea id="mytextarea" cols="80" rows="10"></textarea>
</div>
这里有一个限制,我们不能在Paint()
函数中轻松处理word-wrap
,而不迭代整个文本区域的内容,并绘制到一个隐藏对象以测量每行的高度,否则将会产生非常复杂的代码。
function generateWithNumber() {
let inputTexts = document.getElementById("input").value
let textsByLine = inputTexts.split("\n");
const listMarkup = makeUL(textsByLine);
document.getElementById("output").appendChild(listMarkup);
}
function makeUL(array) {
let list = document.createElement('ol');
for (let i = 0; i < array.length; i++) {
let item = document.createElement('li');
item.appendChild(document.createTextNode(array[i]));
list.appendChild(item);
}
return list;
}
// document.getElementById('foo').appendChild(makeUL(options[0]));
ol {
counter-reset: list;
}
ol > li {
list-style: none;
}
ol > li:before {
content: counter(list) ") ";
counter-increment: list;
}
<textarea id="input"></textarea>
<button onClick=generateWithNumber() >Generate</button>
<p id="output"></p>
这段代码定义了一个名为generateWithNumber()
的函数,该函数在单击带有onClick
事件的按钮时触发。此函数的目的是从<textarea>
元素中获取输入文本,将其分割成行,并在<ol>
(有序列表)元素中将行显示为带有编号的列表。
Consider the use of a contenteditable
ordered list <ol>
instead of <textarea>
ol {
font-family: monospace;
white-space: pre;
}
li::marker {
font-size: 10px;
color: grey;
}
<ol contenteditable><li>lorem ipsum
<li>>> lorem ipsum
<li>lorem ipsum,\
<li>lorem ipsum.
<li>>> lorem ipsum
<li>lorem ipsum
<li>lorem ipsum
<li>lorem
<li>ipsum
<li>>> lorem ipsum
<li>lorem ipsum
</ol>
::marker
样式似乎受限于 (list-style-type
)。例如,去除句点或使用 vertical-align: super
等需要其他解决方法(回到 li:before
和 counter
)。<li>
标签不需要闭合标签 </li>
(https://html.spec.whatwg.org/multipage/syntax.html#optional-tags),这样可以节省打字时间。<textarea>
只是在后台工作(Pseudo contenteditable: how does codemirror works?)。const textarea = document.querySelector("textarea");
const numbers = document.querySelector(".numbers");
textarea.addEventListener("keyup", (e) => {
const num = e.target.value.split("\n").length;
numbers.innerHTML = Array(num).fill("<span></span>").join("");
});
textarea.addEventListener("keydown", (event) => {
if (event.key === "Tab") {
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
textarea.value =
textarea.value.substring(0, start) +
"\t" +
textarea.value.substring(end);
event.preventDefault();
}
});
body {
font-family: Consolas, "Courier New", Courier, monospace;
}
.editor {
display: inline-flex;
gap: 10px;
font-family: Consolas, "Courier New", Courier, monospace;
line-height: 21px;
background-color: #282a3a;
border-radius: 2px;
padding: 20px 10px;
}
textarea {
line-height: 21px;
overflow-y: hidden;
padding: 0;
border: 0;
background: #282a3a;
color: #fff;
min-width: 500px;
outline: none;
resize: none;
font-family: Consolas, "Courier New", Courier, monospace;
}
.numbers {
width: 20px;
text-align: right;
}
.numbers span {
counter-increment: linenumber;
}
.numbers span::before {
content: counter(linenumber);
display: block;
color: #506882;
}
<div class="editor">
<div class="numbers">
<span></span>
</div>
<textarea cols="30" rows="10"></textarea>
</div>
它实际上运行得相当不错。
行号并非瞬间出现,但速度相当快。
'use scrict';
var linenumbers = document.getElementById('line-numbers');
var editor = document.getElementById('codeblock');
function getWidth(elem) {
return elem.scrollWidth - (parseFloat(window.getComputedStyle(elem, null).getPropertyValue('padding-left')) + parseFloat(window.getComputedStyle(elem, null).getPropertyValue('padding-right')))
}
function getFontSize(elem) {
return parseFloat(window.getComputedStyle(elem, null).getPropertyValue('font-size'));
}
function cutLines(lines) {
return lines.split(/\r?\n/);
}
function getLineHeight(elem) {
var computedStyle = window.getComputedStyle(elem);
var lineHeight = computedStyle.getPropertyValue('line-height');
var lineheight;
if (lineHeight === 'normal') {
var fontSize = computedStyle.getPropertyValue('font-size');
lineheight = parseFloat(fontSize) * 1.2;
} else {
lineheight = parseFloat(lineHeight);
}
return lineheight;
}
function getTotalLineSize(size, line, options) {
if (typeof options === 'object') options = {};
var p = document.createElement('span');
p.style.setProperty('white-space', 'pre');
p.style.display = 'inline-block';
if (typeof options.fontSize !== 'undefined') p.style.fontSize = options.fontSize;
p.innerHTML = line;
document.body.appendChild(p);
var result = (p.scrollWidth / size);
p.remove();
return Math.ceil(result);
}
function getLineNumber() {
var textLines = editor.value.substr(0, editor.selectionStart).split("\n");
var currentLineNumber = textLines.length;
var currentColumnIndex = textLines[textLines.length-1].length;
return currentLineNumber;
}
function init() {
var totallines = cutLines(editor.value), linesize;
linenumbers.innerHTML = '';
for (var i = 1; i <= totallines.length; i++) {
var num = document.createElement('p');
num.innerHTML = i;
linenumbers.appendChild(num);
linesize = getTotalLineSize(getWidth(editor), totallines[(i - 1)], {'fontSize' : getFontSize(editor)});
if (linesize > 1) {
num.style.height = (linesize * getLineHeight(editor)) + 'px';
}
}
linesize = getTotalLineSize(getWidth(editor), totallines[(getLineNumber() - 1)], {'fontSize' : getFontSize(editor)});
if (linesize > 1) {
linenumbers.childNodes[(getLineNumber() - 1)].style.height = (linesize * getLineHeight(editor)) + 'px';
}
editor.style.height = editor.scrollHeight;
linenumbers.style.height = editor.scrollHeight;
}
editor.addEventListener('keyup', init);
editor.addEventListener('input', init);
editor.addEventListener('click', init);
editor.addEventListener('paste', init);
editor.addEventListener('load', init);
editor.addEventListener('mouseover', init);
#source-code {
width: 100%;
height: 450px;
background-color: #2F2F2F;
display: flex;
justify-content: space-between;
overflow-y: scroll;
border-radius: 10px;
}
#source-code * {
box-sizing: border-box;
}
#codeblock {
white-space: pre-wrap;
width: calc(100% - 30px);
float: right;
height: auto;
font-family: arial;
color: #fff;
background: transparent;
padding: 15px;
line-height: 30px;
overflow: hidden;
min-height: 100%;
border: none;
}
#line-numbers {
min-width: 30px;
height: 100%;
padding: 15px 5px;
font-size: 14px;
vertical-align: middle;
text-align: right;
margin: 0;
color: #fff;
background: black;
}
#line-numbers p {
display: block;
height: 30px;
line-height: 30px;
margin: 0;
}
#codeblock:focus{
outline: none;
}
<div id="source-code">
<div id="line-numbers"><p>1</p></div>
<textarea id="codeblock"></textarea>
</div>