Get cursor position in a sibling component - javascript

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

Related

Print multiple Instance of one class using another class

I'm trying to make a board game(Ludo) that will need 13*13 boxes. I have one box class that print out one square button and rendering it works fine. Code below:
class Box extends React.Component{
render(){
return(
<button className="square">
</button>
);
}
}
Problem is when I try to print multiple Box with the Board class. Apparently this code does not work. I can't figure out why. Any insight will be helpful.
class Board extends React.Component{
render(){
return(
{this.renderRow}
);
}
renderRow(){
for(let i= 0; i < 13; i++){
return(
<Box />
);
}
}
}
It seems like even the box is not working. Only when I comment out Board class the Box class works.
Changing from {this.renderRow} to (this.renderRow) inside Board:render solved the issue where even Box class won't render(as mentioned in update 1). new code is:
class Board extends React.Component{
render(){
return(
(this.renderRow)
);
}
renderRow(){
return(
<Box />
);
}
}
Few problems here. Currently nothing invokes renderRow method + it returns single item instead of array (row). To fix single row rendering you can:
class Board extends React.Component{
render(){
return this.renderRow();
}
renderRow() {
return Array.from({ length: 13 }, (_, i) => <Box key={i} />)
}
}
Now we create an array with 13 boxes.
Try on codesandbox
this.renderRow(). You're forgetting about ().
Also you'll find that react will start giving you errors n the console, due to lack of the key attribute on the Box

referring to a div from outside class

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

Place <div> at x,y coordinate in React

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!!!
}

How to return an HTML <div> tag from a javascript function in React?

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

Bind click to a div and get attribute data in React

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>
);
}
}

Categories