ライブラリを使用する代わりに自分でソリューションを構築したい場合は、contenteditable
要素とその後に偽の正方形のカレットを使用できます。カレットが別の位置に移動すると、その偽のカレットは非表示になり、代わりに実際の垂直線が表示されます。
ただし、オーバータイプモードが無効になっている場合でも、このコードを微調整して常に1文字を選択し、カレットが常に1文字幅の正方形になるようにすることができます。
コマンドを表示しているだけですが、コマンドを別の方法で処理するのは簡単です。
const history = document.getElementById('history');
const input = document.getElementById('input');
const cursor = document.getElementById('cursor');
function focusAndMoveCursorToTheEnd(e) {
input.focus();
const range = document.createRange();
const selection = window.getSelection();
const { childNodes } = input;
const lastChildNode = childNodes && childNodes.length - 1;
range.selectNodeContents(lastChildNode === -1 ? input : childNodes[lastChildNode]);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
}
function handleCommand(command) {
const line = document.createElement('DIV');
line.textContent = `> ${ command }`;
history.appendChild(line);
}
// Every time the selection changes, add or remove the .noCursor
// class to show or hide, respectively, the bug square cursor.
// Note this function could also be used to enforce showing always
// a big square cursor by always selecting 1 chracter from the current
// cursor position, unless it's already at the end, in which case the
// #cursor element should be displayed instead.
document.addEventListener('selectionchange', () => {
if (document.activeElement.id !== 'input') return;
const range = window.getSelection().getRangeAt(0);
const start = range.startOffset;
const end = range.endOffset;
const length = input.textContent.length;
if (end < length) {
input.classList.add('noCaret');
} else {
input.classList.remove('noCaret');
}
});
input.addEventListener('input', () => {
// If we paste HTML, format it as plain text and break it up
// input individual lines/commands:
if (input.childElementCount > 0) {
const lines = input.innerText.replace(/\n$/, '').split('\n');
const lastLine = lines[lines.length - 1];
for (let i = 0; i <= lines.length - 2; ++i) {
handleCommand(lines[i]);
}
input.textContent = lastLine;
focusAndMoveCursorToTheEnd();
}
// If we delete everything, display the square caret again:
if (input.innerText.length === 0) {
input.classList.remove('noCaret');
}
});
document.addEventListener('keydown', (e) => {
// If some key is pressed outside the input, focus it and move the cursor
// to the end:
if (e.target !== input) focusAndMoveCursorToTheEnd();
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleCommand(input.textContent);
input.textContent = '';
focusAndMoveCursorToTheEnd();
}
});
// Set the focus to the input so that you can start typing straigh away:
input.focus();
body {
background: #000;
color: #0F0;
font-family: monospace;
height: 100vh;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: scroll;
word-break: break-all;
margin: 0;
padding: 16px;
}
#input {
display: inline;
outline: none;
visibility: visible;
}
/*
If you press the Insert key, the vertical line caret will automatically
be replaced by a one-character selection.
*/
#input::selection {
color: #000;
background: #0F0;
}
#input:empty::before {
content: ' ';
}
@keyframes blink {
to {
visibility: hidden;
}
}
#input:focus + #caret {
animation: blink 1s steps(5, start) infinite;
}
#input.noCaret + #caret {
visibility: hidden;
}
#caret {
border: 0;
padding: 0;
outline: none;
background-color: #0F0;
display: inline-block;
font-family: monospace;
}
<div id="history"></div>
>
<div id="input" contenteditable="true"></div><button id="caret" for="input"> </button>
このソリューションは、キーボードイベント( / / )ではなく、主にイベントinput
とイベントに依存していることに注意してください。通常、テキスト入力やカーソルの処理にこれらを使用することはお勧めできません。入力の値は、テキストを貼り付けたりドロップしたりすることで更新することもでき、矢印、削除、エスケープ、次のようなショートカットなど、多くのエッジケースがあります。すべてを選択し、コピーして貼り付けます...したがって、処理する必要のあるすべてのキーの完全なリストを考え出すことは、おそらく最善のアプローチではありません。selectionchange
keydown
keypress
keyup
さらに、ほとんどのキーが同じ値を出力するモバイルでは機能しe.key = 'Unidentified'
ませe.which== 229
んe.keyCode = 229
。
代わりに、通常は、などの他のイベントに依存し、次のような非常に特定のキーを処理するためにinput
使用することをお勧めします。KeyboardEvents
↵</kbd> in this case.
e.key
、などのKeyboardEventのプロパティ値を確認する必要がある場合e.code
、e.which
またはhttps://keyjs.deve.keyCode
を使用できます。これらの種類のクロスブラウザの非互換性に関する情報をすぐに追加します!

免責事項:私は著者です。