本文转载于 SegmentFault 社区

社区专栏:前端学习路-郭盖

作者:郭盖


阅读全文你将获得以下解决方案:

1. 点击长文本编辑 textarea,自动获得焦点

2. 随着输入值自动伸缩高度

3. 可复制添加信息

4. 可粘贴文本

5. 可粘贴图片

以下实例代码执行环境为 Chrome80


方案一

HTML5 Textarea 元素
1. 自动获得焦点
点击编辑自动获得焦点后光标跳转到了最前面,why?   640e48d8820f73f16d8ad9df54351218.png   查文档 MDN, textarea 元素存在 selectionEnd 和 selectionStart 的属性用来表示选中的文本开始位置和结束位置,DOM 接口实例是  HTMLTextAreaElement , 它具有  setSelectionRange  方法,用来选中输入框中的文本,用法: TextAreaElement.setSelectionRange(selectionStart, selectionEnd, [optional] selectionDirection); 那么我们只要做到  selectionStart === selectionEnd === value.length  , 这样光标就选中到最末尾。  
edit.addEventListener("click", function() {
txt.classList.add("hidden");
document.body.insertBefore(textarea, edit);
textarea.innerHTML = "这是需要编辑的信息";
textarea.focus();
// textarea.selectionEnd = textarea.innerHTML.length;

textarea.setSelectionRange(
textarea.innerHTML.length,
textarea.innerHTML.length
);
});
2. 自适应高度
当不设置高度时换行会出现滚动条,所以只要让 textarea 滚动条消失,让其 height = scrollHieght , 那么如何监听 textarea 的变化,则需要 input 事件,让 textarea 高度动态等于它的 scrollHeight 即可,代码如下:
textarea.addEventListener("input", function() {
this.style.height = `${this.scrollHeight}px`;
});
cef7bb5f5ae77d1ba76d1324805c7c6b.png 为什么每次输入都会使得高度增加 (每次增加 4px) ,经过测试 Safari 同样也是这个效果, Firefox 符合预期高度不异常增加,查看 Chrome 控制台发现 (查看 chromium)  textarea 内置了很多样式,尝试将去掉 padding 后,可以正常随着内容增加高度自适应了。
chromium: https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css

textarea {
padding: 0;
}



const textarea = document.createElement("textarea");
textarea.addEventListener("input", function(e) {
this.style.height = `${this.scrollHeight}px`;
});
为什么去掉 paddi n g 值,输入后高度就不增加了? (未找到合理的解释,欢迎留言讨论解答) 但是删除文本的情况下 height 并未能自动缩放,分析原因, 当删除文本换行的时候 scrollHeight 的大小并未发生改变, scrollHeight 作为只读属性 (MDN-文档) ,如何让 scrollHeight 高度重新计算哪?只有改变高度,于是:
MDN-文档: https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollHeight
textarea.addEventListener("input", function(e) {
this.style.height = "inherit";
this.style.height = `${this.scrollHeight}px`;
});
到此高度自适应输入内容完成,但是每次换行的闪动很难受, transition 走一波  
textarea {
overflow: hidden; // 防止换行出现滚动条闪动
padding: 5px 10px;
box-sizing: border-box;
transition: all 0.2s linear;
}
结果 transition 并没效果,原因 height 初始值或者参考值必须为数值过渡动画才会生效, height 设置为 auto 或者 inherit 动画不会生效, 所以折中的办法就是当删除的时候重新重置高度,输入时不需要重置,这样输入时动画生效, 删除没有动画, 暂时没想到更好的办法。
textarea.addEventListener("keyup", function(e) {
if (e.keyCode === 8) {
this.style.height = "inherit";
this.style.height = `${this.scrollHeight}px`;
} else {
this.style.height = `${this.scrollHeight}px`;
}
});
3. 支持粘贴文本,图片等
textarea 只能输入文本,无法实现粘贴图片。
textarea.addEventListener(
"paste",
function(e) {
e.preventDefault();
console.log(
"paste",
e.clipboardData.items,
e.clipboardData.types,
e.clipboardData.getData("text/html"),
e.clipboardData.getData("text/plain"),
e.clipboardData.getData("text/Files")
);
},
false
);

 

方案二

div contenteditable 替换 textarea
1. 自动获得焦点
编辑 div contenteditable 为 true ,然后利用 Range 和 Selection  光标移动到最后的效果。 
edit.addEventListener("click", () => {
textarea.setAttribute("contenteditable", true);
textarea.focus();
const range = document.createRange();

// range 包含的内容
range.selectNodeContents(textarea);

// range.setStart(textarea.firstChild, 0);
// range.setStart(textarea.lastChild, textarea.innerHTML.length);

// 起始位置是否相同
range.collapse(false);
const sel = window.getSelection();

// 将所有的区域都从选区中移除。
sel.removeAllRanges();

// 一个区域(Range)对象将被加入选区。
sel.addRange(range);
});
2. 自适应高度
div contenteditable  天然支持根据输入来自适应高度。
3. 粘贴图片,文本等
textarea.addEventListener("paste", function(e) {
e.preventDefault();
const clipboardData = e.clipboardData || e.originalEvent.clipboardData;
// 获取纯文本
let text = clipboardData.getData("text/plain");
let file = clipboardData.getData("text/plain");
// console.log(clipboardData.items, clipboardData.getData("text/Files"));

// 插入img,可以做一些上传图片的一些操作
insertImg(clipboardData);

// 只输入纯文本
document.execCommand("insertText", false, text);
});
此方法可以限定只能上传文本或者图片。
欢迎留言讨论其他 textarea 高度自适应的方案。

- END - 6aff03517ad6c9c9ea643ee97d622923.png
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐