使用contenteditable+div模拟textarea文本域实现高度自适应

开发过程中由于需要在发送消息的时候需要有一个可以高度自适应的文本域,一开始是使用textarea并搭配auto-size插件来做到textarea的高度自适应,后来因为遇到一些问题,而且也多加了依赖缺乏可定制,所以决定使用contenteditable来实现。

contenteditable介绍

contenteditable属性规定元素内容是否可编辑,是H5新增的属性,支持情况相当好,基本上所有的浏览器都兼容。

语法:

<element contenteditable="true|false">

实现主要代码如下

代码实现是基于vue来实现的。
html部分:

<template>
<div class="textarea" contenteditable="true"></div>
</template>

CSS部分

<style scoped lang="less" rel="stylesheet/less">
.textarea {
box-sizing: border-box;
min-height: 136px;
max-height: 300px;
margin-left: auto;
margin-right: auto;
padding: 3px;
outline: 0;
border: 1px solid #a0b3d6;
font-size: 12px;
word-wrap: break-word;
overflow-x: hidden;
overflow-y: auto;
_overflow-y: visible;
-webkit-user-modify: read-write-plaintext-only; // 只是编辑text文本,只能解决webkit内核里面问题,手机端适用
-webkit-user-select: text; // 解决IOS部分手机不支持contenteditable=true属性问题
p {
margin: 0;
}
}
</style>

代码解读:

  • 设置-webkit-user-modify属性,是为了在剪切复制的时候会把剪切的内容的格式也一并带过来,由于我们是仿写textarea,是不支持富文本的,所以需要需要将内容格式化成文本格式,而该属性在webkit内核下就可以达到我们的目的。
  • 设置-webkit-user-select属性,是为了解决在测试过程中出现部分IOS手机不支持contenteditable属性的问题。

JS部分:

<script type="text/babel">
export default {
mounted() {
this.addInputEvent();
this.addFocusEvent();
this.addEventPaste(this.$el);
},
methods: {
/**
* 监听鼠标input事件
*/
addInputEvent(){
this.$el.addEventListener('input', () => {
this.$emit('input', this.getValue());
})
},
/**
* 监听鼠标获取焦点事件
*/
addFocusEvent(){
this.$el.addEventListener('focus', () => {
setTimeout(() => {
// 解决:如果ios手机使用的不是原生键盘(也可能不止IOS手机有这个问题),则会出现键盘挡住输入框问题,当bottom=0的情况,使用这个属性就可以滚动屏幕中央
this.$el.scrollIntoView(true);
}, 300);
this.$emit('focus');
})
},
/**
* 追加
* @param value
* @param bool
*/
appendValue(value, bool) {
this.$el.innerText += value;
this.$emit('input', this.getValue());
},
/**
* 监听复制事件,去除样式得到纯文本
*/
addEventPaste: function (el) {
// 干掉IE http之类地址自动加链接
try {
document.execCommand("AutoUrlDetect", false, false);
} catch (e) {
}
// 监听复制paste事件,目的是为了让-webkit-user-modify属性兼容IE8,毕竟该属性在IE兼容性不好
el.addEventListener('paste', function (e) {
e.preventDefault();
var text = null;
if (window.clipboardData && clipboardData.setData) {
// IE
text = window.clipboardData.getData('text');
} else {
text = (e.originalEvent || e).clipboardData.getData('text/plain') || prompt('在这里输入文本');
}
// 这里的目的是为了将鼠标的光标移动到复制之后文本的末尾的末尾
if (document.body.createTextRange) {
if (document.selection) {
textRange = document.selection.createRange();
} else if (window.getSelection) {
sel = window.getSelection();
var range = sel.getRangeAt(0);
// 创建临时元素,使得TextRange可以移动到正确的位置
var tempEl = document.createElement("span");
tempEl.innerHTML = "&#FEFF;";
range.deleteContents();
range.insertNode(tempEl);
textRange = document.body.createTextRange();
textRange.moveToElementText(tempEl);
tempEl.parentNode.removeChild(tempEl);
}
textRange.text = text;
textRange.collapse(false);
textRange.select();
} else {
// Chrome之类浏览器
document.execCommand("insertText", false, text);
}
});
},
/**
* 替换
* @param value
*/
setValue(value) {
this.$el.innerText = value;
this.$emit('input', this.getValue());
},
/**
* 获取值
* @returns {*}
*/
getValue() {
return this.getHtmlToText();
},
/**
* 获取HTML转换之后的文本(去除div标签,替换<br/>为换行)
* @returns {string}
*/
getHtmlToText() {
return this.replaceToBreak(this.getHtml());
},
/**
* 获取HTML
*/
getHtml() {
return document.querySelector('.textarea').innerHTML
},
/**
* 替换DIV到换行符
* @returns {string}
*/
replaceToBreak(html) {
html = String(html).replace(/<\/div>/gi, '');
html = html.replace(/<div>(<br>)?(<br\/>)?/gi, '\n');
html = html.replace(/<br>|<br\/>/gi, '\n');
return html;
},
/**
* 获取纯text文本
* @returns {string}
*/
getText(){
if (window.navigator.appName.indexOf("Explorer") > -1)
return this.$el.innerText;
else
return this.$el.textContent;
}
},
}
</script>

代码解读:

  • 其中addEventPaste方法是为了解决非webkit内核对于-webkit-user-select属性支持不好的问题。里面主要是监听黏贴事件然后或者剪切板的文本内容然后再阻止黏贴事件,并将文本内容追加到光标中,并将光标移动到相应的位置。
  • 其中replaceToBreak方法是为了解决在textarea中换行的问题,在该伪textarea中换行是会单独将换行内容放到新的DIV中的,所以,当我们需要对该内容进行格式化处理才行。
  • this.$el.scrollIntoView的作用是为了当使用者将我们的输入框是使用绝对定位放在页面底部的时候而被虚拟键盘遮挡的问题。

issue

  • 使用该组件注意一个问题就是不要在可视化区域的节点上使用-webkit-user-select: none样式,否则会出现当鼠标焦点小时光标和小水滴无法消失的情况

github地址

github项目地址

参考链接

div模拟textarea文本域轻松实现高度自适应
如何让contenteditable元素只能输入纯文本

使用contenteditable+div模拟textarea文本域实现高度自适应的更多相关文章

  1. div模拟textarea文本域轻松实现高度自适应——张鑫旭

    by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=1362 一.关于tex ...

  2. div模拟textarea文本域轻松实现高度自适应

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. css之——div模拟textarea文本域的实现

    1.问题的出现: <textarea>标签为表单元素,但一般用于多行文本的输入,但是有一个明显的缺点就是不能实现高度自适应,内容过多就回出现滚动条. 为了实现高度自适应:用div标签来代模 ...

  4. jquery之div模拟textarea文本域轻松实现高度自适应

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. textarea文本域的高度随内容的变化而变化

    用css控制textarea文本域的高度随内容的变化而变化,不出现滚动条. CSS代码: 复制代码 代码如下: .t_area{ width:300px; overflow-y:visible } & ...

  6. DIV仿textarea文本域,contenteditable如何只能输入纯文本

    对于支持HTML5浏览器有2种方法: 1. HTML5 <div contenteditable="plaintext-only"></div> 2.  C ...

  7. div模拟textarea自适应高度

    之前在公司做项目的时候,有这么一个需求,要我写一个评论框,可以随着评论的行数增加而自动扩大,最开始我想用textarea实现,但是后来尝试后发现textarea并不适合,textarea的高度不会随着 ...

  8. textarea文本域轻松实现高度自适应

    转载:http://www.xuanfengge.com/textarea-on-how-to-achieve-a-high-degree-of-adaptive.html 今天需要些一个回复评论的页 ...

  9. div模拟textarea

    有些Weber可能没有用过contenteditable这个属性,如果想编辑一个DIV里面的内容,这个属性是一个非常不错的选择   <div contenteditable="true ...

随机推荐

  1. 深度优先搜索(DFS)和广度优先搜索(BFS)求解迷宫问题

    用下面这个简单的迷宫图作为例子: OXXXXXXX OOOOOXXX XOXXOOOX XOXXOXXO XOXXXXXX XOXXOOOX XOOOOXOO XXXXXXXO O为通路,X为障碍物. ...

  2. WdatePicker插件知识整理(一)

    当WdatePicker.js里的属性:$wdate=true时,在input里加上class="Wdate"就会在选择框右边出现日期图标,如果您不喜欢这个样式,可以把class= ...

  3. 二十、linux文件系统讲解

    1.分区和文件系统的关系: 为什么需要格式化呢?这是因为分区文件系统在没有格式化前,操作系统是无法识别系统分区的格式的,就没办法组织文件目录属性和权限等内容,把分区格式化成操作系统支持的某个文件系统后 ...

  4. LeetCode No.121,122,123

    No.121 MaxProfit 买卖股票的最佳时机 题目 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你 ...

  5. Linux中vim的基本操作

    Vim三种模式之间的相互转换: 1.拷贝当前行 yy,拷贝当前行的向下五行 5yy,粘贴使用p: 2.删除当前行 dd,删除当前行的向下五行 5dd: 3.在文件中查找某个单词[命令行模式下  /关键 ...

  6. ios UINavigationController 导航栏

    添加全屏侧滑返回 .获取到系统的pop返回手势 .获取pop在哪个view上 .获取target,action .自定义UIPanGestureRecognizer //1.获取手势 guard le ...

  7. Redis为什么这么快以及持久化机制

    1.首先我们谈一下为什么Redis快: 一. Redis是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快. 二. 再说一下IO,Redis使用的是 ...

  8. django框架基础-django模板语言-长期维护

    ##################     常用的模板语言       ####################### # django的模板语言, # # 只需要记两种特殊符号: # # {{ } ...

  9. mvn相关介绍和命令

    1.前言 Maven,发音是[`meivin],"专家"的意思.它是一个很好的项目管理工具,很早就进入了我的必备工具行列,但是这次为了把project1项目完全迁移并应用maven ...

  10. springboot 配置热部署 及 热部署后依旧是404的坑

    springboot配置热部署的教程网上一大堆: 个人喜欢这种方式: https://www.cnblogs.com/winner-0715/p/6666579.html 本文主要强调的是,大家如果配 ...