I'm looking to make a react counter with "target character count" for a textarea just like Twitter's does, which reduces as the user types.
For example, on the "Meta Description" field, the target character count is 160. So, if the field is blank, the number would be 160. As the user types, the count is decreased with each character added to the input field until it reaches zero.
If the count is higher than the target, the numbers are written in red with a minus sign in front (again, just like twitter).
One way to do this is the listen to the onChange event on the textarea, and update the state of the ponent (which has the textarea and the counter), and then use that to calculate length and render the remaining char counter.
Is there a more efficient way?
I'm looking to make a react counter with "target character count" for a textarea just like Twitter's does, which reduces as the user types.
For example, on the "Meta Description" field, the target character count is 160. So, if the field is blank, the number would be 160. As the user types, the count is decreased with each character added to the input field until it reaches zero.
If the count is higher than the target, the numbers are written in red with a minus sign in front (again, just like twitter).
One way to do this is the listen to the onChange event on the textarea, and update the state of the ponent (which has the textarea and the counter), and then use that to calculate length and render the remaining char counter.
Is there a more efficient way?
Share Improve this question asked Oct 12, 2015 at 10:46 ghostCoderghostCoder 7,6559 gold badges53 silver badges74 bronze badges 2- 1 This would be the exact way. – Henrik Andersson Commented Oct 12, 2015 at 11:05
- 1 reactfordesigners./labs/… has an example of exactly that. – elithrar Commented Oct 12, 2015 at 11:26
5 Answers
Reset to default 14Here is a rough version of what you wanted. Doesn't handle when chars_left
goes below zero, but should be easy to implement.
var TwitterInput = React.createClass({
getInitialState: function() {
return {
chars_left: max_chars
};
},
handleChange(event) {
var input = event.target.value;
this.setState({
chars_left: max_chars - input.length
});
},
render: function() {
return (
<div>
<textarea onChange={this.handleChange.bind(this)}></textarea>
<p>Characters Left: {this.state.chars_left}</p>
</div>
);
}
});
https://jsfiddle/terda12/b0y4jL6t
I have made a version too,
I am using styled-ponents for styling.
import React from "react";
import styled from "styled-ponents";
class CharCountInput extends React.Component {
state = {
charsLeft: null
};
ponentDidMount() {
this.handleCharCount(this.props.value);
}
handleCharCount = value => {
const { maxChars } = this.props;
const charCount = value.length;
const charsLeft = maxChars - charCount;
this.setState({ charsLeft });
};
handleChange = event => {
this.handleCharCount(event.target.value);
this.props.onChange(event);
};
renderCharactersLeft = () => {
const { charsLeft } = this.state;
let content;
if (charsLeft >= 0) {
content = <SpanOK>{`characters left: ${charsLeft}`}</SpanOK>;
} else if (charsLeft != null && charsLeft < 0) {
const string = charsLeft.toString().substring(1);
content = <SpanError>{`too many characters: ${string}`}</SpanError>;
} else {
content = null;
}
return content;
};
render() {
const { onBlur, value, type, name, placeholder } = this.props;
return (
<Div>
<Input
onChange={this.handleChange}
value={value}
type={type}
name={name}
placeholder={placeholder}
/>
{this.renderCharactersLeft()}
</Div>
);
}
}
export default CharCountInput;
const Div = styled.div`
display: flex;
flex-direction: column;
`;
const Input = styled.input`
box-sizing: border-box;
display: block;
padding: 7px;
width: 100%;
margin: 0 0 0.1rem 0;
border: 1px solid blue;
border-radius: 7px;
font: inherit;
outline: none;
&:focus {
box-shadow: 0 0 4px blue;
}
`;
const Span = styled.span`
align-self: flex-end;
font-size: 0.9rem;
margin: 0 8px 10px 0;
`;
const SpanOK = styled(Span)`
color: black;
`;
const SpanError = styled(Span)`
color: red;
`;
And inside your form:
<CharCountInput
onChange={this.handleChange}
value={this.state.title}
type="text"
maxChars={150}
name="title"
placeholder="Enter text here..."
/>
here is my version
setInterval(function (){
var counter = document.getElementById('counter')
var input = document.getElementById('input')
var input_v = input.value;
var input_vl = input_v.length;
counter.innerHTML = input_vl;
},0)
<body>
<textarea id="input" cols="30" rows="10"></textarea>
<span id="counter"></span>
</body>
var TwitterInput = React.createClass({
getInitialState: function() {
return {
chars_left: max_chars
};
},
handleChange(event) {
var input = event.target.value;
this.setState({
chars_left: max_chars - input.length
});
},
render: function() {
return (
<div>
<textarea onChange={this.handleChange.bind(this)}></textarea>
<p>Characters Left: {this.state.chars_left}</p>
</div>
);
}
});
This answer above from Keith Young works great (tweaked some for my react app). To add the set limit for max characters so it doesn't go negative, simply add
maxlength={max_chars}
to the textarea element.
Here React Native Way to do this but you can use this method with react.js too
//your state
constructor() {
super();
this.state = {
chars_left: 500,
chars_exceed: false,
post_text: ''
};
};
//handle change function
handleChange(text) {
this.setState({
chars_left: text.length,
chars_exceed: text.length > 500 ? true : false,
post_text: text,
});
}
//input inside render
render() {
return (
<View>
//text area
<Textarea
rowSpan={10}
value={this.state.post_text}
onChangeText={this.handleChange.bind(this)}
placeholder="Type here"/>
//text balance count
<Text>{ this.state.chars_left }/500</Text>
</View>
)
}