自适应高度文本框 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 ...
随机推荐
- LeetCode_122. Best Time to Buy and Sell Stock II
122. Best Time to Buy and Sell Stock II Easy Say you have an array for which the ith element is the ...
- pycurl模块
pycurl的使用 pycurl是curl的一个python版本. pycurl的使用说明: pycurl的使用主要是一些参数的设定. 1,c.setopt(pycurl.URL,myurl) 设定链 ...
- Mac 裁剪mp3
系统自带的QuickTime Player
- 使用 ServiceStack.Text 序列化 json的实现代码
相信做 .net 开发的朋友经常会遇到 json 序列化这样的需要,今天发篇文章总结下自己使用ServiceStack.Text 来序列化 json.它的速度比 Newtonsoft.Json 快很多 ...
- 对图片清晰度问题,纠结了一晚上。清理了下Libray,瞬间变清晰了,泪奔
对图片清晰度问题,纠结了一晚上.清理了下Libray,瞬间变清晰了,泪奔
- shell将txt转换为csv
cat aa.txt | tr -s '[:blank:]' ',' > bb.csv
- 说一说Unsafe魔法类
这篇算是对 Unsafe 的一个总体概况,由于内容实在太多,后续会分开几篇文章对里面内容展开细讲 前言 Unsafe可以说是java的后门,类似西游记中的如来佛祖法力无边,Unsafe主要提供一些用于 ...
- 1、4 前后端分离,写静态HTML文件,通过ajax 返回数据
1.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <ti ...
- K8S从入门到放弃系列-(10)kubernetes集群之kube-proxy部署
摘要: kube-proxy的作用主要是负责service的实现,具体来说,就是实现了内部从pod到service和外部的从node port向service的访问 新版本目前 kube-proxy ...
- VLAN之间通信-三层交换机实现
1.打开三层交换机的命令行,配置VLAN和设置端口IP enable //进入特权模式 configure terminal //进入全局配置模式 ip routing //启动交换机的路由功能 vl ...