如何在HTML输入框中使用方形光标?

29

如何在文本的 <input> 标签中使用这个方形光标(如下图所示)?

C:\WIKIPEDIA

6个回答

42

示例

示例

我已经更改了它的工作方式,并且它似乎解决了一些问题 :)

  • 接受任何普通输入所能接受的文本
  • 回退功能可用
  • 理论上可以支持复制粘贴文本

仍然存在一些常见限制,最明显的是无法直观地看到插入符号的位置。

我认为应该深思熟虑是否值得基于其缺点和可用性问题来实施此解决方案。

$(function() {
  var cursor;
  $('#cmd').click(function() {
    $('input').focus();
    cursor = window.setInterval(function() {
      if ($('#cursor').css('visibility') === 'visible') {
        $('#cursor').css({
          visibility: 'hidden'
        });
      } else {
        $('#cursor').css({
          visibility: 'visible'
        });
      }
    }, 500);

  });

  $('input').keyup(function() {
    $('#cmd span').text($(this).val());
  });

  $('input').blur(function() {
    clearInterval(cursor);
    $('#cursor').css({
      visibility: 'visible'
    });
  });
});
#cmd {
  font-family: courier;
  font-size: 14px;
  background: black;
  color: #21f838;
  padding: 5px;
  overflow: hidden;
}
#cmd span {
  float: left;
  padding-left: 3px;
  white-space: pre;
}
#cursor {
  float: left;
  width: 5px;
  height: 14px;
  background: #21f838;
}
input {
  width: 0;
  height: 0;
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="cmd">
  <span></span>
  <div id="cursor"></div>
</div>

<input type="text" name="command" value="" />


1
+1 个很酷的小例子...还有更多需要考虑的问题,比如支持多个元素、选项卡索引问题、可访问性等。 - Tim M.
2
另外还有三个基本问题:一是光标只能放在文本末尾,不能放在其他位置;二是keyup(或keydown)事件的keyCode属性不是字符代码,不应该被视为字符代码。只能使用keypress事件来处理文本输入;三是这不是一个表单输入,因此无法提交到服务器。您需要一个隐藏的输入框,它会自动与您的div内容保持同步。 - Tim Down
请参见第二个版本。它允许任何文本作为输入,并且可以提交到服务器。但是您无法在任意位置放置光标。请查看关于“警告”的部分:P @Tim Down - alex
4
使用现代浏览器,你可以使用selectionEnd属性来确定光标的位置。我在这里有一个支持任意光标定位的代码片段:http://jsfiddle.net/HugeHugh/gZMSQ/ - FirstVertex
这实际上更简单。 - red security
显示剩余4条评论

9
已接受的解决方案存在许多问题:
- 它非常缓慢,特别是在按住键时。 - 真正的插入符号可以用箭头移动,但您无法看到它的位置,因为假的方形插入符号始终位于末尾。 - 需要通过单击来手动再次聚焦输入。 - 无法正确选择文本,因为一旦释放鼠标按钮,选择就会丢失,并且使用键盘完全不可能选择文本。
更好的解决方案可能是使用CSS确保“插入符号”元素始终在contenteditable“输入”之后,并使用JS确保contenteditable元素始终处于聚焦状态。您可以尝试通过将autofocus添加到contenteditable元素并使用<label>作为插入符号元素来完成此最后一件事,但这在contenteditable元素上无效。请注意,不需要键盘事件侦听器:

const input = document.getElementById('input');
const caret = document.getElementById('caret');

// Move the focus back to the input if it moves away from it:
input.addEventListener('blur', (e) => {  
  input.focus();
});

// Set the focus to the input so that you can start typing straight away:
input.focus();
body {
  background: #000;
  color: #0F0;
  font-family: monospace;
  height: 100vh;
  box-sizing: border-box;
  overflow-x: hidden;
  overflow-y: scroll;
  margin: 0;
  padding: 16px;
}

#input {
  display: inline;
  word-break: break-all;
  outline: none;
  visibility: visible;
}

#caret {
  border: 0;
  padding: 0;
  outline: none;
  background-color: #0F0;
  display: inline-block;
  font-family: monospace;
}
C:\WIKIPEDIA > 

<div id="input" contenteditable="true"></div><button id="caret" for="input">&nbsp;</button>

在更现实的例子中,您可能希望:
- 避免将焦点限制在contenteditable元素中,因为这会防止选择先前的命令。相反,只有用户按下某个键后才将焦点聚焦到contenteditable元素。 - 根据光标位置显示不同的插入符号:如果在输入的末尾,则为方块,否则为线(除非使用Ins键启用了覆盖模式)。 - 如果按下了,则添加新的命令/条目。 - 防止输入格式化文本,并在需要时自动将其拆分为多个命令/条目。

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 = `C:\\WIKIPEDIA > ${ 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>

C:\WIKIPEDIA > 

<div id="input" contenteditable="true"></div><button id="caret" for="input">&nbsp;</button>

通常不建议监听键盘事件(keydown / keypress / keyup)来处理文本输入或光标,因为输入的值也可以通过粘贴或拖放文本来更新,并且存在许多边缘情况,例如箭头、删除、转义、全选、复制、粘贴等快捷方式... 因此,试图列出我们应该注意的所有键的详尽列表可能不是最佳方法。

此外,在移动设备上无法正常工作,因为大多数按键发出相同的值e.key = 'Unidentified'e.which== 229e.keyCode = 229

相反,通常更好地依赖于其他事件,如input,并使用KeyboardEvents来处理非常特定的键,例如在这种情况下的

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

Key.js \ JavaScript KeyboardEvent's key codes & key identifiers

免责声明:本人为作者。


1
当使用箭头时,插入符号变成了一个普通的细线,在Chrome上表现得有点松散。 - Roko C. Buljan

2
据我所知,对于html文本框来说这是不可能的,你可以为输入框本身设置样式,但除了应用已经可用的光标选项之外,你无法对光标进行任何操作 :(

1
没错。鼠标光标可以被操作。文本光标则不行。 - jwueller

2

你无法实现这个功能。也就是说,你可以通过使用等宽字体并使用闪烁的 gif 作为背景来自己完成,根据已输入文本的宽度动态计算 gif 的位置 - 但是在你的 gif 上方将有“正常”的光标,这会让解决方案看起来很丑陋。


2
对于 <input> 标签,你并没有太多可以做的。如果你不介意采用一种可怕的 hack 方法,你总是可以使用 JavaScript 根据需要调整文本框大小(设置 width = *something* * count),并在右侧放置一个带有光标的 <img>
我认为除了自己处理文本输入之外,可能没有更少的“ugh”解决方案,这可能有些过度。

0

你需要自己编写文本框并且通过不断将焦点放在其他地方来隐藏真正的光标。然后,在文档/主体级别捕获按键事件,并将该值插入到你自己的元素中。光标将是一个始终位于“文本框”最右侧的动画GIF。

你会遇到第二个问题,整个过程通常不可取。HTML 5开辟了一些新的可能性,但这仍然是为了一个光标而做的大量工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接