12

我发现这甚至很难概念化。最简单的方法是让一个大textarea元素占据屏幕的大部分,并input在其下方放置一个小文本元素。玩家将输入命令,输出将显示在textarea.

问题是,我希望输入完全集成。想想一个 DOS 屏幕。有括号提示符 ,>然后键入命令。按 Enter 键,输出显示在其下方,然后是>下一个命令的另一个提示。从视觉上看,输入与输出并没有分开。我想完成的一个例子可以在这里看到:http ://www.youtube.com/watch?v=UC_FrikiZdE (除了使用鼠标来选择命令,它们可以输入)。

我将如何在 HTML 中执行此操作(使用 JavaScript/jQuery 处理输入/输出)?我在想也许一切都是在可编辑的中完成的textarea,但是 Backspace 按钮无法删除>提示及其他内容中的任何内容,只能删除已输入的文本。

最简单的方法是什么?我无法在网上找到任何演示或教程。任何人都可以指出我可能错过的任何内容吗?谢谢。

4

2 回答 2

14

您可以查看通过 Google 在线找到的这些 JavaScript 终端:

另外,我的一些法国朋友正在研究这个:

于 2012-10-16T00:18:29.733 回答
4

如果您想自己构建解决方案而不是使用库,则可以contenteditable在其后使用一个元素和一个假方形插入符号。如果插入符号移动到另一个位置,则隐藏该假插入符号并显示真正的垂直线。

但是,您可以调整此代码以始终选择一个字符,即使在禁用改写模式时也是如此,因此插入符号始终是一个字符宽的正方形。

我只显示命令,但以不同方式处理它们将是微不足道的:

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">&nbsp;</button>

请注意,此解决方案主要依赖于inputandselectionchange事件,而不是键盘事件 ( keydown/ keypress/ keyup)。使用它们来处理文本输入或光标通常是一个坏主意,因为输入的值也可以通过将文本粘贴或拖放到其中来更新,并且有许多边缘情况,例如箭头、删除、转义、快捷方式如全选、复制、粘贴……所以尝试列出我们应该注意的所有键的详尽列表可能不是最好的方法。

此外,这不适用于移动设备,因为大多数键发出相同的值e.key = 'Unidentified',e.which== 229e.keyCode = 229.

相反,通常最好依靠其他事件,例如input并使用KeyboardEvents来处理非常特定的键,例如↵</kbd> in this case.

如果您需要检查 KeyboardEvent 的属性值,例如e.key, e.codee.which或者e.keyCode您可以使用https://keyjs.dev。我将很快添加有关这些跨浏览器不兼容性的信息!

Key.js \ JavaScript KeyboardEvent 的键码和键标识符

免责声明:我是作者。

于 2020-10-05T23:05:45.667 回答