【问题标题】:What's the best way to simulate a DOS or Terminal screen in a web page?在网页中模拟 DOS 或终端屏幕的最佳方法是什么?
【发布时间】:2012-10-06 00:33:30
【问题描述】:

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

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

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

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

【问题讨论】:

  • 当前命令的内容可编辑 div,之前的命令(和结果)附加到上述不可编辑的 div 中?
  • 这是一个为尝试 MongoDB 编写的很好的示例:try.mongodb.org
  • 太棒了!这正是我要找的,劳伦斯!我忘记在搜索中使用“shell”。让它成为一个有解释的答案,我可以将它设置为问题的答案,它可以帮助其他处于同样困境的人。

标签: javascript php jquery html


【解决方案1】:

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

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

【讨论】:

【解决方案2】:

如果您想自己构建解决方案而不是使用库,您可以使用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>

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

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

相反,通常最好依靠input 等其他事件并使用KeyboardEvents 来处理非常具体的键,如本例中的

如果您需要检查键盘事件的属性值,例如e.keye.codee.whiche.keyCode,您可以使用https://keyjs.dev。我会尽快添加有关这些跨浏览器不兼容的信息!

免责声明:我是作者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-06
    • 1970-01-01
    • 2020-02-18
    • 2010-11-20
    • 1970-01-01
    相关资源
    最近更新 更多