有趣的HTML属性:contenteditable

最近在做一个需求, 需求中有个这样效果:有一个输入框,输入框有placeholder提示音输入框的宽度随着内容的长度自适应,并且输入框不可以换行显示,键盘回车就相当于确认值。刚开始我接到这样的效果,想这还不简单,input不就可以实现吗?但是到我真的进入到开发的时候, 才发现不是很对劲, input 好像实现不了。 那么具体哪里实现不了? 那么解决的办法是什么呢?有没有什么可以替代的办法呢?我们来一一看一下具体过程。

input 实现

由于项目是VUE技术栈,那么久直接使用VUE的方式举例子了。

首先我们来看input 是否能实现:

我们页面正常的放入`input`标签,没有给它设置一个固定的宽度值,那么它就会有默认不变的宽度,想让它动态宽度自适应是不可能的。或者我们给它设置一个宽度,那么`input`的宽度也就固定了,也不能动态自适应内容的宽度了。

那要是非得要用input 标签来实现的话, 能不能实现呢? 答案是能的, 代码那么神奇,看你自己的思路。 我这里提供一种思路吧!

既然input标签是有默认宽度的,所以css的宽度就不能不写。我们就直接设置它的宽度为100%,让它跟这父元素的宽度来改变。父元素的宽度我们可以使用span标签撑起来。span标签的内容与input的内容是一样,然后input标签绝对定位,把span盖起来。 大体就是这么个思路。我们来看看例子:

这样就完美实现了。当然也有很多其他的思路, 看自己有么有想出更好的实现方案吧。

contenteditable 属性实现

当然除了上面的input方式实现。也还有其他的方式可以实现,这就是我们这篇文章的重点:HTMLContentEditable 属性。

contenteditable 属性是HTML5的新属性。规定元素的内容是否可编辑。它的属性值有两种:

  • true:规定元素的内容是可编辑的
  • false:规定元素的内容是不可编辑的

看到这里,大家都会想到textarea。但是contenteditable属性与textarea 是不一样的。

  • textarea支持多行文本的输入,满足我们很多编辑的需求。但是textarea有一个致命的去诶单,那就是不能像div类似这样的标签宽度,高度自适应。
  • textarea 只支持文本的输入。但是现在的很多需求需要在编辑区假如图片,链接,视频等。textarea 是做不到的, 但是标签上设置contenteditabletrue的话。是可以实现的。

那我们现在使用 contenteditable属性来实现我们上面说的需求:有一个输入框,输入框有placeholder提示音,输入框的宽度随着内容的长度自适应,并且输入框不可以换行显示,键盘回车就相当于确认值;

具体的实现方式:

下面需要注意的点,我们一一来讲解

placeholder提示语

inputtextarea能很轻松的实现placeholder提示语的效果,但是contentediable的元素placeholder不起作用。但是可以通过CSS:empty解决:

html:

1
<span class="input" placeholder="请输入标签"></span>

css:

1
2
3
4
5
/* placeholder的设置 */
.input:empty::before {
content: attr(placeholder);
color: #ccc;
}

:empty CSS 伪类 代表没有子元素的元素。子元素只可以是元素节点或文本(包括空格)。注释或处理指令都不会产生影响。

自适应宽度

我们对span标签设置即可,span标签的宽度是内容的宽度撑开的。

但是这里一点需要注意:需要把 span 设置为display: inline-block。否则标签得到焦点将无法看见光标。

1
2
3
4
5
6
7
8
.input {
border: 1px solid #ccc;
font-size: 16px;
padding: 5px;

/* 重要: 不设置这个光标看不见 */
display: inline-block;
}

为啥设置成 inline-block 就可以看见了呢?

块级元素:是指当它们显示在浏览器中时,会在自身前后各插入一个空行,而使自身在页面中占据一个相对独立的块状区域的元素

行内元素: 默认是没有宽度的

答案:因为行内元素是没有宽度的,光标需要1px 的宽度,所以需要设置成inline-block或者 block就可以显示光标了

内容不可以换行显示

我们在标签的 keydown事件的时候,判断当前是否是换行 enter键,如果是,那么我们直接阻止浏览器的换行操作。

1
2
3
4
5
6
if (e.keyCode === 13) {
// 阻止换行
e.cancelBubble = true;
e.preventDefault();
e.stopPropagation();
}

keyCode:13 就是键盘的 enter 键

获取内容

可以通过innerHTMLinnerTexttextContent 获取输入框的内容,介绍一下区别:

  • innerHTML 返回标签之间的内容,包括标签元素和文本信息,基本上所有浏览器都支持。
  • innerText/textContent:标签之间的纯文本信息,会将标签过滤掉。

innerTexttextContent虽然都是获取标签的内容,但是两者也是存在差异的。具体可看 innerText和textContent的区别

我们这个例子使用的innerText

1
2
// 取值
const newValue = e.target.innerText;

有些文章说光标在火狐浏览器会有异常,但是看了,其实没问题,应该是浏览器修复bug了吧

contenteditable 兼容性问题

参考文章

contenteditable 踩坑记

contenteditable属性

文章作者: 舒小琦
文章链接: https://shuliqi.github.io/2021/03/20/webpack学习-优化(optimization)/HTML的ContentEditable-属性/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 舒小琦的Blog