其他答案都是错的,有几个错误(截止至2019年5月7日)。其他解决方案建议使用“white-space: nowrap”(防止文字换行)+ “overflow: hidden”(防止长文本超出字段)+ 隐藏<br> 和其他内容。
这些解决方案中的第一个错误是,“overflow: hidden”还会阻止滚动文本。用户将无法通过以下方式滚动文本:
- 按下鼠标中键
- 选择文本并将鼠标指针向左或向右移动
- 使用水平鼠标滚轮(用户有此功能时)
他唯一可以使用箭头键来滚动。
您可以通过同时使用“overflow: hidden”和“overflow: auto”(或“scroll”)来解决此问题。您应该创建具有“overflow: hidden”的父元素以隐藏用户不应看到的内容。此元素必须具有输入边框和其他设计。您应该创建具有“overflow-x: auto”和“contenteditable”属性的子div。此元素将有滚动条,因此用户可以滚动它而没有任何限制,并且由于在父元素中隐藏了溢出内容,他将看不到此滚动条。
解决方案示例:
document.querySelectorAll('.CETextInput').forEach(el => {
el.parentNode.addEventListener('mousedown', function(e) {
if (e.target === this) {
setTimeout(() => this.children[0].focus(), 0);
}
});
el.parentNode.addEventListener('keydown', function(e) {
if (e.keyCode === 13)
e.preventDefault();
});
});
.CETextInputBorder {
display: inline-block;
border: 1px solid #aaa;
}
.CETextInputCont {
overflow: hidden;
cursor: text;
width: 10em;
height: 1em;
line-height: 1em;
padding: 5px;
font-size: 20px;
font-family: sans-serif;
}
.CETextInput {
white-space: pre;
overflow-x: auto;
min-height: 100%;
padding: 5px 0;
margin-top: -5px;
outline: none;
}
<div class="CETextInputBorder">
<div class="CETextInputCont">
<div class="CETextInput" contenteditable></div>
</div>
</div>
步骤二:解决<br>和其他问题:
此外,用户或扩展程序可能会粘贴以下内容,导致问题:
- <br> (可由用户粘贴)
- <img> (可能具有大尺寸)(可由用户粘贴)
- 带有另一种“white-space”值的元素
- <div> 和其他将文本带到另一行的元素
- 具有不适合的“display”值的元素
但是,建议隐藏所有 <br>也是错误的。因为Mozilla Firefox会向空字段添加<br>元素(我猜这可能是在删除最后一个字符后文本光标消失的错误的解决方法;在2019-03-19发布的Firefox 66中检查)。如果隐藏此元素,则当用户将焦点移动到字段时,光标将设置在此隐藏的<br>元素中,并且文本光标也将始终隐藏。
如果您知道字段为空,则可以通过JavaScript修复此问题。您需要在此处使用一些javascript(您不能使用:empty选择器,因为该字段包含<br>元素而不是空白)。以下是解决方案示例:
document.querySelectorAll('.CETextInput').forEach(el => {
el.parentNode.addEventListener('mousedown', function(e) {
if (e.target === this) {
setTimeout(() => this.children[0].focus(), 0);
}
});
el.parentNode.addEventListener('keydown', function(e) {
if (e.keyCode === 13)
e.preventDefault();
});
updateEmpty.call(el);
el.addEventListener('input', updateEmpty);
function updateEmpty(e) {
const s = this.innerText.replace(/[\r\n]+/g, '');
this.classList.toggle('empty', !s);
}
});
.CETextInputBorder {
display: inline-block;
border: 1px solid #aaa;
}
.CETextInputCont {
overflow: hidden;
cursor: text;
width: 10em;
height: 1em;
line-height: 1em;
padding: 5px;
font-size: 20px;
font-family: sans-serif;
}
.CETextInput {
white-space: pre;
overflow-x: auto;
min-height: 100%;
padding: 5px 0;
margin-top: -5px;
outline: none;
}
.CETextInput:not(.empty) br,
.CETextInput img {
display: none;
}
.CETextInput * {
display: inline;
white-space: pre;
}
<div class="CETextInputBorder">
<div class="CETextInputCont">
<div class="CETextInput" contenteditable></div>
</div>
</div>
步骤三:解决获取值问题:
我们隐藏了<br>元素,因此“innerText”值将不包含它们。但是:
- 当设置“empty”类时,结果可能包含<br>元素。
- 您的其他样式或扩展可能通过“!important”标记或具有更高优先级的规则覆盖“display: none”。
因此,在获取值时,应进行替换以避免意外获取换行符:
s = s.replace(/[\r\n]+/g, '');
不要使用 JavaScript 隐藏 <br>
你可以通过 JavaScript 删除 <br> 以解决问题,但这是一个很糟糕的解决方案,因为在每次删除后,用户都无法再使用“撤消”操作来取消之前所做的更改。
你也可以使用 document.execCommand('delete') 来删除 <br>,但这很难实现 + 用户可以撤消您的删除并恢复 <br> 元素。
添加占位符
虽然这个问题并没有被提出,但我想许多使用单行 contenteditable 元素的人都需要它。以下是使用 CSS 和上述的“empty”类的示例:
document.querySelectorAll('.CETextInput').forEach(el => {
el.parentNode.addEventListener('mousedown', function(e) {
if (e.target === this) {
setTimeout(() => this.children[0].focus(), 0);
}
});
el.parentNode.addEventListener('keydown', function(e) {
if (e.keyCode === 13)
e.preventDefault();
});
updateEmpty.call(el);
el.addEventListener('input', updateEmpty);
function updateEmpty(e) {
const s = this.innerText.replace(/[\r\n]+/g, '');
this.classList.toggle('empty', !s);
if (!s && !Array.prototype.filter.call(this.children, el => el.nodeName === 'BR').length)
this.appendChild(document.createElement('br'));
}
});
.CETextInputBorder {
display: inline-block;
border: 1px solid #aaa;
}
.CETextInputCont {
overflow: hidden;
cursor: text;
width: 10em;
height: 1em;
line-height: 1em;
padding: 5px;
font-size: 20px;
font-family: sans-serif;
}
.CETextInput {
white-space: pre;
overflow-x: auto;
min-height: 100%;
padding: 5px 0;
margin-top: -5px;
outline: none;
}
.CETextInput:not(.empty) br,
.CETextInput img {
display: none;
}
.CETextInput * {
display: inline;
white-space: pre;
}
.CETextInput[placeholder].empty::before {
content: attr(placeholder);
display: inline-block;
width: 0;
white-space: nowrap;
pointer-events: none;
cursor: text;
color: #b7b7b7;
padding-top: 8px;
margin-top: -8px;
}
<div class="CETextInputBorder">
<div class="CETextInputCont">
<div class="CETextInput" placeholder="Type something here" contenteditable><br></div>
</div>
</div>
只使用一个 div 和 "scrollbar-width" 的解决方案
您还可以通过设置 "overflow-x: auto"、"overflow-y: hidden" 和 "scrollbar-width: none" 来仅使用一个 div。但是,"scrollbar-width" 是一种新属性,目前仅在 Firefox 64+ 中起作用,其他浏览器尚未支持。
您还可以添加以下内容:
- webkit 前缀版本:" -webkit-scrollbar-width: none"
- 非标准化的 ".CETextInput::-webkit-scrollbar { display: none; }"(适用于基于 Webkit 的浏览器)
- "-ms-overflow-style: none"
我不建议使用这种解决方案,但这里有一个示例:
document.querySelectorAll('.CETextInput').forEach(el => {
el.addEventListener('keydown', function(e) {
if (e.keyCode === 13)
e.preventDefault();
});
updateEmpty.call(el);
el.addEventListener('input', updateEmpty);
function updateEmpty(e) {
const s = this.innerText.replace(/[\r\n]+/g, '');
this.classList.toggle('empty', !s);
}
});
.CETextInput {
white-space: pre;
overflow-x: auto;
overflow-y: hidden;
-webkit-scrollbar-width: none;
scrollbar-width: none;
-ms-overflow-style: none;
width: 10em;
height: 1em;
line-height: 1em;
padding: 5px;
border: 1px solid #aaa;
font-size: 20px;
font-family: sans-serif;
}
.CETextInput::-webkit-scrollbar {
display: none;
}
.CETextInput:not(.empty) br,
.CETextInput img {
display: none;
}
.CETextInput * {
display: inline;
white-space: pre;
}
<div class="CETextInput" contenteditable></div>
这个解决方案的3个问题与填充有关:
- 在Firefox中(在2019-05-11上测试了Firefox 66),当输入长文本时,没有右填充。这是因为Firefox不显示底部或右侧填充,当在具有滚动条并且内容滚动到末尾的同一元素中使用填充时。
- 在所有浏览器中,在滚动长文本到中间位置时没有填充。看起来更糟糕。 <input type="text">没有这个问题。
- 当用户按home或end键时,浏览器会滚动到填充不可见的位置。
要解决这些问题,您需要使用3个元素,就像我们以前使用的那样,但在这种情况下,您不需要使用scrollbar-width。我们使用3个元素的解决方案没有这些问题。
其他问题(在每个解决方案中):
- 粘贴文本并带有换行符时,模糊结束。我会想办法解决它。
- 在使用填充时,“this.children[0].focus()”在基于Webkit的浏览器中不够用(光标位置不在用户点击的位置)。我会想办法解决它。
- Firefox(在2019-05-11上测试了Firefox 66):当输入短文本时,用户无法通过在其右侧双击来选择最后一个单词。我会考虑一下。
- 当用户在页面中开始选择文本时,他可以结束在我们的字段中。通常的<input type="text">没有这种行为。但我认为这不是关键问题。