I am trying to hide the field when user click on outside the input field and show the field when user click on input field. I have seen some example on stackoverflow where they said to use e.stopPropagation() I did it but still in my case its not working. I don't know whats I am doing wrong.
Here is my code.
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.pasteRef = React.createRef();
this.state = {
showPastePopup: false,
fname: ''
}
}
onPasteClick = () =>{
this.setState({
showPastePopup: false
})
}
closePastePopup = e => {
console.log("called")
this.setState({
showPastePopup:false
})
}
showPastePopup = e => {
e.stopPropagation();
let posx = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
let posy = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
this.pasteRef.current.style.left = posx + "px";
this.pasteRef.current.style.top = posy + "px";
this.setState({
showPastePopup: true,
})
}
render() {
return (
<div>
<h2>Todos: Some text will be here.........</h2>
<span id="pasteBox" ref={this.pasteRef} onClick={this.onPasteClick} className={this.state.showPastePopup ? "showCopyBox control" : "hideCopyBox control"}>
paste
</span>
<div id="pasteArea" className="col w-50 border border-success " onClick={this.closePastePopup}>
<div className="mt-1">
First Name: <input onMouseUp={this.showPastePopup} value={this.state.fname || ''} type="text" id="fname" name="fname" /> {this.state.fname}
</div>
<br/>
<div className="mt-1">
Last Name: <input onMouseUp={this.showPastePopup} value='' type="text" id="lname" name="lname" />
</div>
<br/>
<div className="mt-1">
Address: <input onMouseUp={this.showPastePopup} value='' type="text" id="add" name="add" />
</div>
</div>
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
If I comment on closePastePopup() then its show the popup but how can close the popup if user click outside of the input tag?
Any help will be appreciated.
Here is my jsfiddke
The problem is that you are using the different types of events, if you write onClick or onMouseUp for both elements, it works.
The stopPropagation method stops only the current event.
You didn't pass e to methods and didn't bind them in constructor.
Constructor should look like this:
constructor(props) {
super(props)
this.pasteRef = React.createRef();
this.state = {
showPastePopup: false,
fname: ''
}
this.onPasteClick = this.onPasteClick.bind(this);
this.closePastePopup = this.closePastePopup.bind(this);
this.showPastePopup = this.showPastePopup.bind(this);
}
And when you invoke methods:
<input onMouseUp={e => this.showPastePopup(e)} value='' type="text" id="add" name="add" />
Related
I am new to Svelte and web development and I hope you could point me in the right direction.
The current code belongs in a svelte component.
As shown here: https://svelte.dev/repl/f0e5c30117724ec38b7d19781d2c4de6?version=3.48.0
It is supposed to show one text field by default, while allowing for an additional text field to be added and removed on dynamically added buttons.
Currently, this code can dynamically add the text field, however, it cannot dynamically remove the text field on button click.
I believe there might be an error in the GetDynamicElement function. However, I am not sure where exactly. Any suggestions?
p.s. I know there are answers here that are close, but I don't think they are applicable in this situation, especially on Svelte.
<script>
var num_links = 1;
let container;
const GetDynamicElement = (value) => {
return (
'<input name = "DynamicField" type="text" size =111 id =link placeholder="Enter Next link! " value = "' +
value +
'" />' +
'<input type="button" value="Remove" on:click = {RemoveField(this)}>'
// "RemoveSuggestionCart(this)" />'
);
};
const addField = () => {
if (num_links < 2) {
console.log("addField");
const div = document.createElement("DIV");
div.innerHTML = GetDynamicElement("");
container.appendChild(div); // Append timetable space
num_links += 1;
}
};
//Removes the entire division inclusive of it's text field.
const RemoveField = (div) => {
console.log("RemoveField");
div.removeChild(div.parentNode);
num_links -= 1;
};
</script>
<div>
<input
name="DynamicField"
type="text"
size="121"
id="link"
placeholder="Enter First Link!"
/>
<div bind:this={container} />
</div>
<button on:click|preventDefault={addField}>[+ add timetable link]</button>
<style>
</style>
Add/ remove fields and have a button to log or send to endpoint or whatever with "Log" button.
<script>
// to display one empty inputs before clicking add needed
let values=[{
"url": "",
"name": "",
}];
const addField = () => {
values = [...values, {url: '', name: ''}]
};
const removeField = () => {
values = values.slice(0, values.length-1)
};
</script>
{#each values as v, i}
<div>
<input id={i} type="text" bind:value={values[i].url} placeholder="url"/>
<input id={i} type="text" bind:value={values[i].name} placeholder="name"/>
</div>
{/each}
{#if values.length >= 2}
<input type="button" value="Remove" on:click={removeField}>
{/if]
<button on:click|preventDefault={addField}>Add</button>
<button on:click={() => console.log(values)}>Log Me</button>
Try: https://svelte.dev/repl/2441993f8d9946aa894bf07a8a8f9b4f
Edited: thanks #Corrl - edited nicer.
You can use your num_link and svelte's #each to create to inputs using svelte:
{#each Array(num_links) as _, i}
<div>
<input
name="DynamicField"
type="text"
size="121"
id={`link_${i}`}
placeholder="Enter First Link!"
/>
</div>
{/each}
Working example: https://svelte.dev/repl/04169e030b6944258cfd07af15873b48?version=3.48.0
I've been working on a BMI calculator written in react.js class component, i found out that clicking on a submit button won't mutate some of the this.state values at first time, but the second click would mutate all values, i've read relevant questions in these links:
Pass values to child component on click submit button - ReactJS, click twice
ReactJS - Need to click twice to set State and run function
but i couldn't figure it out how to add changes to my code.
Here is my code:
import React, {Component} from 'react';
class App extends Component{
constructor(props){
super(props);
this.state = {fullName: '', weight: '', height: '', bmi: '', message: '', optimalWeight: ''};
this.handleChange = this.handleChange.bind(this);
this.calculateBMI = this.calculateBMI.bind(this);
this.handleSubmit =this.handleSubmit.bind(this);
}
handleChange(e){
this.setState({[e.target.name]: e.target.value});
}
calculateBMI(){
let heightSquared = (this.state.height/100 * this.state.height/100);
let bmi = this.state.weight / heightSquared;
let low = Math.round(18.5 * heightSquared);
let high = Math.round(24.99 * heightSquared);
let message = "";
if(bmi >= 18.5 && bmi <= 24.99){
message = "You are in a healthy weight range";
}
else if(bmi >= 25 && bmi <= 29.9){
message = "You are overweight";
}
else if(bmi >= 30){
message="You are obese";
}
else if(bmi < 18.5){
message="You are under weight";
}
this.setState({message: message});
this.setState({optimalWeight: "Your suggested weight range is between "+low+ " - "+high});
this.setState({bmi: Math.round(bmi * 100) / 100});
}
handleSubmit(e){
this.calculateBMI();
e.preventDefault();
console.log(this.state);
}
render(){
return(
<div className="App">
<div className="App-header">
<h2>BMI calculator</h2>
</div>
<form onSubmit={this.handleSubmit}>
<label>
Please enter your name
</label>
<input type="text" name="fullName" value={this.state.fullName} onChange={this.handleChange} />
<label>
Enter your height in cm
</label>
<input type="number" name="height" value={this.state.height} onChange={this.handleChange} />
<label>
Enter your weight in kg
</label>
<input type="number" name="weight" value={this.state.weight} onChange={this.handleChange} />
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
export default App;
update:
I also check the question Using async setState but my problem is not about updating in an async way but thanks to #LMulvey i did solve it by grouping setState and adding a console.log as callback function to it in calculateBMI()
Your app is actually working just fine. The problem is that you're trying to log your state before setState has fully finished. If you instead attach your console.log as a callback to your final setState call, you'll see that it's working as intended.
this.setState({bmi: Math.round(bmi * 100) / 100}, () => console.log(this.state));
It's worth noting that you don't need one setState call for each property that you're updating and you can group them together like this, which would also be leading to your issue of race conditions here:
this.setState({
bmi: Math.round(bmi * 100) / 100,
optimalWeight: "Your suggested weight range is between "+low+ " - "+high,
message
}, () => console.log(this.state));
I am trying to use input type text along with input type range where three usecases are there.
1. defaultcase: when page is loaded the slider should point to the value as given in the text input field.
2. when user enters the text value, the slider pointer should also update.
3. when user uses the slider to change value, the text value should get updated.
In my below code
In default case: in input type range when i give defaultValue="200" the slider pointer points always to min value.
When i enter value in text area, the slider is not getting updated.
i tried some ways to fix it but failing.
if (
Type == "integer" &&
LowerBound &&
UpperBound !== 0
) {
editComponent = (
<div className="SettingsTreeValueNode__InputField">
<input
type="text"
pattern="[0-9]*"
ref="text"
value={this.state.value}
oninput={this.handleinputChanged.bind(this)}
className="inputtext"
onBlur={e => this.focusOfInputLost(e)}
/>
{LowerBound}
<input
type="range"
className="sliderinput"
defaultValue="200"
min={LowerBound}
max={UpperBound}
step="1"
oninput={this.handleSliderChange.bind(this)}
onMouseUp={this.handleWhenMouseReleased.bind(this)}
/>
{UpperBound}
</div>
);
}
handleSliderChange(event) {
var value = event.target.value;
var slidervalue = parseInt(value);
this.setState({ value: slidervalue });
}
handleInputChanged(event) {
var value = event.target.validity.valid
? event.target.value
: this.state.value;
this.setState(
{
value: value,
inputValid: this.isInputValid()
});
}
ยดยดยด[![slider along with text area][1]][1]
[1]: https://i.stack.imgur.com/w5MZ6.png
Below code will solve your problems, only use single state object to display value in both controls now it will works in both way either change value on input or slider.
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
}
handleSliderChange = (event) => {
var value = event.target.value;
var slidervalue = parseInt(value);
this.setState({ value: slidervalue });
}
handleInputChanged = (event) => {
var value = event.target.validity.valid
? event.target.value
: this.state.value;
this.setState(
{
value: value
});
}
render() {
let LowerBound = 0;
let UpperBound = 200
return (
<div>
<button onClick={this.handleCopy}>click me</button>
<div className="SettingsTreeValueNode__InputField">
<input
type="text"
pattern="[0-9]*"
ref="text"
value={this.state.value}
onInput={this.handleInputChanged}
className="inputtext"
/>
{LowerBound}
<input
type="range"
className="sliderinput"
defaultValue="200"
min="0"
max="200"
step="1"
value={this.state.value ? this.state.value : UpperBound}
onInput={this.handleSliderChange}
/>
{UpperBound}
</div>
</div>
);
}
}
export default App;
I am creating a random quote generator. There is a quote box that displays quote and author names. I created a method to invoke on a button click that may randomize the quote list and display a new quote and a next button to get next quote from my quote list. I can see the first quote but the component didn't re-renders on clicking buttons or something gets wrong that I can't get next quote or can't randomize. Here is the code:
class UI_qoutebox extends React.Component {
constructor(props) {
super(props);
this.state = { qoutes: props.qoutes, authors: props.authors, num: 0 };
this.UI_rq = this.UI_rq.bind(this);
this.UI_next = this.UI_next.bind(this);
}
UI_rq() {
let rnd = Math.floor(Math.random() * this.state.qoutes.length);
this.setState({ num: rnd });
}
UI_next() {
let p = this.state.num + 1;
if (p > this.state.qoutes.length) { p = 0 }
this.setState({ num: p })
}
render() {
const { qoutes, authors, num } = this.state;
return (
<div className="qoute-box">
<span className="qoute">{qoutes[num]}</span>
<span>{authors[num]}</span>
<input type="button" value="Randomize" onClick={() => this.UI_rq} />
<input type="button" value="Next" onClick={() => this.UI_next} />
</div>
)
}
}
I am working on Freecodecamp's project and I need quick help. Thanks in advance.
Change this
<input type="button" value="Randomize" onClick={this.UI_rq}/>
<input type="button" value="Next" onClick={this.UI_next}/>
I want to dynamically change my DOM's height. But I can't. No errors, console.log prints nothing, just blank with a number indicating how many times this blank line is printed on console. ref works but it seems that whatever I write to the ref won't be updated to the DOM.
On each keypress, my resizeTextInput function is invoked.
constructor(props) {
super(props);
this.state = {
textLen: 0,
commentError: false,
};
this.textInputRef = React.createRef();
this.commentBlockRef = React.createRef();
this.resizeTextInput = this.resizeTextInput.bind(this);
this.handleSearch = this.postComment.bind(this);
this.postComment = this.postComment.bind(this);
}
resizeTextInput() {
this.commentBlockRef.current.style.height = 300;
console.log(this.commentBlockRef.current.style.height);
}
postComment(event) {
this.resizeTextInput();
}
render() {
return (
<div className="comment-wrap">
<div className={`comment-block ${className}`} ref={this.commentBlockRef}>
<textarea className={`textarea-${className}`} type="text" placeholder="Write a comment..." onKeyUp={this.postComment} ref={this.textInputRef} size="55" />
<div className={`comment-block-len ${(commentError || postError) ? 'comment-error' : ''}`}>{textLen}/{MAX_LEN}</div>
</div>
</div>
);
}
}
It should be 300px in string instead 300 only.
this.commentBlockRef.current.style.height = '300px';