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的更多相关文章

  1. htm5 手机自适应问题 文本框被激活(获取焦点)时,页面会放大至原来尺寸。

    加上这句话就ok啦 <meta name="viewport" content="width=device-width, initial-scale=1.0, mi ...

  2. html5 textarea 文本框根据输入内容自适应高度

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  3. jquery文本框textarea自适应高度

    浏览器中默认的文本框是不能根据内容的增多变高,只能固定高度有滚动条,体验不是很好,找了很多方法兼容都不行,总算找到个兼容良好的方法: <body> <textarea id=&quo ...

  4. 自适应高度输入框(contenteditable/textarea)

      一.用div模拟textarea div模拟输入域可以根据输入内容自动伸缩,而input和textarea输入内容较多时,高度固定,内容会显示不全. 1.坑1(IOS端无法输入) 在取消全局默认样 ...

  5. Jquery实现textarea根据文本内容自适应高度

    本文给大家分享的是Jquery实现textarea根据文本内容自适应高度,这些在平时的项目中挺实用的,所以抽空封装了一个文本框根据输入内容自适应高度的插件,这里推荐给小伙伴们. autoTextare ...

  6. Jquery实现 TextArea 文本框根据输入内容自动适应高度

    原文 Jquery实现 TextArea 文本框根据输入内容自动适应高度 在玩微博的时候我们可能会注意到一个细节就是不管是新浪微博还是腾讯微博在转发和评论的时候给你的默认文本框的高度都不会很高,这可能 ...

  7. JS案例 - 可自动伸缩高度的textarea文本框

    文本框的默认现象: textarea如果设置cols和rows来规定textarea的尺寸,那么textarea的默认宽高是这俩属性设置的值,可以通过鼠标拖拽缩放文本框的尺寸. textarea如果设 ...

  8. [Swift通天遁地]二、表格表单-(12)设置表单文字对齐方式以及自适应高度的文本区域TextArea

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  9. iOS之自动调节输入文本框的高度

    //自动调节输入文本框的高度 - (void)textViewDidChange:(UITableView *)textView{ float height; if ([[[UIDevice curr ...

随机推荐

  1. CerntOS7下搭建git服务器

    (1).安装git yum安装git,需要ssh的支持.某些版本需要安装git-core,那才是服务器. [root@youxi1 ~]# yum -y install git 创建git用户 [ro ...

  2. 123457123456#0#-----com.yuming.HitMouse01--前拼后广--幼儿打地鼠游戏

    com.yuming.HitMouse01--前拼后广--幼儿打地鼠游戏

  3. 123457123456#0#-----com.threeapp.magicImageShow01----儿童宝宝魔法画笔

    com.threeapp.magicImageShow01----儿童宝宝魔法画笔

  4. 【转】hr的嘴,骗人的鬼

    入职前,从上往下读,入职后,从下往上读. - 我们非常欢迎新鲜血液补充进我们的团队:- 如果条件太苛刻,我待不久的:- 我们公司绝对不可能这样:- 请问每个月的工作都能按时足额发放吗?- 这难道不是理 ...

  5. Node中导入模块require和import??

    转自:https://blog.csdn.net/wxl1555/article/details/80852326 S6标准发布后,module成为标准,标准的使用是以export指令导出接口,以im ...

  6. VC++单文档程序固定菜单栏和工具栏

    MainFrm.cpp框架类下,找到OnCreate方法 m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY); m_wndToolBar.EnableDocking( ...

  7. if语句、while和for循环

    一.if语句 1.多路分支 if 条件1: 代码块1 elif 条件2: 代码块2 else: 代码块3 #python会执行第一次测试为真的语句,如果所有测试都为假,就执行else部分(本例) 2. ...

  8. 【Pandas数据分析案例】2018年北京积分入户情况分析

    据说,北京落户的难度比加入美国国籍还高.而北京2018年首次实行积分入户制,让我们来分析一下首批通过积分入户拿到北京户口的数据. 首先从北京积分落户官网下载公示名单: 根据表格中的信息,我们主要从以下 ...

  9. python实现文件搜索工具(简易版)

    在python学习过程中有一次需要进行GUI 的绘制, 而在python中有自带的库tkinter可以用来简单的GUI编写,于是转而学习tkinter库的使用. 学以致用,现在试着编写一个简单的磁文件 ...

  10. 1255: 打怪升级(Java)

    WUSTOJ 1255: 打怪升级 Description 对于多数RPG游戏来说,除了剧情就是打怪升级.本题的任务是用最短的时间取得所有战斗的胜利.这些战斗必须按照特定的顺序进行,每打赢一场,都可能 ...