Javascript 可编辑的单行输入

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6831482/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 23:21:32  来源:igfitidea点击:

contenteditable single-line input

javascriptjqueryhtmlcsscontenteditable

提问by Gijs

For an application we're developing at the company where I work, we need an input that supports inserting emoticons inside our JS-based web app. We're currently using an input with the emoticon shortcodes (ie ':-)') and would like to switch to inserting actual, graphical images.

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

Our original plan was to use a contenteditable<div>. We're using listeners for the paste event as well as the different key/mouse interactions to ensure no unwanted markup enters the contenteditable (we strip text out of its container tags and leave only image tags that we inserted ourselves).

我们最初的计划是使用contenteditable<div>. 我们为粘贴事件以及不同的键/鼠标交互使用侦听器,以确保没有不需要的标记进入 contenteditable(我们从容器标签中去除文本,只留下我们自己插入的图像标签)。

However, the problem right now is that the div resizes if you put in enough content (ie its height increases). We don't want this to happen, nor is it acceptable for the text to just be hidden (ie plain overflow: hidden). So:

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

Is there a way to make the contenteditable div behave like a single-line input?

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

I'd like it best if there is a relatively simple attribute/css property that I've missed that will do what I want, but if necessary CSS+JS suggestions will also be appreciated.

如果有一个我遗漏的相对简单的属性/css 属性可以做我想做的事情,我会最好,但如果有必要,CSS+JS 建议也将受到赞赏。

回答by Alessio

[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;
}
<div contenteditable="true" class="single-line">
    This should work.
</div>?

回答by tw16

I think you are looking for a contenteditabledivwith only one line of text that scrolls horizontally when it overflows the div. This should do the trick: http://jsfiddle.net/F6C9T/1

我认为您正在寻找contenteditablediv只有一行文本的文本,当它溢出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;
}
<div contenteditable>
    Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
</div>

The min-height: 40pxincorporates the height for when the horizontal scroll bar appears. A min-height:20pxwould automatically expand when the horizontal scroll bar appears, but this doesn't work in IE7 (though you could use conditional comments to apply separate styling if you wanted it).

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

回答by vitaliydev

Other answers are wrong and contain few mistakes (on 2019-05-07). Other solutions suggest to use "white-space: nowrap" (prevents carrying to another line) + "overflow: hidden" (prevents long text going beyond the field) + hiding <br> and other.

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

First mistake in that solutions is "overflow: hidden" also prevents scrolling the text. User will not be able to scroll the text by:

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

  • Pressing mouse middle button
  • Selecting the text and moving mouse pointer to the left or right
  • Using horizontal mouse scroll (when user have such a thing)
  • 按鼠标中键
  • 选择文本并向左或向右移动鼠标指针
  • 使用水平鼠标滚动(当用户有这样的事情时)

The only way he can scroll is using keyboard arrows.

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

You can solve this problem by using "overflow: hidden" and "overflow: auto" (or "scroll") at the same time. You should create parent div with "overflow: hidden" to hide content user should not see. This element must have input borders and other design. And you should create child div with "overflow-x: auto" and "contenteditable" attribute. This element will have scrollbar so user can scroll it without any limitations and he will not see this scrollbar because of hiding overflow in parent element.

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

Example of solution:

解决方案示例:

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();
 });
});
.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*/
}
<div class="CETextInputBorder">
 <div class="CETextInputCont">
  <div class="CETextInput" contenteditable></div>
 </div>
</div>



Step 2: Solving problem with <br> and other:

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

Also there is a problem that user or extensions can paste

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

  • <br> (can be pasted by user)
  • <img> (may have big size) (can be pasted by user)
  • elements with another "white-space" value
  • <div> and other elements that carry text to another line
  • elements with unsuitable "display" value
  • <br>(可由用户粘贴)
  • <img>(可能有大尺寸)(可由用户粘贴)
  • 具有另一个“空白”值的元素
  • <div> 和其他将文本带到另一行的元素
  • 具有不合适“显示”值的元素

But advise to hide all<br> is wrong too. That is because Mozilla Firefox adds <br> element to empty field (I guess it may be workaround of bug with text cursor disappearing after deleting last character; checked in Firefox 66 released on 2019-03-19). If you hide this element then when user moves focus to field caret will be set in this hidden <br> element and text cursor will be hidden too (always).

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

You can fix this if you will be <br> when you know field is empty. You need some javascript here (you cannot use :empty selector because field contains <br> elements and not empty). Example of solution:

如果您在知道字段为空时将成为 <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);
 }
});
/*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;
}
<!--OLD CODE:-->

<div class="CETextInputBorder">
 <div class="CETextInputCont">
  <div class="CETextInput" contenteditable></div>
 </div>
</div>



Step 3: Solving problem with getting value:

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

We hided <br> elements so "innerText" value will not contain them. But:

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

  1. When "empty" class is set result may contain <br> elements.
  2. Your other styles or extensions may override "display: none" by "!important" mark or by rule with higher priority.
  1. 设置“空”类时,结果可能包含 <br> 元素。
  2. 您的其他样式或扩展可能会通过“!重要”标记或具有更高优先级的规则覆盖“显示:无”。

So when you get value you should make replace to avoid accidental getting line breaks:

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

s = s.replace(/[\r\n]+/g, '');



Do not use javascript for hiding <br>

不要使用 javascript 来隐藏 <br>

Also you could solve the problem with <br> by removing them by javascript but this is very bad solution because after every removing user cannot use "undo" action anymore for canceling changes was made before removing.

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

Also you could use document.execCommand('delete') to delete <br> but it is hard to implement + user can undo your deletion and restore <br> elements.

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



Adding placeholder

添加占位符

It was not asked in question but I guess many people using single-line contenteditable elements will need it. Here is example how to make placeholder using css and "empty" class we talked above:

它没有被问到,但我想很多使用单行 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'));
 }
});
/*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;
}
<!--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.-->



Solution with only one div and "scrollbar-width"

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

You can also use only one div by setting "overflow-x: auto", "overflow-y: hidden" and "scrollbar-width: none". But "scrollbar-width" is new property and works only in Firefox 64+ and no other browsers yet.

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

You can also add:

您还可以添加:

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

I would not recommend to use this solution, but here is example:

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

//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);
 }
});
/*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;
}
<!--NEW CODE:-->

<div class="CETextInput" contenteditable></div>

This solution has 3 problemswith paddings:

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

  1. In Firefox (tested on 2019-05-11, Firefox 66) there is no right padding when long text is typed. That is because Firefox does not display bottom or right padding when using padding in the same element that have scrollbar and when content is scrolled to the end.
  2. In all browsers there is no padding when scrolling long text in middle position. It looks worse. <input type="text"> does not have this problem.
  3. When user press home or end browsers scroll to place paddings are not visible.
  1. 在 Firefox 中(在 2019-05-11 上测试,Firefox 66)在输入长文本时没有正确的填充。这是因为在具有滚动条的同一元素中使用填充以及内容滚动到末尾时,Firefox 不会显示底部或右侧填充。
  2. 在所有浏览器中,在中间位置滚动长文本时没有填充。看起来更糟。<input type="text"> 没有这个问题。
  3. 当用户按主页或结束浏览器滚动以放置填充不可见时。

To solve these problems you need use 3 elements like we used before but in this case you don't need use scrollbar-width. Our solution with 3 elements does not have these problems.

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



Other problems (in every solution):

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

  • Blur on pasting text ends with line break. I will think how to fix it.
  • When using paddings this.children[0].focus() is not enough in webkit-based browsers (cursor position is not where user clicked). I will think how to fix it.
  • Firefox (tested on 2019-05-11, Firefox 66): When short text is typed user cannot select last word by double clicking on the right of it. I will think about it.
  • When user starts text selection in the page he can end it in our field. Usual <input type="text"> does not have this behavior. But I don't think it is critical.
  • 粘贴文本时的模糊以换行符结束。我会考虑如何解决它。
  • 当使用填充时 this.children[0].focus() 在基于 webkit 的浏览器中是不够的(光标位置不是用户点击的位置)。我会考虑如何解决它。
  • Firefox(在 2019-05-11 上测试,Firefox 66):当输入短文本时,用户无法通过双击右侧的最后一个单词来选择。我会考虑的。
  • 当用户在页面中开始文本选择时,他可以在我们的字段中结束它。通常的 <input type="text"> 没有这种行为。但我认为这并不重要。

回答by josh

Here's a relatively simple solution that uses the contenteditable's input event to scan the dom and filter out various flavors of new lines (so it should be robust against copy/paste, drag 'n drop, hitting enter on the keyboard, etc). It condenses multiple TextNodes into single TextNodes, strips newlines from TextNodes, kills BRs, and puts a "display: inline" on any other element that it touches. Tested on Chrome, no guarantees anywhere else.

这是一个相对简单的解决方案,它使用 contenteditable 的输入事件来扫描 dom 并过滤掉各种风格的新行(因此它应该对复制/粘贴、拖放、敲击键盘上的 Enter 等具有鲁棒性)。它将多个 TextNode 压缩为单个 TextNode,从 TextNode 中去除换行符,杀死 BR,并在它接触的任何其他元素上放置一个“显示:内联”。在 Chrome 上测试,在其他任何地方都不能保证。

var editable = $('#editable')

editable.on('input', function() {
  return filter_newlines(editable);
});


function filter_newlines(div) {
    var node, prev, _i, _len, _ref, _results;
    prev = null;
    _ref = div.contents();
    _results = [];
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        node = _ref[_i];
        if (node.nodeType === 3) {
            node.nodeValue = node.nodeValue.replace('\n', '');
            if (prev) {
                node.nodeValue = prev.nodeValue + node.nodeValue;
                $(prev).remove();
            }
            _results.push(prev = node);
        } else if (node.tagName.toLowerCase() === 'br') {
            _results.push($(node).remove());
        } else {
            $(node).css('display', 'inline');
            filter_newlines($(node));
            _results.push(prev = null);
        }
    }
    return _results;
}
#editable {
    width: 200px;
    height: 20px;
    border: 1px solid black;
}
<div id="editable" contenteditable="true"></div>

Or here's the fiddle: http://jsfiddle.net/tG9Qa/

或者这里是小提琴:http: //jsfiddle.net/tG9Qa/

回答by Berge Aadland

If you want a different way of solving it other than changing the requirements, with a little display:tableit is fully possible =)

如果你想用不同的方式来解决它而不是改变需求,那么一点点display:table是完全可能的 =)

.container1 {
    height:20px;
    width:273px; 
    overflow:hidden;
    border:1px solid green;
}
.container2 {
    display:table;
}
.textarea {
    width:273px;
    font-size: 18px;
    font-weight: normal;
    line-height: 18px;
    outline: none;
    display: table-cell;
    position: relative;
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    user-select: text;
    word-wrap: break-word;    
    overflow:hidden;
}
<div class="container1">
    <div class="container2">
        <div contenteditable="true" class="textarea"></div>
    </div>
</div>

回答by ptimson

I adapted the @tw16 accepted solution (on 5th Dec 2019) to add in scrolling. The trick was to add scrolling using overflow-x: autobut then hide the scrollbar (https://stackoverflow.com/a/49278385)

我改编了@tw16 接受的解决方案(2019 年 12 月 5 日)以添加滚动。诀窍是使用添加滚动overflow-x: auto但然后隐藏滚动条(https://stackoverflow.com/a/49278385

/* Existing Solution */
[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;
}

/* Make Scrollable */
[contenteditable="true"].single-line {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none;  /* Internet Explorer 10+ */
}
[contenteditable="true"].single-line::-webkit-scrollbar { /* WebKit */
    width: 0;
    height: 0;
}    
<div contenteditable="true" class="single-line">
    This should scroll when you have really long text!
</div>?

回答by trgraglia

Check out this answer I just posted. This should help you out:

看看我刚刚发布的这个答案。这应该可以帮助您:

How to create a HTML5 single line contentEditable tab which listens to Enter and Esc

如何创建一个 HTML5 单行 contentEditable 选项卡来监听 Enter 和 Esc

Here is the HTML markup:

这是 HTML 标记:

<span contenteditable="false"></span>

Here is the jQuery/javascript:

这是 jQuery/javascript:

$(document).ready(function() {
    $('[contenteditable]').dblclick(function() {
        $(this).attr('contenteditable', 'true');
        clearSelection();
        $(this).trigger('focus');
    });

    $('[contenteditable]').live('focus', function() {
        before = $(this).text();
        if($(this).attr('contenteditable') == "true") { $(this).css('border', '1px solid #ffd646'); }
    //}).live('paste', function() {
    }).live('blur', function() {
        $(this).attr('contenteditable', 'false');
        $(this).css('border', '1px solid #fafafa');
        $(this).text($(this).text().replace(/(\r\n|\n|\r)/gm,""));

        if (before != $(this).text()) { $(this).trigger('change'); }
    }).live('keyup', function(event) {
        // ESC=27, Enter=13
        if (event.which == 27) {
            $(this).text(before);
            $(this).trigger('blur');
        } else if (event.which == 13) {
            $(this).trigger('blur');
        }
    });

    $('[contenteditable]').live('change', function() {
        var $thisText = $(this).text();
        //Do something with the new value.
    });
});

function clearSelection() {
    if ( document.selection ) {
        document.selection.empty();
    } else if ( window.getSelection ) {
        window.getSelection().removeAllRanges();
    }
}

Hope this helps someone!!!

希望这对某人有所帮助!!!

回答by Gijs

So, for posterity: the simplest solution is to get your product manager to change the requirements so you can do multiline editing. This is what ended up happening in our case.

因此,对于后代:最简单的解决方案是让您的产品经理更改需求,以便您可以进行多行编辑。这就是我们案例中最终发生的事情。

However, before that happened, I ended up going quite a way in creating a manually moving single-line richtext editor. I wrapped it up in a jQuery plugin in the end. I don't have time to finish it up (there are probably bugs in IE, Firefox works best and Chrome works pretty well - comments are sparse and sometimes not very clear). It uses parts of the Rangy library(extracted because I didn't want to rely on the complete library) to get screen positions of selections in order to test for mouse position vs. selection (so you can drag selections and move the box).

然而,在那之前,我最终在创建一个手动移动的单行富文本编辑器方面做了很大的努力。最后我将它封装在一个 jQuery 插件中。我没有时间完成它(IE 中可能存在错误,Firefox 工作得最好,Chrome 工作得很好——评论很少,有时不是很清楚)。它使用部分Rangy 库(因为我不想依赖完整库而提取)来获取选择的屏幕位置,以测试鼠标位置与选择(因此您可以拖动选择并移动框)。

Roughly, it works by using 3 elements. An outer div (the thing you call the plugin on), which gets overflow: hidden, and then 2 levels of nesting inside it. The first level is absolutely positioned, the second level is the actual contenteditable. This separation is necessary because otherwise some browsers will give the contenteditable absolutely positioned element grippies, to let the user move it around...

粗略地说,它通过使用 3 个元素来工作。一个外部 div(你调用插件的东西),它会溢出:隐藏,然后在里面有 2 层嵌套。第一级是绝对定位,第二级是实际内容编辑。这种分离是必要的,否则一些浏览器会给 contenteditable 绝对定位的元素提供抓地力,让用户移动它......

In any case, then there is a whole bunch of code to move the absolutely positioned element around inside the top element, thus moving the actual contenteditable. The contenteditable itself has white-space nowrap, etc. etc. to force it to stay a single line.

在任何情况下,然后有一大堆代码来移动顶部元素内的绝对定位元素,从而移动实际的 contenteditable。contenteditable 本身有空格 nowrap 等,以强制它保持一行。

There is also code in the plugin that strips out anything that isn't an image (like br, tables, etc. etc.) from any text that's pasted / dragged into the box. You need some parts of this (like the brs, stripping/normalizing paragraphs, etc.) but maybe you would normally want to keep links, em, strongand/or some other formatting.

插件中还有一些代码可以br从粘贴/拖动到框中的任何文本中去除任何不是图像的内容(如、表格等)。您需要其中的某些部分(如 brs、剥离/规范化段落等),但也许您通常希望保留链接emstrong和/或其他一些格式。

Source: https://gist.github.com/1161922

来源:https: //gist.github.com/1161922

回答by shaggy

You can replace this div with text input (after onclick event is called).
I have used something similar to this pluginand it worked fine.

您可以将此 div 替换为文本输入(在调用 onclick 事件之后)。
我使用了类似于这个插件的东西,它工作得很好。

回答by Hans

with jQuery I have set a .keypress event and then tests for e.keyCode == 13 (return key) and if is return false from the event and the editing is not able to make multilines

使用 jQuery 我设置了一个 .keypress 事件,然后测试 e.keyCode == 13(返回键),如果是从事件返回 false 并且编辑无法制作多行

$('*[contenteditable=yes]').keypress(function(e) {
  if(e.keyCode == 13 && !$(this).data('multiline')) {
    return false;
  }
})