onMouseMove doesnot work outside of element react - reactjs

I have an svg element on which I am doing onMouseDown, OnMouseMove and onMouseUp. My problem is that as soon as the user leaves the element while still holding their mouse button down, it does not register the mousemove.
I want to keep the onMouseMove event on even after user leaves the element.
Here is my code:
Class School extents React.Component {
onDragStartCircle = (e) {
//taking the initial state
}
onDragCircle = () {
// draging the element
}
onDragEndCircle = () {
// saving data to the database
}
render() {
return (
<div>
<svg>
<circle
cx={50}
cy={50}
r={10}
fill="red"
onMouseDown={this.onDragStartCircle}
onMouseMove={this.onDragCircle}
onMouseUp={this.onDragEndCircle}
/>
</svg>
</div>
);
}
}
I have also tried onDragStart, onDrag these are not working. I am using es6.

Here is an example of your code, whichs shows how to use a container to register events outside of that circle.
You should consider subscribing to the move event on drag start, and unsubscribing again on drag end, to prevent to much events firing. But this should get you started.
class School extends React.Component {
onDragStartCircle = (e) => {
console.log('drag start')
}
onDragCircle = () => {
console.log('move')
}
onDragEndCircle = () => {
console.log('drag end')
}
render() {
return (
<div class="container"
onMouseMove={this.onDragCircle}
onMouseUp={this.onDragEndCircle}>
<svg>
<circle
cx={50}
cy={50}
r={10}
fill="red"
onMouseDown={this.onDragStartCircle}
/>
</svg>
</div>
);
}
}
ReactDOM.render(
<School />,
document.getElementById("react")
);
.container {
width: 100%;
height: 100%;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Related

Change element content with onClick in React

In my application I have multiple blocks generated dynamically and each one of them has an onClick event.
My goal is to be able to change the contents of the div when the click happens.
Is there a way to do this thru event.target property of the onClick event?
Or should i create a ref for each div upon creation and then work with refs?
Or should i create an array of Div elements in component state and search&modify the element later re-rendering all divs from array?
Since blocks are generating dynamically, have onClick event on children components.
const Parent = () => {
return (
<Child content={content} contentAfterClick={content} />
<Child content={content} contentAfterClick={content} />
)
}
class Child extends Component {
constructor() {
super();
this.state ={
read: false,
};
}
render() {
if (this.state.read) {
return(
<div>{this.props.contentAfterClick}</div>
)
}
return (
<div onClick={() => this.setState({ read: true })}>
<div>{this.props.content}</div>
</div>
);
};
}
This demo illustrates how you can change the contents of a div, the text, when a click happens through the onClick and event.target object as you wanted.
You can do this through the use of refs, but normally you want to avoid refs unless absolutely necessary because there are easier ways to accomplish the same thing in React.
Also wouldn't want to keep the physical DOM nodes, HTMLDivElement, in state. Instead, keep the contents it relies upon in state (in our case a single number value), then when you change the contents it will automatically update and rerender our div nodes.
// Example class component
class Container extends React.Component {
constructor(props) {
super(props);
const blocks = [];
blocks.push(0);
blocks.push(0);
blocks.push(0);
this.state = { blocks: blocks, clickedElementContents: "" };
}
increment(event, index) {
const newBlocks = this.state.blocks;
newBlocks[index]++;
this.setState({ blocks: newBlocks, clickedElementContents: event.target.innerText });
}
render() {
return (
<div>
<div className="block" onClick={(event) => { this.increment(event, 0) }}>Click me! ({this.state.blocks[0]})</div>
<div className="block" onClick={(event) => { this.increment(event, 1) }}>Click me! ({this.state.blocks[1]})</div>
<div className="block" onClick={(event) => { this.increment(event, 2) }}>Click me! ({this.state.blocks[2]})</div>
<span>Contents of the clicked element: {this.state.clickedElementContents}</span>
</div>
);
}
}
// Render it
ReactDOM.render(
<Container/>,
document.body
);
.block {
display: inline-block;
background-color: black;
color: white;
padding: 5px;
margin-right: 10px;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Not Able to Toggle Class Using React Componnent

Can you please take a look at this demo and let me know why I am not able to toggle .green class for #root using onClick in react js?
function toggler(e){
var x = document.getElementById("root");
x.classList.toggle('green');
}
const Button = ({ styleClass, onClick, text }) => {
return (
<button
type="button"
onClick={e => onClick(e)}
className={`btn ${styleClass}`}
>
{text}
</button>
);
};
ReactDOM.render(
<div>
<Button styleClass="btn-primary" text='Primary Button' onClick={toggler} />
</div>
, window.root);
#root{
height:300px;
width:300px;
background:khaki;
}
.green{
background:green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<div id="root"></div>
You should not touch the DOM directly when you're writing React components. React can help you manage your class name with state.
Do something like this:
import React, { Component } from 'react';
export default class Button extends Component {
constructor(props) {
super(props);
this.state = {
buttonStyleClass: 'bar'
}
}
toggler = (e) => {
this.setState({
buttonStyleClass: 'foo'
})
}
render() {
return (
<div
className={this.state.buttonStyleClass}
onClick={this.toggler}
>
Click me
</div>
);
}
}
The problem here is that id-selectors have higher priority over class-selectors in css. Since you have defined the base color with #root, you can't toggle it with just .green.
Many solutions here, but one of them could be #root.green, adding !important or selecting your root otherwise.
That being said, you should not mutate the DOM directly when using React. It voids one of its biggest advantages. See mxdi9i7's answer for more info.

React tabIndex onBlur don't hide element

I have a sidebar element that uses tabIndex and onBlur to control visibility, so when the user selects anything outside of the sidebar it automatically hides.
This works well, but I need to add a drop-down menu to the sidebar which then gets focus and causes the sidebar to collapse (before a user could select something).
state = {
visible: true
}
componentDidMount () {
this.focusSidebar()
}
componentDidUpdate () {
if (this.state.visible) this.focusSidebar()
}
focusSidebar () {
ReactDOM.findDOMNode(this.refs.sidebarRegion).focus()
}
hideSidebar () => {
this.setState({ visible: false })
}
render () {
return (
<div
onBlur={this.hideSidebar}
tabIndex='0'
className={`sidebar ${this.state.visible ? '' : 'hidden'}`}
ref='sidebarRegion'
>
<select>
<option>Foo</option>
</select>
</div>
)
}
I'm not seeing a good way to handle this with my current implementation of the sidebar, but I'm trying to find a way to self-contain the sidebar element without needing to hoist the visible/hidden state outside of the component.
You can use document.activeElement to achieve what you want. I will not add more details, as it was explained here. You can also take a look at this gist.
Here it is demonstrated with your code, I didn't add css, but a console log to tell you when it should hide:
class Hello extends React.Component {
state = {
visible: true
}
componentDidMount () {
this.focusSidebar()
}
componentDidUpdate () {
if (this.state.visible) this.focusSidebar()
}
focusSidebar () {
ReactDOM.findDOMNode(this.refs.sidebarRegion).focus()
}
hideSidebar(e) {
var currentTarget = e.currentTarget;
setTimeout(()=>{
if (!currentTarget.contains(document.activeElement)) {
this.setState({ visible: false })
console.log("Hiding the sidebar!");
}
}, 0);
}
render () {
return (
<div
onBlur={this.hideSidebar.bind(this)}
tabIndex='0'
className={`sidebar ${this.state.visible ? '' : 'hidden'}`}
ref='sidebarRegion'
>
<select>
<option>Foo</option>
</select>
</div>
)
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

Re-using React Components

I've been doing React lessons, but one thing I don't ever see done is reusing components. For example, if I had a button, and wanted to produce a div every time that button was clicked. How would I do it using a React component that's sole purpose is rendering a single div, And that button uses that one component to add additional divs to the page every time it's clicked?
Do you mean, something like this?
The CustomButton stateless component (presentational) just receives props and can be disabled, text can be changed and a callback can be defined. It doesn't have any own state and can be reused throughout your app where you might need a button.
The ButtonSampleApp is a container component that uses the presentional component and supplies it with a callback, and then handles that callback. To add a div in it's rendering. The ButtonSampleApp uses component state to achieve this
const CustomButton = ({ text, callback, isEnabled }) => {
return <button onClick={() => callback()} disabled={!isEnabled} type="button">{ text }</button>;
};
class ButtonSampleApp extends React.Component {
constructor() {
super();
this.state = {
divs: []
};
}
onButtonClicked() {
const { divs } = this.state;
this.setState( { divs: [...divs, { text: divs.length }] });
}
render() {
const { max } = this.props;
const { divs } = this.state;
return (<div>
<h1>Click on button to add a max of { max } divs</h1>
<div>
{ divs && divs.map( ({text}) => <div key={text}>{ text }</div> ) }
</div>
<CustomButton isEnabled={!divs || divs.length < max} text="Add button" callback={() => this.onButtonClicked()} />
</div>);
}
}
ReactDOM.render( <ButtonSampleApp max={10} />, document.querySelector('#container') );
<script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<div id="container"></div>

In React, onMouseEnter or hover is not working as expected

I have one image with opacity = 1 at the beginning.
When mouse enters the image, change opacity = 0.5. When mouse leaves the image, change the opacity back.
here is one code:
mouseEnter() {
console.log('mouse enter')
const classname = '.' + this.props.post.code
document.querySelector(classname).classList.add('image-hover-opacity')
}
mouseLeave() {
console.log('mouse leave')
const classname = '.' + this.props.post.code
document.querySelector(classname).classList.remove('image-hover-opacity')
}
render() {
<img src={src} onMouseEnter={::this.mouseEnter} onMouseLeave={::this.mouseLeave} />
}
onMouseEnter and onMouseLeave are fired when mouse enters and leaves the image, respectively, good. But the problem is when I move the mouse inside the image, both onMouseEnter and onMouseLeave are fired.
And I have tried css solution as well, when I hover on image, change the opacity property. But the problem is the same: when I move mouse inside the image, :hover and not hover are fired multiple times.
How to solve this? thanks
UPDATE
There is something in my previous code. Created one jsfiddle, and it works.
sorry guys
Using document.querySelector is not a very React way of thinking. You can try this approach:
Use a div wrapping this img to avoid this weird mouseEnter behavior
Use this.state with opacity
constructor() {
this.state = {
opacity: 1
}
}
mouseEnter() {
console.log('mouse enter')
this.setState({opacity: 0.5})
}
mouseLeave() {
console.log('mouse leave')
this.setState({opacity: 1})
}
render() {
<div style={{opacity: this.state.opacity}}>
<img src={src} onMouseEnter={::this.mouseEnter} onMouseLeave={::this.mouseLeave} />
</div>
}
I really think you can achieve this in CSS only.
So your component should have simple className property and that class should have the definitions for:
.image-hover-opacity:hover {
opacity: 0.5;
}
class Example extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
return(
<img className="image-hover-opacity" src="http://i.imgur.com/PLKabDV.png" />
);
}
}
ReactDOM.render(<Example />, document.getElementById('root'));
.image-hover-opacity:hover {
opacity: 0.5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Resources