自适应高度文本框 react contenteditable
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const reduceTargetKeys = (target, keys, predicate) => Object.keys(target).reduce(predicate, {});
const omit = (target = {}, keys = []) =>
reduceTargetKeys(target, keys, (acc, key) => keys.some(omitKey => omitKey === key) ? acc : { ...acc, [key]: target[key] });
const pick = (target = {}, keys = []) =>
reduceTargetKeys(target, keys, (acc, key) => keys.some(pickKey => pickKey === key) ? { ...acc, [key]: target[key] } : acc);
const isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
const propTypes = {
content: PropTypes.string,
editable: PropTypes.bool,
focus: PropTypes.bool,
maxLength: PropTypes.number,
multiLine: PropTypes.bool,
sanitise: PropTypes.bool,
caretPosition: PropTypes.oneOf(['start', 'end']),
tagName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), // The element to make contenteditable. Takes an element string ('div', 'span', 'h1') or a styled component
innerRef: PropTypes.func,
onBlur: PropTypes.func,
onFocus: PropTypes.func,
onKeyDown: PropTypes.func,
onPaste: PropTypes.func,
onChange: PropTypes.func,
styled: PropTypes.bool, // If element is a styled component (uses innerRef instead of ref)
};
const defaultProps = {
content: '',
editable: true,
focus: false,
maxLength: Infinity,
multiLine: false,
sanitise: true,
caretPosition: null,
tagName: 'div',
innerRef: () => { },
onBlur: () => { },
onFocus: () => { },
onKeyDown: () => { },
onPaste: () => { },
onChange: () => { },
styled: false,
};
class ContentEditable extends Component {
constructor(props) {
super();
this.state = {
value: props.content,
isFocused: false,
};
}
componentDidMount() {
this.setFocus();
this.setCaret();
}
componentWillReceiveProps(nextProps) {
if (nextProps.content !== this.sanitiseValue(this.state.value)) {
this.setState({ value: nextProps.content }, () => {
if (!this.state.isFocused) this.forceUpdate();
});
}
}
shouldComponentUpdate(nextProps) {
const propKeys = Object.keys(nextProps).filter(key => key !== 'content');
return !isEqual(pick(nextProps, propKeys), pick(this.props, propKeys));
}
componentDidUpdate() {
this.setFocus();
this.setCaret();
}
setFocus = () => {
if (this.props.focus && this._element) {
this._element.focus();
}
};
setCaret = () => {
const { caretPosition } = this.props;
if (caretPosition && this._element) {
const { value } = this.state;
const offset = value.length && caretPosition === 'end' ? 1 : 0;
const range = document.createRange();
const selection = window.getSelection();
range.setStart(this._element, offset);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
}
};
sanitiseValue(val) {
const { maxLength, multiLine, sanitise } = this.props;
if (!sanitise) {
return val;
}
// replace encoded spaces
let value = val.replace(/ /, ' ').replace(/[\u00a0\u2000-\u200b\u2028-\u2029\u202e-\u202f\u3000]/g, ' ');
if (multiLine) {
// replace any 2+ character whitespace (other than new lines) with a single space
value = value.replace(/[\t\v\f\r ]+/g, ' ');
} else {
value = value.replace(/\s+/g, ' ');
}
return value
.split('\n')
.map(line => line.trim())
.join('\n')
.replace(/\n{3,}/g, '\n\n') // replace 3+ line breaks with two
.trim()
.substr(0, maxLength);
}
_onChange = ev => {
const { sanitise } = this.props;
const rawValue = this._element.innerText;
const value = sanitise ? this.sanitiseValue(rawValue) : rawValue;
if (this.state.value !== value) {
this.setState({ value: rawValue }, () => {
this.props.onChange(ev, value);
});
}
};
_onPaste = ev => {
const { maxLength } = this.props;
ev.preventDefault();
const text = ev.clipboardData.getData('text').substr(0, maxLength);
document.execCommand('insertText', false, text);
this.props.onPaste(ev);
};
_onBlur = ev => {
const { sanitise } = this.props;
const rawValue = this._element.innerText;
const value = sanitise ? this.sanitiseValue(rawValue) : rawValue;
// We finally set the state to the sanitised version (rather than the `rawValue`) because we're blurring the field.
this.setState({
value,
isFocused: false,
}, () => {
this.props.onChange(ev, value);
this.forceUpdate();
});
this.props.onBlur(ev);
};
_onFocus = ev => {
this.setState({
isFocused: true,
});
this.props.onFocus(ev);
};
_onKeyDown = ev => {
const { maxLength, multiLine } = this.props;
const value = this._element.innerText;
// return key
if (!multiLine && ev.keyCode === 13) {
ev.preventDefault();
ev.currentTarget.blur();
// Call onKeyUp directly as ev.preventDefault() means that it will not be called
this._onKeyUp(ev);
}
// Ensure we don't exceed `maxLength` (keycode 8 === backspace)
if (maxLength && !ev.metaKey && ev.which !== 8 && value.replace(/\s\s/g, ' ').length >= maxLength) {
ev.preventDefault();
// Call onKeyUp directly as ev.preventDefault() means that it will not be called
this._onKeyUp(ev);
}
};
_onKeyUp = ev => {
// Call prop.onKeyDown callback from the onKeyUp event to mitigate both of these issues:
// Access to Synthetic event: https://github.com/ashleyw/react-sane-contenteditable/issues/14
// Current value onKeyDown: https://github.com/ashleyw/react-sane-contenteditable/pull/6
// this._onKeyDown can't be moved in it's entirety to onKeyUp as we lose the opportunity to preventDefault
this.props.onKeyDown(ev, this._element.innerText);
};
render() {
const { tagName: Element, content, editable, styled, ...props } = this.props;
return (
<Element
{...omit(props, Object.keys(propTypes))}
{...(styled
? {
innerRef: c => {
this._element = c;
props.innerRef(c);
},
}
: {
ref: c => {
this._element = c;
props.innerRef(c);
},
})}
style={{ minHeight: '0.28rem', minWidth: '100%', display: 'inline-block', whiteSpace: 'pre-wrap', wordWrap: 'break-word', wordBreak: 'break-all', ...props.style }}
contentEditable={editable}
key={Date()}
dangerouslySetInnerHTML={{ __html: this.state.value }}
onBlur={this._onBlur}
onFocus={this._onFocus}
onInput={this._onChange}
onKeyDown={this._onKeyDown}
onKeyUp={this._onKeyUp}
onPaste={this._onPaste}
/>
);
}
}
ContentEditable.propTypes = propTypes;
ContentEditable.defaultProps = defaultProps;
export default ContentEditable;
自适应高度文本框 react contenteditable的更多相关文章
- htm5 手机自适应问题 文本框被激活(获取焦点)时,页面会放大至原来尺寸。
加上这句话就ok啦 <meta name="viewport" content="width=device-width, initial-scale=1.0, mi ...
- html5 textarea 文本框根据输入内容自适应高度
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- jquery文本框textarea自适应高度
浏览器中默认的文本框是不能根据内容的增多变高,只能固定高度有滚动条,体验不是很好,找了很多方法兼容都不行,总算找到个兼容良好的方法: <body> <textarea id=&quo ...
- 自适应高度输入框(contenteditable/textarea)
一.用div模拟textarea div模拟输入域可以根据输入内容自动伸缩,而input和textarea输入内容较多时,高度固定,内容会显示不全. 1.坑1(IOS端无法输入) 在取消全局默认样 ...
- Jquery实现textarea根据文本内容自适应高度
本文给大家分享的是Jquery实现textarea根据文本内容自适应高度,这些在平时的项目中挺实用的,所以抽空封装了一个文本框根据输入内容自适应高度的插件,这里推荐给小伙伴们. autoTextare ...
- Jquery实现 TextArea 文本框根据输入内容自动适应高度
原文 Jquery实现 TextArea 文本框根据输入内容自动适应高度 在玩微博的时候我们可能会注意到一个细节就是不管是新浪微博还是腾讯微博在转发和评论的时候给你的默认文本框的高度都不会很高,这可能 ...
- JS案例 - 可自动伸缩高度的textarea文本框
文本框的默认现象: textarea如果设置cols和rows来规定textarea的尺寸,那么textarea的默认宽高是这俩属性设置的值,可以通过鼠标拖拽缩放文本框的尺寸. textarea如果设 ...
- [Swift通天遁地]二、表格表单-(12)设置表单文字对齐方式以及自适应高度的文本区域TextArea
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- iOS之自动调节输入文本框的高度
//自动调节输入文本框的高度 - (void)textViewDidChange:(UITableView *)textView{ float height; if ([[[UIDevice curr ...
随机推荐
- 查看linux是几位操作系统
查看linux是几位操作系统 摘自:https://blog.csdn.net/a34569345/article/details/80179927 2018年05月03日 14:44:44 bill ...
- 123457123457#0#-----com.yimeng.wangZheChengYu01--前拼后广--成语头脑王者
com.yimeng.wangZheChengYu01--前拼后广--成语头脑王者
- Apache配置优化之开启GZip传输
1.确保apache已经编译的模块里有mod_deflate模块 2.确保apache的配置文件里引入了压缩的模块 3.确保要开启Gzip压缩的虚拟主机配置里有如下配置,并重启apache服务:如果要 ...
- 第二十二章 集成验证码——《跟我学Shiro》
目录贴:跟我学Shiro目录贴 在做用户登录功能时,很多时候都需要验证码支持,验证码的目的是为了防止机器人模拟真实用户登录而恶意访问,如暴力破解用户密码/恶意评论等.目前也有一些验证码比较简单,通过一 ...
- Informix网页数据维护客户端工具
Informix是IBM公司出品的关系数据库管理系统,目前还有在银行,电信等行业使用,Informix的客户端工具很少,数据维护及可视化比较麻烦,现在TreeSoft数据库管理系统已支持Informi ...
- 汉化Git Gui
先来一个图: 其实使用自带的图形化操作git还是很方便的,但是英文界面还是让相当一部分小伙伴不太舒服,比如我哈. 汉化包地址:链接: https://pan.baidu.com/s/1qQtBUsf1 ...
- in-place数据交换
实现in-place的数据交换 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 经典的排序问题 问题描述 一个数组中包含两个已经排好序的子数组,设计一个in- ...
- Linux 安装环境初始化检查 安装Nginx
一 .阿里云 centos 6.8 32 位裸环境 实现:Linux Nginx mysql php redis 查看当前安装的服务 [root@iZgahlk1l73998Z etc]# servi ...
- 详解MySQL 内连接、外连接、左连接、右连接
建表语句: CREATE TABLE `a_table` ( `a_id` ) DEFAULT NULL, `a_name` ) DEFAULT NULL, `a_part` ) DEFAULT NU ...
- ByteDance面试
1.HashMap.HashSet源码解读 2.Http状态码.包头内容有哪些 响应头 说明 示例 状态 Access-Control-Allow-Origin 指定哪些网站可以跨域源资源共享 Acc ...