(Resolved )Best way to remove Draggable React component from flow without jumping? - javascript

I have Note components that are rendered using notes.map(), each with position: static. The notes are draggable using react-draggable npm module.
The functionality I'm trying to achieve is when a note is deleted, to not affect the position of notes that have been dragged by the user. I've attempted to set position: absolute for notes that have been dragged. However, this causes the note to 'jump' in position once (happens when removed from flow).
-- Initial State:
-- After first drag attempt, test note jumps on top of other note:
-- Able to drag normally after first attempt:
I've included relevant code for Notes.jsx component:
function Note(props) {
const [dragDisabled, setDragDisabled] = useState(true);
const [beenDragged, setBeenDragged] = useState(props.beenDragged);
const [position, setPosition] = useState({ xPos: props.xPos, yPos: props.yPos });
// Drag Note Functions
function handleClick() {
setDragDisabled(prevValue => {
return !prevValue;
})
}
function firstDrag(event) {
if (!beenDragged) {
axios.post("api/note/beenDragged", {id: props.id})
.then(setBeenDragged(true));
}
}
function finishDrag(event, data) {
setPosition({ xPos: data.x, yPos: data.y });
}
useEffect(() => {
axios.post("/api/note/updateposition", {position, id: props.id });
}, [position]);
return <Draggable
disabled={dragDisabled}
onStart={firstDrag}
onStop={finishDrag}
defaultPosition={{ x: props.xPos, y: props.yPos }}
// position={location}
>
<div className='note' style={{position: beenDragged ? 'absolute' : 'static'}}>
<h1>{props.title}</h1>
<p>{props.content}</p>
<button onClick={handleClick}>
{dragDisabled ? <LockIcon /> : <LockOpenIcon />}
</button>
<EditPopup title={props.title} content={props.content} editNote={editNote} />
<DeletePopup deleteNote={deleteNote} />
</div>
</Draggable>
}
and for my CSS styling
.note {
background: #fff;
/* background-image: url("https://www.transparenttextures.com/patterns/notebook-dark.png"); */
background-image: url("https://www.transparenttextures.com/patterns/lined-paper-2.png");
border-radius: 7px;
box-shadow: 0 2px 5px rgb(120, 150, 179);
padding: 10px;
width: 240px;
margin: 16px;
float: left;
}
Relevant code for how App.jsx renders the notes:
function App() {
const [notes, setNotes] = useState([]);
return (
<div id="bootstrap-override">
{notes.map((note) => {
return <Note
key={note._id}
id={note._id}
title={note.title}
content={note.content}
xPos={note.xPos}
yPos={note.yPos}
beenDragged={note.beenDragged}
deleteNote={deleteNote}
editNote={editNote}
/>
})}
</div>);
}
Any help is much appreciated, thank you!

I solved the issue not directly, but rather using a different approach to get the result I was looking for.
To summarize, I used position: absolute for all notes, and created them at random x,y coordinates within a boundary. This way deleted notes would not affect the position of existing notes.
Hope this helps anyone with similar issues!

When your draggables are position: static, you'll experience that screen jank when removing an element because the order of the elements in the document help define where your draggables live, regardless of their dragged state (which boils down to something like transform: translate(100px, 150px);.
The translate is like saying: "go over 100px and up 100px from wherever you live. When we delete the first element, everybody shifts. They can still translate(100px, 150px); but the point from which they originate will have changed.
Someone mentioned setting the draggable's to position: absolute and the parent as position: relative. That works, but might introduce headaches of it's own.
I will offer an innocent, perhaps naive, solution: hiding the element. A quick and dirty way: instead of removing the element from the DOM, you can set the element's visibility: none. You can even delete the item in the backend and offer a "restore note" feature in the UI since the draggable will still contain content.
And whenever you're hiding things, make sure you do so with accessibility in mind. Here's a good article on that.
I also made a derpy screencast: https://www.youtube.com/watch?v=Fr0PfT3Frzk&ab_channel=SamKendrick. It goes a little fast, but I begin by deleting the first element to illustrate your problem. Then I undo the delete and instead attach a visibility: hidden style to the element I want to delete.

Related

How to use IntersectionObserver API to make navBar sticky

I am making the clone of a webpage which is made in JS but I am developing it by HTML, CSS, JS. Its navBar looks like this . Here is the link if you want to experience yourself link.
So, I have tried to implement this using IntersectionObserver API as well as by using window.addEventListener(). I don't want to implement this by using scroll event Listener because it is too heavy for end user.
const intersectionCB = ([entry]) => {
const elem = entry.target;
if (!entry.isIntersecting) {
elem.classList.add('nav__2-sticky');
// observer.unobserve(navBar);
} else {
elem.classList.remove('nav__2-sticky');
}
};
const observer = new IntersectionObserver(intersectionCB, {
root: null,
threshold: 0
});
observer.observe(navBar);
In HTML file
<div class="nav__2">
<div class="row nav__2--content">
<div class="logo-container">
<img src="img/logo-black.png" alt="" class="logo" />
</div>
........
In SCSS file
.nav {
&__2 {
top: 8rem;
position: absolute;
left: 0;
width: 100%;
&-sticky {
position: fixed;
top: 0;
}
}
}
You might understand what is happening. When navBar gets out of the view, (navBar is positioned at 8rem from top!). I append nav__2-sticky class (which is positioned fixed at 0 from top) to appear on the screen. Due to which entry.isIntersecting becomes true and elem.classList.remove('nav__2-sticky'); is executed. As a result navBar again gets out of the view and again elem.classList.add('nav__2-sticky') is executed. This cycle of adding and removing classes due to entry.isIntersecting becoming True and False is creating a problem for me. This happens in such speed that it shows abnormal behaviour.
So, is there any proper solution for this? I would also like to hear other solutions that might work.
I used scroll event after all. Here is the code, I think I don't need to explain. You will get more detailed explanation here link
const initialCords = navBar.getBoundingClientRect();
document.addEventListener('scroll', () => {
if (window.scrollY > initialCords.top) {
navBar.classList.add('nav__2-sticky');
} else {
navBar.classList.remove('nav__2-sticky');
}
});
Another angle could be to run the intersection observer on an element that is out of view (below the bottom of the screen) and not only the navbar itself

Making two Component same height when one is a array map

I am learning React and Redux and now I have this problem.
Here's a codesandbox
I want this two Component to be side by side and always have the same height even if Component2 grow larger since it's a map.
Try like this:
Search for book title "dep"
Watch the log grow pushing down the screen
Here's an image showing the Component2 getting larger then Component1 and I don't want that I have added style={{overflowY:"scroll"} to Component2 but don't understand why it pushes down anyway.
I tried using FlexBox but it's not working:
.row {
display: flex; /* equal height of the children */
}
.col {
flex: 1; /* additionally, equal width */
padding: 1em;
border: solid;
}
I don't want to set a fixed height.
looks like you need to do more CSS here.
Try to add a max-height: 300px in that component that have the overflow-y.
I've managed to solve this without setting height manually.
So, I've added a ref for the form.
this.formRef = React.createRef();
Used ref into the form tag.
<form ref={this.formRef}
And added an extra property to the state:
formHeight: "200px"
At componentDidMount hook I've assigned the height of the form into the state.
componentDidMount() {
this.setState(
(prevState) => ({
localBook: {
...prevState.localBook
},
formHeight:
this.formRef.current.parentElement.clientHeight.toString() + "px"
}),
() => {}
);
}
At your logger component I've added another style from props.
<div style={{ overflowY: "scroll", maxHeight: props.maxHeight }}>
And passed the prop from the parent:
<EnhancedTable maxHeight={this.state.formHeight} />
Demo at CodeSandbox.
Result:

React animation for moving an element from one parent to another

I am trying to create an animation for moving a child element from one parent element to another using React.
A user should be able to click on an element and see it move into another div.
I made a simple demo component (without the animation) to show what I mean. When an element is clicked, the state updates and the elements are re-rendered in the correct place.
class App extends React.Component {
state = {
list: ['Alice', 'Bob', 'Charlie', 'David', 'Emily', 'Frank'],
top: [0, 1, 2],
bottom: [3, 4, 5]
}
moveDown = (item) => {
let { top, bottom } = this.state
this.setState({
top: top.filter(x => x !== item),
bottom: [...bottom, item]
})
}
moveUp = (item) => {
let { top, bottom } = this.state
this.setState({
top: [...top, item],
bottom: bottom.filter(x => x !== item)
})
}
render() {
let { top, bottom, list } = this.state
return (
<div style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'center',
height: '90vh',
width: '100%'
}}>
<div>
{top.map((item) =>
<div
onClick={() => this.moveDown(item)}
style={{color:'red'}}>{list[item]}</div>
)}
</div>
<div>
{bottom.map((item) =>
<div
onClick={() => this.moveUp(item)}
style={{color:'green'}}>{list[item]}</div>
)}
</div>
</div>
)
}
}
Codepen demo: https://codepen.io/ee92/pen/LqrBjL?editors=0010
Big appreciation to and thanks in advance for any help or advice on how to achieve this div-to-div animation.
No it's not possible
It's not possible to animate in that way because the DOM thinks you're removing a div and then adding a new div. Even though it's the same div to you, the DOM doesn't have that context. Animations are controlled by changes to CSS, not HTML.
...but here's how to do it
If you actually need both lists to stay in different divs the best you can do is either:
Animate the old item to the new item position, then delete the old item and show the new item.
Remove the old item and create a new item where the old item was and move it to the new item position.
Same concept, two ways of doing it.
I modified your existing sample to show a simplified version of option 2. Note that there are a number of animation decisions to make like what happens when the list gets smaller, how should the items change from red to green, etc., and I didn't try and objectively solve them. Also, this would be much easier if you could have all the items for both lists in one div, and control their positions absolutely. But if they need to end up in separate divs...
https://codepen.io/sallf/pen/VgBwQr?editors=0010
What's going on
Adding a transition to .item we can make the animation happen when we make adjustments to the transform property.
On item click we update our lists in state and add...
transition.item to know which item is animating...
transition.startTop to know the offset y position the item should start at relative to the bottom of the list it's moving to, and...
transition.startAnim as a flag to control the animation.
Since transitions need something to change before they'll animate, we use setTimeout to delay the change of transition.startAnim which basically causes the animation from the computed position, back to 0.

Monaco editor dynamically resizable

I have been searching for a discussion about if it's possible to mimic the html tag textarea's resizing when using Monaco Editor's field all over the Internet but I couldn't find one answering my question.
I'm using the monaco-editor npm package in a React application. Do you have any idea if this is easy to implement?
Thank you in advance!
SOLUTION
With pure css I selected the target html element and just added these properties:
div {
resize: vertical;
overflow: auto;
}
TL;DR: add automaticLayout: true to your editor's configuration.
NL;PR:
Monaco has a built-in auto resize to parent container functionality:
createEditorWithAutoResize(){
this.editor = monaco.editor.create(
this.editorDiv.current, {
value: "var x = 0;",
language: 'javascript',
automaticLayout: true // <<== the important part
}
);
}
componentDidMount(){this.createEditorWithAutoResize();}
constructor(props){super(props); this.editorDiv = React.createRef();}
render(){return <div ref={this.editorDiv} className="editor" ></div>}
And the CSS for the editor (it avoids rendering the editor for the first time with like 10px height):
.editor{
height: 100%;
}
First tested: v0.10.1, Last tested: v0.32.1
Note:
< v0.20.0: The mechanism does not listen to its container size changes, it polls them.
#nrayburn-tech (Monaco Editor's contributor): Version 0.20 uses MutationObserver for all browsers. Version 0.21 and later uses ResizeObserver on supported browsers, otherwise, it uses polling as a fallback.
if you have a reference to the editor you can just call
editor.layout()
on some resize event.
For example, on window resize:
window.onresize = function (){
editor.layout();
};
For anyone coming here having this issue in a basic web app (html, css, javascript) I've found a solution for the resizing issue I'm experiencing.
I have the monaco editor in a resizable flex container. It will only grow the width, not shrink it, and vertical resizing doesn't seem to work out of the box.
If you use the monaco config "automaticLayout: true" and the following CSS it seems to resize as expected:
.monaco-editor { position: absolute !important; }
I tried the max-width 99% trick but it causes a laggy delayed effect when increasing the width near edge of page.
For posterity, the solution I arrived on was to set automaticLayout: false so that I could perform all the layout in a resize event listener.
const placeholder = document.getElementById('placeholder')
const editor = monaco.editor.create(placeholder, {
value: '// hello world',
language: 'javascript',
automaticLayout: false // or remove, it defaults to false
})
// we need the parent of the editor
const parent = placeholder.parentElement
window.addEventListener('resize', () => {
// make editor as small as possible
editor.layout({ width: 0, height: 0 })
// wait for next frame to ensure last layout finished
window.requestAnimationFrame(() => {
// get the parent dimensions and re-layout the editor
const rect = parent.getBoundingClientRect()
editor.layout({ width: rect.width, height: rect.height })
})
})
By first reducing the editor layout to 0 we can safely query the dimensions of the parent element without the child (editor) contributing to its size. We can then match the editor to the new parent dimensions. Since this takes place over a single frame, there should be no flickering or lag.
this is old question but get the problem to and solved it with react-resize-detector
based on ResizeObserver it feet perfectly to the need (check browser compatibility)
Exemple of component :
import React, { Component } from 'react';
import ReactResizeDetector from 'react-resize-detector';
import * as monaco from 'monaco-editor';
class Editor extends Component {
constructor(props) {
super(props)
this.state = {
width: 0,
height: 0,
}
this.editor_div = React.createRef()
this.handle_rezise = this.handle_rezise.bind(this);
}
componentDidMount() {
const editor_model = monaco.editor.createModel('', 'sql');
this.monaco_editor = monaco.editor.create(this.editor_div.current, this.props.editorOptions);
this.monaco_editor.setModel(editor_model);
}
componentWillUnmount() {
this.monaco_editor && this.monaco_editor.dispose();
}
handle_rezise(width, height) {
this.monaco_editor.layout({ height, width });
}
render() {
return(
<div
className="editor-container"
style={{ height: '100%' }}>
<ReactResizeDetector
handleWidth
handleHeight
onResize={ this.handle_rezise }
refreshMode="debounce"
refreshRate={100} />
<div
className="editor"
ref={ this.editor_div }
style={{ height: '100%' }} />
</div>
)
}
}
export default Editor;
Hope it's help
In my case I'm using that exact CSS but although automaticLayout: true works, I found out overkill (seems to pooling the DOM 100ms interval and I have several editors opened in the document. SO I ended up implementing it manually :
just in case , my needs are different: I want the user to resize it the container - in a standard way and cheap (both on code and performance) on libraries and performance. This is what I did:
css container : resize: vertical; overflow: auto
and this js :
function installResizeWatcher(el, fn, interval){
let offset = {width: el.offsetWidth, height: el.offsetHeight}
setInterval(()=>{
let newOffset = {width: el.offsetWidth, height: el.offsetHeight}
if(offset.height!=newOffset.height||offset.width!=newOffset.width){
offset = newOffset
fn()
}
}, interval)
}
const typeScriptCodeContainer = document.getElementById('typeScriptCodeContainer')
typeScriptCodeEditor = monaco.editor.create(typeScriptCodeContainer, Object.assign(editorOptions, {value: example.codeValue}))
installResizeWatcher(typeScriptCodeContainer, typeScriptCodeEditor.layout.bind(typeScriptCodeEditor), 2000)
yes, 2 seconds interval and make sure it registers only once. I see there is / was a resize interval on 100ms for the automatic relayout in monaco - IMHO that's too much.
See it in action: https://typescript-api-playground.glitch.me/?example=2

How do you Hover in ReactJS? - onMouseLeave not registered during fast hover over

How can you achieve either a hover event or active event in ReactJS when you do inline styling?
I've found that the onMouseEnter, onMouseLeave approach is buggy, so hoping there is another way to do it.
Specifically, if you mouse over a component very quickly, only the onMouseEnter event is registered. The onMouseLeave never fires, and thus can't update state... leaving the component to appear as if it still is being hovered over. I've noticed the same thing if you try and mimic the ":active" css pseudo-class. If you click really fast, only the onMouseDown event will register. The onMouseUp event will be ignored... leaving the component appearing active.
Here is a JSFiddle showing the problem: https://jsfiddle.net/y9swecyu/5/
Video of JSFiddle with problem: https://vid.me/ZJEO
The code:
var Hover = React.createClass({
getInitialState: function() {
return {
hover: false
};
},
onMouseEnterHandler: function() {
this.setState({
hover: true
});
console.log('enter');
},
onMouseLeaveHandler: function() {
this.setState({
hover: false
});
console.log('leave');
},
render: function() {
var inner = normal;
if(this.state.hover) {
inner = hover;
}
return (
<div style={outer}>
<div style={inner}
onMouseEnter={this.onMouseEnterHandler}
onMouseLeave={this.onMouseLeaveHandler} >
{this.props.children}
</div>
</div>
);
}
});
var outer = {
height: '120px',
width: '200px',
margin: '100px',
backgroundColor: 'green',
cursor: 'pointer',
position: 'relative'
}
var normal = {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'red',
opacity: 0
}
var hover = {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'red',
opacity: 1
}
React.render(
<Hover></Hover>,
document.getElementById('container')
)
Have you tried any of these?
onMouseDown onMouseEnter onMouseLeave
onMouseMove onMouseOut onMouseOver onMouseUp
SyntheticEvent
it also mentions the following:
React normalizes events so that they have consistent properties across different browsers.
The event handlers below are triggered by an event in the bubbling phase. To register an event handler for the capture phase, append Capture to the event name; for example, instead of using onClick, you would use onClickCapture to handle the click event in the capture phase.
The previous answers are pretty confusing. You don't need a react-state to solve this, nor any special external lib. It can be achieved with pure css/sass:
The style:
.hover {
position: relative;
&:hover &__no-hover {
opacity: 0;
}
&:hover &__hover {
opacity: 1;
}
&__hover {
position: absolute;
top: 0;
opacity: 0;
}
&__no-hover {
opacity: 1;
}
}
The React-Component
A simple Hover Pure-Rendering-Function:
const Hover = ({ onHover, children }) => (
<div className="hover">
<div className="hover__no-hover">{children}</div>
<div className="hover__hover">{onHover}</div>
</div>
)
Usage
Then use it like this:
<Hover onHover={<div> Show this on hover </div>}>
<div> Show on no hover </div>
</Hover>
you can use onMouseOver={this.onToggleOpen} and onMouseOut={this.onToggleOpen} to muse over and out on component
Note: This answer was for a previous version of this question where the question asker was trying to use JavaScript to apply css styles… which can simply be done with CSS.
A simple css-only solution.
For applying basic styles, CSS is simpler and more performant that JS solutions 99% of the time. (Though more modern CSS-in-JS solutions — eg. React Components, etc — are arguably more maintainable.)
Run this code snippet to see it in action…
.hover-button .hover-button--on,
.hover-button:hover .hover-button--off {
display: none;
}
.hover-button:hover .hover-button--on {
display: inline;
}
<button class='hover-button'>
<span class='hover-button--off'>Default</span>
<span class='hover-button--on'>Hover!</span>
</button>
If you can produce a small demo showing the onMouseEnter / onMouseLeave or onMouseDown / onMouseUp bug, it would be worthwhile to post it to ReactJS's issues page or mailing list, just to raise the question and hear what the developers have to say about it.
In your use case, you seem to imply that CSS :hover and :active states would be enough for your purposes, so I suggest you use them. CSS is orders of magnitude faster and more reliable than Javascript, because it's directly implemented in the browser.
However, :hover and :active states cannot be specified in inline styles. What you can do is assign an ID or a class name to your elements and write your styles either in a stylesheet, if they are somewhat constant in your application, or in a dynamically generated <style> tag.
Here's an example of the latter technique: https://jsfiddle.net/ors1vos9/
I've just bumped into this same problem when listening for onMouseLeave events on a disabled button. I worked around it by listening for the native mouseleave event on an element that wraps the disabled button.
componentDidMount() {
this.watchForNativeMouseLeave();
},
componentDidUpdate() {
this.watchForNativeMouseLeave();
},
// onMouseLeave doesn't work well on disabled elements
// https://github.com/facebook/react/issues/4251
watchForNativeMouseLeave() {
this.refs.hoverElement.addEventListener('mouseleave', () => {
if (this.props.disabled) {
this.handleMouseOut();
}
});
},
render() {
return (
<span ref='hoverElement'
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
<button disabled={this.props.disabled}>Submit</button>
</span>
);
}
Here's a fiddle https://jsfiddle.net/qfLzkz5x/8/
I'd use onMouseOver & onMouseOut. Cause in React:
The onMouseEnter and onMouseLeave events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase.
Here it is in the React documentation for mouse events.
A package called styled-components can solve this problem in an ELEGANT way.
Reference
Glen Maddern - Styling React Apps with Styled Components
Example
const styled = styled.default
const Square = styled.div`
height: 120px;
width: 200px;
margin: 100px;
background-color: green;
cursor: pointer;
position: relative;
&:hover {
background-color: red;
};
`
class Application extends React.Component {
render() {
return (
<Square>
</Square>
)
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id='app'></div>
You can't with inline styling alone. Do not recommend reimplementing CSS features in JavaScript we already have a language that is extremely powerful and incredibly fast built for this use case -- CSS. So use it! Made Style It to assist.
npm install style-it --save
Functional Syntax (JSFIDDLE)
import React from 'react';
import Style from 'style-it';
class Intro extends React.Component {
render() {
return Style.it(`
.intro:hover {
color: red;
}
`,
<p className="intro">CSS-in-JS made simple -- just Style It.</p>
);
}
}
export default Intro;
JSX Syntax (JSFIDDLE)
import React from 'react';
import Style from 'style-it';
class Intro extends React.Component {
render() {
return (
<Style>
{`
.intro:hover {
color: red;
}
`}
<p className="intro">CSS-in-JS made simple -- just Style It.</p>
</Style>
}
}
export default Intro;
Use Radium!
The following is an example from their website:
var Radium = require('radium');
var React = require('react');
var color = require('color');
#Radium
class Button extends React.Component {
static propTypes = {
kind: React.PropTypes.oneOf(['primary', 'warning']).isRequired
};
render() {
// Radium extends the style attribute to accept an array. It will merge
// the styles in order. We use this feature here to apply the primary
// or warning styles depending on the value of the `kind` prop. Since its
// all just JavaScript, you can use whatever logic you want to decide which
// styles are applied (props, state, context, etc).
return (
<button
style={[
styles.base,
styles[this.props.kind]
]}>
{this.props.children}
</button>
);
}
}
// You can create your style objects dynamically or share them for
// every instance of the component.
var styles = {
base: {
color: '#fff',
// Adding interactive state couldn't be easier! Add a special key to your
// style object (:hover, :focus, :active, or #media) with the additional rules.
':hover': {
background: color('#0074d9').lighten(0.2).hexString()
}
},
primary: {
background: '#0074D9'
},
warning: {
background: '#FF4136'
}
};
I had a similar issue when onMouseEnter was called but sometimes the corresponding onMouseLeave event wasn't fired, here is a workaround that works well for me (it partially relies on jQuery):
var Hover = React.createClass({
getInitialState: function() {
return {
hover: false
};
},
onMouseEnterHandler: function(e) {
this.setState({
hover: true
});
console.log('enter');
$(e.currentTarget).one("mouseleave", function (e) {
this.onMouseLeaveHandler();
}.bind(this));
},
onMouseLeaveHandler: function() {
this.setState({
hover: false
});
console.log('leave');
},
render: function() {
var inner = normal;
if(this.state.hover) {
inner = hover;
}
return (
<div style={outer}>
<div style={inner}
onMouseEnter={this.onMouseEnterHandler} >
{this.props.children}
</div>
</div>
);
}
});
See on jsfiddle: http://jsfiddle.net/qtbr5cg6/1/
Why was it happening (in my case): I am running a jQuery scrolling animation (through $('#item').animate({ scrollTop: 0 })) when clicking on the item. So the cursor doesn't leave the item "naturally", but during a the JavaScript-driven animation ... and in this case the onMouseLeave was not fired properly by React (React 15.3.0, Chrome 51, Desktop)
I know It's been a while since this question was asked but I just run into the same issue of inconsistency with onMouseLeave()
What I did is to use onMouseOut() for the drop-list and on mouse leave for the whole menu, it is reliable and works every time I've tested it.
I saw the events here in the docs: https://facebook.github.io/react/docs/events.html#mouse-events
here is an example using https://www.w3schools.com/bootstrap/bootstrap_dropdowns.asp:
handleHoverOff(event){
//do what ever, for example I use it to collapse the dropdown
let collapsing = true;
this.setState({dropDownCollapsed : collapsing });
}
render{
return(
<div class="dropdown" onMouseLeave={this.handleHoverOff.bind(this)}>
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Dropdown Example
<span class="caret"></span></button>
<ul class="dropdown-menu" onMouseOut={this.handleHoverOff.bind(this)}>
<li>bla bla 1</li>
<li>bla bla 2</li>
<li>bla bla 3</li>
</ul>
</div>
)
}
I personally use Style It for inline-style in React or keep my style separately in a CSS or SASS file...
But if you are really interested doing it inline, look at the library, I share some of the usages below:
In the component:
import React from 'react';
import Style from 'style-it';
class Intro extends React.Component {
render() {
return (
<Style>
{`
.intro {
font-size: 40px;
}
`}
<p className="intro">CSS-in-JS made simple -- just Style It.</p>
</Style>
);
}
}
export default Intro;
Output:
<p class="intro _scoped-1">
<style type="text/css">
._scoped-1.intro {
font-size: 40px;
}
</style>
CSS-in-JS made simple -- just Style It.
</p>
Also you can use JavaScript variables with hover in your CSS as below :
import React from 'react';
import Style from 'style-it';
class Intro extends React.Component {
render() {
const fontSize = 13;
return Style.it(`
.intro {
font-size: ${ fontSize }px; // ES2015 & ES6 Template Literal string interpolation
}
.package {
color: blue;
}
.package:hover {
color: aqua;
}
`,
<p className="intro">CSS-in-JS made simple -- just Style It.</p>
);
}
}
export default Intro;
And the result as below:
<p class="intro _scoped-1">
<style type="text/css">
._scoped-1.intro {
font-size: 13px;
}
._scoped-1 .package {
color: blue;
}
._scoped-1 .package:hover {
color: aqua;
}
</style>
CSS-in-JS made simple -- just Style It.
</p>
Hover is a CSS feature; it comes with the CSS side of the app so do it the right way is so much fun. Simply put, when I require a hover effect on an element in the React app, first, I create a class in my CSS file and then I add the created class into the className of the element. The steps I follow are:
Create a CSS file unless you have index.css
Create a class with a hover pseudo-class enabled
.hover__effect:hover {}
Add the effects you require
.hover__effect:hover {
background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);
}
Then add the hover__effect to the class's component that should be different when the mouse pointer hovers over it
Hover
Please check my sandbox for a live demo.

Categories

Resources