令人满意的单行输入

Gij*_*ijs 56 html javascript css jquery contenteditable

对于我在我工作的公司开发的应用程序,我们需要一个支持在基于JS的Web应用程序中插入表情符号的输入.我们目前正在使用带有表情符号短代码(即':-)')的输入,并希望切换到插入实际的图形图像.

我们最初的计划是使用contenteditable <div>.我们正在使用监听器来执行粘贴事件以及不同的键/鼠标交互,以确保没有不需要的标记进入contenteditable(我们从其容器标签中删除文本并仅留下我们自己插入的图像标签).

但是,现在的问题是如果你输入足够的内容(即它的高度增加),div会调整大小.我们不希望这种情况发生,文本只是被隐藏(即普通overflow: hidden)也是不可接受的.所以:

有没有办法让contenteditable div表现得像单行输入?

我最喜欢,如果有一个相对简单的属性/ css属性,我错过了将做我想要的,但如果有必要CSS + JS建议也将不胜感激.

小智 106

[contenteditable="true"].single-line {
    white-space: nowrap;
    width:200px;
    overflow: hidden;
} 
[contenteditable="true"].single-line br {
    display:none;

}
[contenteditable="true"].single-line * {
    display:inline;
    white-space:nowrap;
}
Run Code Online (Sandbox Code Playgroud)
<div contenteditable="true" class="single-line">
    This should work.
</div>?
Run Code Online (Sandbox Code Playgroud)

  • 谢谢`display:none`为'br`,有趣!! (10认同)
  • 此外,如果您将固定高度设置为[contenteditable ="true"].单行类 - 那么即使用户在内部粘贴格式化文本,这也可以防止换行 (3认同)
  • 请注意,div仍将包含换行符,尤其是在javascript中检索innerHTML时 (3认同)
  • 阿莱西奥,你能解释一下你的逻辑吗? (2认同)

vit*_*dev 8

其他答案是错误的,几乎没有错误(2019-05-07)。其他解决方案建议使用“空白:nowrap”(防止携带到另一行)+“溢出:隐藏”(防止长文本超出字段)+隐藏 <br> 等。

该解决方案中的第一个错误是“溢出:隐藏”也会阻止滚动文本。用户将无法通过以下方式滚动文本:

  • 按鼠标中键
  • 选择文本并向左或向右移动鼠标指针
  • 使用水平鼠标滚动(当用户有这样的事情时)

他可以滚动的唯一方法是使用键盘箭头。

您可以通过同时使用“溢出:隐藏”和“溢出:自动”(或“滚动”)来解决此问题。您应该使用“溢出:隐藏”创建父 div 以隐藏用户不应看到的内容。这个元素必须有输入边框和其他设计。并且您应该使用“overflow-x: auto”和“contenteditable”属性创建子 div。此元素将具有滚动条,因此用户可以不受任何限制地滚动它,并且由于隐藏父元素中的溢出,他将看不到此滚动条。

解决方案示例:

document.querySelectorAll('.CETextInput').forEach(el => {
	//Focusing on child element after clicking parent. We need it because parent element has bigger width than child.
	el.parentNode.addEventListener('mousedown', function(e) {
		if (e.target === this) {
			setTimeout(() => this.children[0].focus(), 0);
		}
	});
	
	//Prevent Enter. See purpose in "Step 2" in answer.
	el.parentNode.addEventListener('keydown', function(e) {
		if (e.keyCode === 13)
			e.preventDefault();
	});
});
Run Code Online (Sandbox Code Playgroud)
.CETextInputBorder { /*This element is needed to prevent cursor: text on border*/
	display: inline-block;
	border: 1px solid #aaa;
}

.CETextInputCont {
	overflow: hidden;
	cursor: text; /*You must set it because parent elements is bigger then child contenteditable element. Also you must add javascript to focus child element on click parent*/
	
	/*Style:*/
	width: 10em;
	height: 1em;
	line-height: 1em;
	padding: 5px;
	font-size: 20px;
	font-family: sans-serif;
}

.CETextInput {
	white-space: pre; /*"pre" is like "nowrap" but displays all spaces correctly (with "nowrap" last space is not displayed in Firefox, tested on Firefox 66, 2019-05-15)*/
	overflow-x: auto;
	min-height: 100%; /*to prevent zero-height with no text*/
	
	/*We will duplicate vertical padding to let user click contenteditable element on top and bottom. We would do same thing for horizontal padding but it is not working properly (in all browsers when scroll is in middle position and in Firefox when scroll is at the end). You can also replace vertical padding with just bigger line height.*/
	padding: 5px 0;
	margin-top: -5px;
	
	outline: none; /*Prevent border on focus in some browsers*/
}
Run Code Online (Sandbox Code Playgroud)
<div class="CETextInputBorder">
	<div class="CETextInputCont">
		<div class="CETextInput" contenteditable></div>
	</div>
</div>
Run Code Online (Sandbox Code Playgroud)


第 2 步:解决 <br> 和其他问题:

还有一个问题是用户或扩展程序可以粘贴

  • <br>(可由用户粘贴)
  • <img>(可能有大尺寸)(可由用户粘贴)
  • 具有另一个“空白”值的元素
  • <div> 和其他将文本传送到另一行的元素
  • 具有不合适“显示”值的元素

但是建议隐藏所有<br> 也是错误的。那是因为 Mozilla Firefox 将 <br> 元素添加到空字段(我想这可能是删除最后一个字符后文本光标消失的解决方法;在 2019-03-19 发布的 Firefox 66 中检查)。如果您隐藏此元素,则当用户将焦点移动到字段时,将在此隐藏的 <br> 元素中设置插入符号,并且文本光标也将(始终)隐藏。

如果您在知道字段为空时将成为 <br>,则可以解决此问题。你在这里需要一些 javascript(你不能使用 :empty 选择器,因为字段包含 <br> 元素而不是空的)。解决方案示例:

document.querySelectorAll('.CETextInput').forEach(el => {
	//OLD CODE:
	
	//Focusing on child element after clicking parent. We need it because parent element has bigger width than child.
	el.parentNode.addEventListener('mousedown', function(e) {
		if (e.target === this) {
 	 	 	setTimeout(() => this.children[0].focus(), 0);
 	 	}
	});
	
	//Prevent Enter to prevent blur on Enter
	el.parentNode.addEventListener('keydown', function(e) {
		if (e.keyCode === 13)
			e.preventDefault();
	});
	
	//NEW CODE:
	
	//Update "empty" class on all "CETextInput" elements:
	updateEmpty.call(el); //init
	el.addEventListener('input', updateEmpty);

	function updateEmpty(e) {
		const s = this.innerText.replace(/[\r\n]+/g, ''); //You must use this replace, see explanation below in "Step 3"
		this.classList.toggle('empty', !s);
	}
});
Run Code Online (Sandbox Code Playgroud)
/*OLD CODE:*/

.CETextInputBorder { /*This element is needed to prevent cursor: text on border*/
	display: inline-block;
	border: 1px solid #aaa;
}

.CETextInputCont {
	overflow: hidden;
	cursor: text; /*You must set it because parent elements is bigger then child contenteditable element. Also you must add javascript to focus child element on click parent*/
	
	/*Style:*/
	width: 10em;
	height: 1em;
	line-height: 1em;
	padding: 5px;
	font-size: 20px;
	font-family: sans-serif;
}

.CETextInput {
	white-space: pre; /*"pre" is like "nowrap" but displays all spaces correctly (with "nowrap" last space is not displayed in Firefox, tested on Firefox 66, 2019-05-15)*/
	overflow-x: auto;
	min-height: 100%; /*to prevent zero-height with no text*/
	
	/*We will duplicate vertical padding to let user click contenteditable element on top and bottom. We would do same thing for horizontal padding but it is not working properly (in all browsers when scroll is in middle position and in Firefox when scroll is at the end). You can also replace vertical padding with just bigger line height.*/
	padding: 5px 0;
	margin-top: -5px;
	
	outline: none; /*Prevent border on focus in some browsers*/
}

/*NEW CODE:*/

.CETextInput:not(.empty) br,
.CETextInput img { /*We hide <img> here. If you need images do not hide them but set maximum height. User can paste image by pressing Ctrl+V or Ctrl+Insert.*/
	display: none;
}

.CETextInput * {
	display: inline;
	white-space: pre;
}
Run Code Online (Sandbox Code Playgroud)
<!--OLD CODE:-->

<div class="CETextInputBorder">
	<div class="CETextInputCont">
		<div class="CETextInput" contenteditable></div>
	</div>
</div>
Run Code Online (Sandbox Code Playgroud)


第 3 步:解决获取价值的问题:

我们隐藏了 <br> 元素,因此“innerText”值将不包含它们。但:

  1. 设置“空”类时,结果可能包含 <br> 元素。
  2. 您的其他样式或扩展可能会通过“!重要”标记或具有更高优先级的规则覆盖“显示:无”。

因此,当您获得价值时,您应该进行替换以避免意外换行:

s = s.replace(/[\r\n]+/g, '');
Run Code Online (Sandbox Code Playgroud)


不要使用 javascript 来隐藏 <br>

您也可以通过 javascript 删除它们来解决 <br> 的问题,但这是非常糟糕的解决方案,因为在每个删除用户之后,无法再使用“撤消”操作来取消在删除之前所做的更改。

您也可以使用 document.execCommand('delete') 删除 <br> ,但很难实现 + 用户可以撤消您的删除并恢复 <br> 元素。


添加占位符

它没有被问到,但我想很多使用单行 contenteditable 元素的人会需要它。以下是如何使用 css 和我们上面讨论的“空”类制作占位符的示例:

//OLD CODE:

document.querySelectorAll('.CETextInput').forEach(el => {
	//Focusing on child element after clicking parent. We need it because parent element has bigger width than child.
	el.parentNode.addEventListener('mousedown', function(e) {
		if (e.target === this) {
 	 	 	setTimeout(() => this.children[0].focus(), 0);
 	 	}
	});
	
	//Prevent Enter to prevent blur on Enter
	el.parentNode.addEventListener('keydown', function(e) {
		if (e.keyCode === 13)
			e.preventDefault();
	});
	
	//Update "empty" class on all "CETextInput" elements:
	updateEmpty.call(el); //init
	el.addEventListener('input', updateEmpty);

	function updateEmpty(e) {
		const s = this.innerText.replace(/[\r\n]+/g, ''); //You must use this replace, see explanation below in "Step 3"
		this.classList.toggle('empty', !s);
		
		//NEW CODE:
		
		//Make element always have <br>. See description in html. I guess it is not needed because only Firefox has bug with bad cursor position but Firefox always adds this element by itself except on init. But on init we are adding it by ourselves (see html).
		if (!s && !Array.prototype.filter.call(this.children, el => el.nodeName === 'BR').length)
			this.appendChild(document.createElement('br'));
	}
});
Run Code Online (Sandbox Code Playgroud)
/*OLD CODE:*/

.CETextInputBorder { /*This element is needed to prevent cursor: text on border*/
	display: inline-block;
	border: 1px solid #aaa;
}

.CETextInputCont {
	overflow: hidden;
	cursor: text; /*You must set it because parent elements is bigger then child contenteditable element. Also you must add javascript to focus child element on click parent*/
	
	/*Style:*/
	width: 10em;
	height: 1em;
	line-height: 1em;
	padding: 5px;
	font-size: 20px;
	font-family: sans-serif;
}

.CETextInput {
	white-space: pre; /*"pre" is like "nowrap" but displays all spaces correctly (with "nowrap" last space is not displayed in Firefox, tested on Firefox 66, 2019-05-15)*/
	overflow-x: auto;
	min-height: 100%; /*to prevent zero-height with no text*/
	
	/*We will duplicate vertical padding to let user click contenteditable element on top and bottom. We would do same thing for horizontal padding but it is not working properly (in all browsers when scroll is in middle position and in Firefox when scroll is at the end). You can also replace vertical padding with just bigger line height.*/
	padding: 5px 0;
	margin-top: -5px;
	
	outline: none; /*Prevent border on focus in some browsers*/
}

.CETextInput:not(.empty) br,
.CETextInput img { /*We hide <img> here. If you need images do not hide them but set maximum height. User can paste image by pressing Ctrl+V or Ctrl+Insert.*/
	display: none;
}

.CETextInput * {
	display: inline;
	white-space: pre;
}

/*NEW CODE:*/

.CETextInput[placeholder].empty::before { /*Use ::before not ::after or you will have problems width first <br>*/
	content: attr(placeholder);
	display: inline-block;
	width: 0;
	white-space: nowrap;
	pointer-events: none;
	cursor: text;
	color: #b7b7b7;
	
	padding-top: 8px;
	margin-top: -8px;
}
Run Code Online (Sandbox Code Playgroud)
<!--OLD CODE:-->

<div class="CETextInputBorder">
	<div class="CETextInputCont">
		<div class="CETextInput" placeholder="Type something here" contenteditable><br></div>
	</div>
</div>

<!--We manually added <br> element for Firefox browser because Firefox (tested on 2019-05-11, Firefox 66) has bug with bad text cursor position in empty contenteditable elements that have ::before or ::after pseudo-elements.-->
Run Code Online (Sandbox Code Playgroud)


只有一个 div 和“滚动条宽度”的解决方案

你也可以通过设置“overflow-x: auto”、“overflow-y: hidden”和“scrollbar-width: none”来只使用一个div。但“滚动条宽度”是新属性,仅适用于 Firefox 64+,尚不适用于其他浏览器。

您还可以添加:

  • webkit 前缀版本:“-webkit-scrollbar-width: none”
  • 非标准化的“.CETextInput::-webkit-scrollbar { display: none; }”(适用于基于 webkit 的浏览器)
  • “-ms-overflow-style: 无”

我不建议使用此解决方案,但以下是示例:

//OLD CODE:

document.querySelectorAll('.CETextInput').forEach(el => {
	//Focusing on child is not needed anymore
	
	//Prevent Enter to prevent blur on Enter
	el.addEventListener('keydown', function(e) {
		if (e.keyCode === 13)
			e.preventDefault();
	});
	
	//Update "empty" class on all "CETextInput" elements:
	updateEmpty.call(el); //init
	el.addEventListener('input', updateEmpty);

	function updateEmpty(e) {
		const s = this.innerText.replace(/[\r\n]+/g, ''); //You must use this replace, see explanation below in "Step 3"
		this.classList.toggle('empty', !s);
	}
});
Run Code Online (Sandbox Code Playgroud)
/*NEW CODE:*/

.CETextInput {
	white-space: pre; /*"pre" is like "nowrap" but displays all spaces correctly (with "nowrap" last space is not displayed in Firefox, tested on Firefox 66, 2019-05-15)*/
	overflow-x: auto; /*or "scroll"*/
	overflow-y: hidden;
	-webkit-scrollbar-width: none; /*Chrome 4+ (probably), webkit based*/
	scrollbar-width: none; /*FF 64+, Chrome ??+, webkit based, Edge ??+*/
	-ms-overflow-style: none; /*IE ??*/
	
	/*Style:*/
	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; /*Chrome ??, webkit based*/
}

/*OLD CODE:*/

.CETextInput:not(.empty) br,
.CETextInput img { /*We hide <img> here. If you need images do not hide them but set maximum height. User can paste image by pressing Ctrl+V or Ctrl+Insert.*/
	display: none;
}

.CETextInput * {
	display: inline;
	white-space: pre;
}
Run Code Online (Sandbox Code Playgroud)
<!--NEW CODE:-->

<div class="CETextInput" contenteditable></div>
Run Code Online (Sandbox Code Playgroud)

这个解决方案有3 个关于 padding 的问题

  1. 在 Firefox 中(在 2019-05-11 上测试,Firefox 66)在输入长文本时没有正确的填充。这是因为在具有滚动条的同一元素中使用填充以及内容滚动到末尾时,Firefox 不会显示底部或右侧的填充。
  2. 在所有浏览器中,在中间位置滚动长文本时没有填充。看起来更糟。<input type="text"> 没有这个问题。
  3. 当用户按主页或结束浏览器滚动以放置填充不可见时。

要解决这些问题,您需要使用我们之前使用的 3 个元素,但在这种情况下,您不需要使用滚动条宽度。我们的 3 个元素的解决方案没有这些问题。


其他问题(在每个解决方案中):

  • 粘贴文本时的模糊以换行符结束。我会考虑如何解决它。
  • 当使用填充时 this.children[0].focus() 在基于 webkit 的浏览器中是不够的(光标位置不是用户点击的位置)。我会考虑如何解决它。
  • Firefox(在 2019-05-11 上测试,Firefox 66):当输入短文本时,用户无法通过双击右侧的最后一个单词来选择。我会考虑的。
  • 当用户在页面中开始文本选择时,他可以在我们的字段中结束它。通常的 <input type="text"> 没有这种行为。但我认为这并不重要。


tw1*_*w16 7

我认为你正在寻找一个contenteditable div只有一行文本,当它溢出时,它会水平滚动div.这应该是诀窍:http://jsfiddle.net/F6C9T/1

div {
    font-family: Arial;
    font-size: 18px;
    min-height: 40px;
    width: 300px;
    border: 1px solid red;
    overflow: hidden;
    white-space: nowrap;
}
Run Code Online (Sandbox Code Playgroud)
<div contenteditable>
    Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
</div>
Run Code Online (Sandbox Code Playgroud)

min-height: 40px结合了用于显示的水平滚动条时的高度.min-height:20px当水平滚动条出现时,A 会自动展开,但这在IE7中不起作用(尽管如果需要,可以使用条件注释来应用单独的样式).

  • 该解决方案不起作用,因为用户无法通过选择文本或鼠标中键(但可以使用键盘箭头)滚动行。 (2认同)

pti*_*son 5

我改编了 @tw16 接受的解决方案(2019 年 12 月 5 日)以添加滚动功能。技巧是使用添加滚动overflow-x: auto,然后隐藏滚动条(/sf/answers/3449486981/

\n\n

\r\n
\r\n
/* Existing Solution */\r\n[contenteditable="true"].single-line {\r\n    white-space: nowrap;\r\n    width: 200px;\r\n    overflow: hidden;\r\n} \r\n[contenteditable="true"].single-line br {\r\n    display:none;\r\n\r\n}\r\n[contenteditable="true"].single-line * {\r\n    display:inline;\r\n    white-space:nowrap;\r\n}\r\n\r\n/* Make Scrollable */\r\n[contenteditable="true"].single-line {\r\n    overflow-x: auto;\r\n    overflow-y: hidden;\r\n    scrollbar-width: none; /* Firefox */\r\n    -ms-overflow-style: none;  /* Internet Explorer 10+ */\r\n}\r\n[contenteditable="true"].single-line::-webkit-scrollbar { /* WebKit */\r\n    width: 0;\r\n    height: 0;\r\n}    
Run Code Online (Sandbox Code Playgroud)\r\n
<div contenteditable="true" class="single-line">\r\n    This should scroll when you have really long text!\r\n</div>\xe2\x80\x8b
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n