I have a div which is scrollable and i want to refer that div from outside my class. For example
const myDiv = document.getElementById('scrollDiv');
class Test extends React.Component{
listenScrollEvent = (e) => {
console.log("myDiv returns undefined",myDiv);
};
render(){
return (
<div id="scrollDiv" onScroll={this.listenScrollEvent.bind(this)}></div>
)
}
}
Here inside listenScrollEvent i want to access myDiv which is referred to div with id scrollDiv. But i'm getting a undefined value in my console log. I can use const myDiv = document.getElementById('scrollDiv'); inside my listenScrollEvent method but then every time i scroll, referring to div happens.
You can access an element by using refs in React like this -
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.ref = null;
}
listenScrollEvent = () => {
console.log(this.myRef.getBoundingClientRect().top);
};
render() {
return (
<button
ref={my => (this.myRef = my)}
id="scrollDiv"
onScroll={this.listenScrollEvent}
>
Click Me!
</button>
);
}
}
More on refs - https://reactjs.org/docs/refs-and-the-dom.html
Related
I have a component for restaurants list and I have an onClick event for every list-item. Everything is working fine except when I click any child element of the list item. It responds with nothing or undefined (I don't know because nothing is showing up in the console.)
Here is my component:
import React from "react";
class ResturantsList extends React.Component {
constructor(props) {
super(props);
this.state = {
reviews: {}
};
}
handleReviews = e => {
const reviewsList = this.props.resturants.find(
resturant => resturant.id === e.target.id
);
console.log(reviewsList.reviews);
};
render() {
let list = this.props.resturants.map(resturant => {
return (
<li
key={resturant.id}
id={resturant.id}
className="resturant"
onClick={this.handleReviews}
>
<div className="resturant__hero">
<h1 className="resturant__name">{resturant.name}</h1>
<img src={resturant.icon} alt="" className="resturant__icon" />
</div>
<div className="resturant__row">
<p className="item">Rating</p>
<p className="description">{resturant.rating || "N/A"}</p>
</div>
</li>
);
});
return <div>{list}</div>;
}
}
export default ResturantsList;
So the problem is I can only click on the padding of li to get accurate result otherwise it throws error.
This behavior is new for me and very unexpected.
edit-
Binding in the constructor is not the problem, I had that line but this is not real issue. My event working just fine but only when I click li and not any of its child.
Use e.currentTarget
Try using e.currentTarget instead of e.target in your handleReviews(), like so:
handleReviews = e => {
const reviewsList = this.props.resturants.find(
resturant => resturant.id === e.currentTarget.id // <--- Here
);
console.log(reviewsList.reviews);
};
I’ve solved it by adding for each li’s a data-id attribute and binding the handleReviews method to this.
<li
key={resturant.id}
data-id={resturant.id}
className="resturant"
onClick={this.handleReviews}
>
And then in handleReviews I’ll get the element with (e.currentTarget.dataset.id):
handleReviews = e => {
const reviewsList = this.props.resturants.find(
resturant => resturant.id === e.currentTarget.dataset.id
);
};
I have a simple component which looks like this:
import React from "react";
import './MyContainer.css';
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
showWhereClicked = (e) => {
console.log(`you have clicked X:${e.screenX} Y:${e.screenY}`);
// do stuff
}
render() {
return (
<div className="myContainer" onClick={this.showWhereClicked}>
I am 500px tall.
</div>
);
}
}
export default MyContainer;
Whenever I click anywhere inside <MyContainer />, I get a console message giving me an X and Y coordinate of where I clicked on screen.
I wish to place a <div> inside at the X and Y location of my mouse click. Ideally a box or something, say 100x100px wide.
Later I wish to implement a way for me to freely move these <div> components around the screen.
How can I achieve this?
The way I would handle this is by using css in js.
You can set the position of any DOM-Element with position: absolute;, top : yCoordinate and left : xCoordinate css attributes.
// take control over the style of a component
const [style, setStyle] = useState(initialStyle);
const setCoordinates = (x,y) => {
// You don't need whitespace in here, I added it for readability
// I would recommend using something like EmotionJS for this
return `position:absolute;
left:${x}px;
top:${y}px;`
}
...
return(
<div
style = {style}
onClick = { e => {
const newStyle =
setCoordinates(e.target.screenX,
e.target.screenY);
setStyle(newStyle);
}}
></div>)
You can then set those in any shape or form and the desired result should be visible. You won't need to redraw anything, because the DOM didn't change, just the css.
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
placedDiv:{
top:-9999px;
left:-9999px; // hide div first
width:100px;
height:100px;
position:absolute;
}
};
}
showWhereClicked = (e) => {
console.log(`you have clicked X:${e.screenX} Y:${e.screenY}`);
this.setState({
placedDiv:{
top:e.screenY + 'px'
left:e.screenX + 'px'
}
})
// do stuff
}
render() {
return (
<div className="myContainer" onClick={this.showWhereClicked}>
I am 500px tall.
<div style={this.state.placedDiv}></div>
</div>
);
}
}
export default MyContainer;
.myContainer {
position:relative /// in CSS!!!
}
I am working on a React application where I am trying to render text on the screen when a button is clicked. I have defined a function onButtonClick which gets triggered whenever the button is clicked. However, the HTML that I am returning from the function is not rendered on the screen. I am in the learning stages of React so please excuse me if the question seems silly.
class App extends Component {
constructor() {
super();
this.state = {
blockno:0
}
}
OnButtonClick = () => {
this.setState({blockno: this.state.blockno + 1})
return(
<div>
<h3>Some text</h3>
</div>
);
}
render() {
return(
<div>
<Button onButtonClick={this.OnButtonClick}/>
</div>
);
}
}
The value is being returned, but the framework/browser/etc. has no reason to do anything with that value.
Try thinking about this a different way, a "more React way". You don't want to return the value to be rendered, you want to update state. Something like this:
constructor() {
super();
this.state = {
blockno:0,
showDiv: false // <-- note the new property in state
}
}
OnButtonClick = () => {
this.setState({blockno: this.state.blockno + 1, showDiv: true})
}
Now you're not returning anything, but rather updating the state of the component. Then in your render method you conditionally render the UI based on the current state:
render() {
return(
<div>
<Button onButtonClick={this.OnButtonClick}/>
{
this.state.showDiv
?
<div>
<h3>Some text</h3>
</div>
: ''
}
</div>
);
}
The click handler doesn't modify the page, it just modifies the state of the component you're writing. The render method is responsible for rendering the UI based on that state. Any time state changes, render will be called again to re-render the output.
(Note: It's not 100% clear if this is exactly the functionality you're looking for in the UI, since it's not really clear what you're trying to build. But the point here is to illustrate how to update state and render output in React. Your logic can be tweaked as needed from there.)
You have to make a render based on your state. Please check the tutorial at the react docs to learn more about how React works. It's really good
Here is a version of your code that works. Hope it helps
class App extends Component {
constructor() {
super();
this.state = {
blockno: 0
};
}
OnButtonClick = () => {
//updates the states
this.setState({ blockno: this.state.blockno + 1 });
};
//remember: every time there is an update to the state the render functions re-runs
render() {
//variable holding the blocks in an array
let blocks = []
//if blockno is greater than 0, it checks everytime that there is a state change
if (this.state.blockno > 0) {
//for every block added
for (let index = 0; index < this.state.blockno; index++) {
//We`re going to add to the array of blocks a new div with the block number
blocks.push(
<div>
<h3>My block number is {index}</h3>
</div>
);
}
}
return (
<div>
<div>
{/**button that updates the state on every click */}
<button onClick={this.OnButtonClick}>
Click me to add a new div!
</button>
</div>
{/**This render the blocks variable that holds the divs */}
{blocks}
</div>
);
}
}
What I see is that you are trying to build a counter. The value that you're returning from the click handler function can't be rendered, instead you need to manage it in the render function as follow:
class App extends Component {
constructor() {
super();
this.state = {
blockno: 0
}
}
OnButtonClick = () => {
this.setState(prevState => ({ blockno: prevState.blockno + 1 }));
}
render() {
return(
<div>
{this.state.blockno > 0 && <div>some text {this.state.blockno}</div>}
<Button onButtonClick={this.OnButtonClick} />
</div>
);
}
}
Also note that the setState method is asynchronous, please read the documentation https://reactjs.org/docs/react-component.html#setstate
There are multiple divs on a page. What I want to do is get an attribute when a div is clicked. It is very easy in jquery but I am using Reactjs. Here is my code. It works but every time when I click on div attribute of the last element in is returned. Following is my code and the codepen URL.
https://codepen.io/anon/pen/gepVNP?editors=0010
class Content extends React.Component{
constructor(props) {
super(props)
this.click = this.click.bind(this)
}
click(){
// this.prop.setActiveMenu();
var summary = this.refs.summary;
console.log(summary.getAttribute('data-slug'))
}
render(){
return(
<div className="content">
{posts.map((post)=>{
return (
<div ref="summary" data-slug={post.slug} onClick={this.click} key={post.slug}>
<h1>{post.title}</h1>
<div>{post.content}</div>
<br/><br/><br/><br/><br/>
</div>
);
})}
</div>
);
}
}
That's because you're changing the ref element inside the map everytime you iterate on the posts array.
No need for refs here IMO.
Why not use the event.target from the click event?
onClick(event){
console.log(event.target.getAttribute('data-slug'))
}
BTW:
String refs are considered legacy. have a look here:
Why ref='string' is "legacy"?
I discourage you from using this approach, you could use a component instead.
The ref prop accepts a function and return the ref, so passing a string doesn't work.
class Content extends React.Component{
constructor(props) {
super(props)
this.click = this.click.bind(this)
}
click(){
console.log(this.summary.getAttribute('data-slug'))
}
render(){
return(
<div className="content">
{posts.map((post)=>{
return (
<div ref={ref => this.summary = ref} data-slug={post.slug} onClick={this.click} key={post.slug}>
<h1>{post.title}</h1>
<div>{post.content}</div>
<br/><br/><br/><br/><br/>
</div>
);
})}
</div>
);
}
}
Suppose I have two components:
class App extends Component {
insertToStory = (word) => {
// how to get the cursor position here?
}
render() {
return (
<div>
<StoryTextarea text={this.props.text} />
<Toolbar insert={this.insertToStory} />
</div>
)
)
}
the StoryTextarea contains a textarea, and Toolbar contains a button, when clicked, we should insert some word to the textarea under the currrent cursor position. but how can I get the cursor position in insertToStory? or is there other ways to implement this?
Using refs is a good option to achieve that.
1º Add a new method in your StoryTextArea component to get the cursor position.
class StoryTextArea extends React.Component{
constructor(props) {
super(props);
this.getCursorPosition = this.getCursorPosition.bind(this);
}
getCursorPosition(){
return this.refs.textarea.selectionStart;
}
render(){
return <div>
<textarea ref="textarea"/>
</div>
}
}
2º Add a ref to the StoryTextArea component
<StoryTextarea ref="storyTextArea" text={this.props.text} />
3º Call getCursorPosition using this.refs.storyTextArea.getCursorPosition()
insertToStory = (word) => {
let position = this.refs.storyTextArea.getCursorPosition();
}
jsfiddle example