javascript-可编辑的单行输入

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

我们最初的计划是使用overflow: hidden<div>。我们将侦听器用于粘贴事件以及不同的键/鼠标交互,以确保没有多余的标记进入contenteditable(我们将文本从其容器标签中删除,仅保留图像标签) 我们自己插入)。

但是,现在的问题是,如果您放入足够的内容(即其高度增加),则div会重新调整大小。 我们不希望发生这种情况,也不希望仅隐藏文本(即纯文本overflow: hidden)。 所以:

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

如果有一个我想念的相对简单的attribute / css属性可以满足我的要求,那么我将是最好的选择,但是如果有必要,CSS + JS的建议也将受到赞赏。

Gijs asked 2019-11-16T21:15:26Z
9个解决方案
104 votes

<div contenteditable="true" class="single-line">
    This should work.
</div>​
<div contenteditable="true" class="single-line">
    This should work.
</div>​

Alessio answered 2019-11-16T21:15:42Z
7 votes

我认为您正在寻找的min-height: 40px min-height:20px仅包含一行文本,当溢出div时,该文本会水平滚动。这应该可以解决问题:[http://jsfiddle.net/F6C9T/1]

min-height: 40px
min-height: 40px

min-height: 40px包含出现水平滚动条时的高度。 当出现水平滚动条时,min-height:20px会自动展开,但这在IE7中不起作用(尽管您可以使用条件注释来应用单独的样式)。

tw16 answered 2019-11-16T21:16:23Z
3 votes

这是一个相对简单的解决方案,它使用contenteditable的input事件来扫描dom并过滤掉各种新行样式(因此,它应该对复制/粘贴,拖放,按键盘上的Enter等具有鲁棒性)。 它将多个TextNode压缩为单个TextNode,从TextNode删除换行符,杀死BR,并在其接触的任何其他元素上放置“ display:inline”。 已在Chrome上测试,无法保证其他任何地方。

<div id="editable" contenteditable="true"></div>
<div id="editable" contenteditable="true"></div>
<div id="editable" contenteditable="true"></div>

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

josh answered 2019-11-16T21:17:04Z
2 votes

如果除了更改需求之外,您还希望采用其他解决方法,只需添加<div class="container1"> <div class="container2"> <div contenteditable="true" class="textarea"></div> </div> </div>92,这完全有可能=)

<div class="container1">
    <div class="container2">
        <div contenteditable="true" class="textarea"></div>
    </div>
</div>
<div class="container1">
    <div class="container2">
        <div contenteditable="true" class="textarea"></div>
    </div>
</div>

Berge Aadland answered 2019-11-16T21:17:37Z
2 votes

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

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

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

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

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

解决方案示例:

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);
	}
});
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);
	}
});
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);
	}
});


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

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

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

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

如果您知道字段为空时将成为<br>,则可以解决此问题。 您需要在此处使用一些JavaScript(您不能使用:empty选择器,因为field包含<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);
	}
});
<!--NEW CODE:-->

<div class="CETextInput" contenteditable></div>
<!--NEW CODE:-->

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


步骤3:解决获得价值的问题:

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

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

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

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


不要使用javascript隐藏<br>

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

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


添加占位符

有人问这个问题,但我想许多使用单行contenteditable元素的人都需要它。 这是如何使用上面讨论过的css和“ empty”类制作占位符的示例:

<!--NEW CODE:-->

<div class="CETextInput" contenteditable></div>
<!--NEW CODE:-->

<div class="CETextInput" contenteditable></div>
<!--NEW CODE:-->

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


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

您还可以通过设置“ overflow-x:自动”,“ overflow-y:隐藏”和“ scrollbar-width:无”来仅使用一个div。 但是“ scrollbar-width”是新属性,仅在Firefox 64+和更高版本中有效。

您还可以添加:

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

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

<!--NEW CODE:-->

<div class="CETextInput" contenteditable></div>
<!--NEW CODE:-->

<div class="CETextInput" contenteditable></div>
<!--NEW CODE:-->

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

此解决方案存在3个填充问题:

  1. 在Firefox(测试于2019-05-11,Firefox 66)中,键入长文本时没有右填充。 这是因为当在具有滚动条的同一元素中使用填充,以及将内容滚动到末尾时,Firefox不会显示底部填充或右侧填充。
  2. 在所有浏览器中,在中间位置滚动长文本时都不会填充。 看起来更糟 <input type =“ text”>没有此问题。
  3. 当用户按下Home或End浏览器时,滚动到不显示填充的位置。

要解决这些问题,您需要使用3个元素,就像我们之前使用的一样,但是在这种情况下,您不需要使用scrollbar-width。 我们的3个元素的解决方案没有这些问题。


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

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

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

但是,在那之前,我最终做了很多工作来创建手动移动的单行RTF编辑器。 最后,我将其包装在jQuery插件中。 我没有时间来完成它(IE中可能存在错误,Firefox运行得最好,而Chrome运行得很好-评论很少,有时不是很清楚)。 它使用Rangy库的某些部分(提取出来是因为我不想依赖完整的库)来获取选择的屏幕位置,以便测试鼠标位置与选择的位置(因此您可以拖动选择并移动框)。

大致来说,它通过使用3个元素来工作。 一个外部div(您将其称为插件的东西),将溢出:隐藏,然后在其中嵌套2级。 第一层是绝对定位的,第二层是实际可编辑的内容。 这种分离是必要的,因为否则,某些浏览器将提供内容可编辑的绝对定位的元素抓图,以使用户可以四处移动...

在任何情况下,都有大量代码可以将绝对定位的元素在顶部元素内移动,从而移动实际可编辑的内容。 contenteditable本身具有空白nowrap等,以强制其保持一行。

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

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

Gijs answered 2019-11-16T21:24:15Z
1 votes

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

如何创建HTML5单行contentEditable选项卡,它侦听Enter和Esc

这是HTML标记:

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

这是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();
    }
}

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

trgraglia answered 2019-11-16T21:25:07Z
0 votes

您可以使用文本输入(在调用onclick事件之后)替换此div。
我使用了与此插件类似的工具,并且效果很好。

shaggy answered 2019-11-16T21:25:40Z
-3 votes

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

$('*[contenteditable=yes]').keypress(function(e) {
  if(e.keyCode == 13 && !$(this).data('multiline')) {
    return false;
  }
})
Hans answered 2019-11-16T21:26:06Z
translate from https://stackoverflow.com:/questions/6831482/contenteditable-single-line-input