Input resizing with React
An automatically expanding textarea that respects word-breaks
HTML
<body>
<script src="https://fb.me/react-15.1.0.js"></script>
<script src="https://fb.me/react-dom-15.1.0.js"></script>
<img style="width:400px;" src="https://cloud.githubusercontent.com/assets/547148/18024962/171ce86c-6bce-11e6-8216-5ab7f361fdde.jpg" />
<p>An automatically expanding textarea that respects CSS word-breaks.</p>
<p>Uses a ghost div to measure height. See CSS comments to hide it, and you're ready to go.</p>
<p>👻</p>
<div id="app"></div>
</body>
SCSS
* {
font-family: monospace;
font-size: 16px;
}
body {
padding: 1em 1em 6em 1em;
}
p, label {
display: block;
margin: 0.8em 0;;
}
.container {
position: relative;
}
.textarea {
width: 360px;
outline: none;
min-height: 20px;
padding: 0;
box-shadow: none;
display: block;
border: 2px solid black;
overflow: hidden; // Removes scrollbar
transition: height 0.2s ease;
}
.textarea--ghost {
opacity: 0.3;
display: block;
white-space: pre-wrap;
word-wrap: break-word;
// Uncomment below to hide the ghost div...
//
// visibility: hidden;
// position: absolute;
// top: 0;
}
Babel + JSX
const DEFAULT_HEIGHT = 20;
class Textarea extends React.Component {
constructor(props) {
super(props);
this.state = {
height: DEFAULT_HEIGHT,
value: "Don't get lost in the upside down",
};
this.setValue = this.setValue.bind(this);
this.setFilledTextareaHeight = this.setFilledTextareaHeight.bind(this);
}
componentDidMount() {
this.mounted = true;
this.setFilledTextareaHeight();
}
setFilledTextareaHeight() {
if (this.mounted) {
const element = this.ghost;
this.setState({
height: element.clientHeight,
});
}
}
setValue(event) {
const { value }= event.target;
this.setState({ value });
}
getExpandableField() {
const isOneLine = this.state.height <= DEFAULT_HEIGHT;
const { height, value } = this.state;
return (
<div>
<label htmlFor="textarea">Add some text...</label>
<textarea
className="textarea"
name="textarea"
id="textarea"
autoFocus={true}
defaultValue={value}
style={{
height,
resize: isOneLine ? "none" : null
}}
onChange={this.setValue}
onKeyUp={this.setFilledTextareaHeight}
/>
</div>
);
}
getGhostField() {
return (
<div
className="textarea textarea--ghost"
ref={(c) => this.ghost = c}
aria-hidden="true"
>
{this.state.value}
</div>
);
}
render() {
return (
<div className="container">
{this.getExpandableField()}
{this.getGhostField()}
</div>
);
}
}
ReactDOM.render(
<Textarea />,
document.getElementById("app")
);