React 中的内联 CSS 样式:如何实现 a:hover?

Inline CSS styles in React: how to implement a:hover?

我非常喜欢 inline CSS pattern in React 并决定使用它。

但是,您不能使用 :hover 和类似的选择器。那么在使用内联 CSS 样式时实现悬停突出显示的最佳方法是什么?

#reactjs 的一个建议是拥有一个 Clickable 组件并像这样使用它:

<Clickable>
    <Link />
</Clickable>

Clickable 有一个 hovered 状态,并将其作为 props 传递给 Link。但是,Clickable(我实现它的方式)将 Link 包装在 div 中,以便它可以设置 onMouseEnteronMouseLeave。这让事情变得有点复杂(例如 span 包裹在 div 中的行为与 span 不同)。

有没有更简单的方法?

我也是这种情况。非常喜欢在组件中保留样式的模式,但悬停状态似乎是最后一个障碍。

我所做的是编写一个 mixin,您可以将其添加到需要悬停状态的组件中。 这个 mixin 会添加一个新的 hovered 属性 到你的组件状态。如果用户将鼠标悬停在组件的主要 DOM 节点上,它将设置为 true,如果用户离开该元素,它将设置回 false

现在,在您的组件渲染函数中,您可以执行以下操作:

<button style={m(
     this.styles.container,
     this.state.hovered && this.styles.hover,
)}>{this.props.children}</button>

现在每次 hovered 状态改变时组件都会重新渲染。

我还为此创建了一个沙盒存储库,我自己用它来测试其中的一些模式。如果您想查看我的实现示例,请查看。

https://github.com/Sitebase/cssinjs/tree/feature-interaction-mixin

您可以使用 Radium - 它是一个用于 ReactJS 内联样式的开源工具。它恰好添加了您需要的选择器。很受欢迎,看看吧 - Radium on npm

我认为 onMouseEnter 和 onMouseLeave 是可行的方法,但我认为不需要额外的包装器组件。以下是我的实现方式:

var Link = React.createClass({
  getInitialState: function(){
    return {hover: false}
  },
  toggleHover: function(){
    this.setState({hover: !this.state.hover})
  },
  render: function() {
    var linkStyle;
    if (this.state.hover) {
      linkStyle = {backgroundColor: 'red'}
    } else {
      linkStyle = {backgroundColor: 'blue'}
    }
    return(
      <div>
        <a style={linkStyle} onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>Link</a>
      </div>
    )
}

然后您可以使用悬停状态 (true/false) 更改 link 的样式。

您可以使用 css modules as an alternative, and additionally react-css-modules 进行 class 名称映射。

这样您就可以按如下方式导入您的样式,并使用普通 css 本地作用域到您的组件:

import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';

class Table extends React.Component {
    render () {
        return <div styleName='table'>
            <div styleName='row'>
                <div styleName='cell'>A0</div>
                <div styleName='cell'>B0</div>
            </div>
        </div>;
    }
}

export default CSSModules(Table, styles);

这是一个webpack css modules example

制作 Style It —— 部分原因 —— 由于这个原因(其他人不同意其他库/语法的实现,并且内联样式缺乏对前缀 属性 值的支持)。相信我们应该能够在 JavaScript 中简单地编写 CSS 并拥有完全独立的组件 HTML-CSS-JS。现在我们可以使用 ES5 / ES6 模板字符串,而且它也可以很漂亮! :)

npm install style-it --save

函数语法 (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 语法 (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;

如果您使用 React 和 Typescript,请查看 Typestyle

下面是 :hover

的示例代码
import {style} from "typestyle";

/** convert a style object to a CSS class name */
const niceColors = style({
  transition: 'color .2s',
  color: 'blue',
  $nest: {
    '&:hover': {
      color: 'red'
    }
  }
});

<h1 className={niceColors}>Hello world</h1>

我在我最近的一个应用程序中为此使用了一个非常 hack-ish 的解决方案,我发现它比在 vanilla js 中编写自定义悬停设置函数更快(虽然,我承认,也许不是大多数环境中的最佳实践..)因此,如果您仍然感兴趣,请继续。

我创建一个父元素只是为了保存内联 javascript 样式,然后是一个具有 className 或 id 的子元素,我的 css 样式表将锁定并写入悬停样式我专用的 css 文件。这是可行的,因为更细化的子元素通过继承接收内联 js 样式,但其悬停样式被 css 文件覆盖。

所以基本上,我的实际 css 文件存在的唯一目的是保持悬停效果,没有别的。这使得它非常简洁且易于管理,并允许我在我的内联 React 组件样式中完成繁重的工作。

这是一个例子:

const styles = {
  container: {
    height: '3em',
    backgroundColor: 'white',
    display: 'flex',
    flexDirection: 'row',
    alignItems: 'stretch',
    justifyContent: 'flex-start',
    borderBottom: '1px solid gainsboro',
  },
  parent: {
    display: 'flex',
    flex: 1,
    flexDirection: 'row',
    alignItems: 'stretch',
    justifyContent: 'flex-start',
    color: 'darkgrey',
  },
  child: {
    width: '6em',
    textAlign: 'center',
    verticalAlign: 'middle',
    lineHeight: '3em',
  },
};

var NavBar = (props) => {
  const menuOptions = ['home', 'blog', 'projects', 'about'];

  return (
    <div style={styles.container}>
      <div style={styles.parent}>
        {menuOptions.map((page) => <div className={'navBarOption'} style={styles.child} key={page}>{page}</div> )}
      </div>
    </div>
  );
};


ReactDOM.render(
  <NavBar/>,
  document.getElementById('app')
);
.navBarOption:hover {
  color: black;
}
<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="app"></div>

请注意,"child" 内联样式没有 "color" 属性 集。如果是这样,这将不起作用,因为内联样式将优先于我的样式表。

完全 CSS 支持正是大量 CSSinJS 库的原因,要有效地做到这一点,您需要生成实际的 CSS,而不是内联样式。此外,内联样式在更大的系统中反应要慢得多。免责声明 - 我维护 JSS.

添加到 ,这里是涵盖焦点和活动状态的事件,并且使用 onMouseOver 而不是 onMouseEnter 因为后者不会冒泡,如果你有应用事件的目标中的任何子元素。

var Link = React.createClass({

  getInitialState: function(){
    return {hover: false, active: false, focus: false}
  },

  toggleHover: function(){
    this.setState({hover: !this.state.hover})
  },

  toggleActive: function(){
    this.setState({active: !this.state.active})
  },

  toggleFocus: function(){
    this.setState({focus: !this.state.focus})
  },

  render: function() {
    var linkStyle;
    if (this.state.hover) {
      linkStyle = {backgroundColor: 'red'}
    } else if (this.state.active) {
      linkStyle = {backgroundColor: 'blue'}
    } else if (this.state.focus) {
      linkStyle = {backgroundColor: 'purple'}
    } 

    return(
      <div>
        <a style={linkStyle} 
          onMouseOver={this.toggleHover} 
          onMouseOut={this.toggleHover} 
          onMouseUp={this.toggleActive} 
          onMouseDown={this.toggleActive} 
          onFocus={this.toggleFocus}> 
          Link 
        </a>
      </div>
    )
  }

关于 styled-components and react-router v4 你可以这样做:

import {NavLink} from 'react-router-dom'

const Link = styled(NavLink)`     
  background: blue;

  &:hover {
    color: white;
  }
`

...
<Clickable><Link to="/somewhere">somewhere</Link></Clickable>

对于在 React 组件中使用内联样式(以及使用 :hover CSS 函数)来说,这可能是一个很好的 hack:

...

<style>
  {`.galleryThumbnail.selected:hover{outline:2px solid #00c6af}`}
</style>

...

onMouseOver 和 onMouseLeave 与 setState 起初对我来说似乎有点开销 - 但由于这是反应的工作方式,对我来说这似乎是最简单和最干净的解决方案。

例如渲染主题 css 服务器端,也是一个很好的解决方案,可以使 React 组件更干净。

如果您不必将动态样式附加到元素(例如用于主题),则根本不应使用内联样式,而应使用 css 类。

这是保持 html / JSX 简洁的传统 html/css 规则。

简单的方法是使用三元运算符

var Link = React.createClass({
  getInitialState: function(){
    return {hover: false}
  },
  toggleHover: function(){
    this.setState({hover: !this.state.hover})
  },
  render: function() {
    var linkStyle;
    if (this.state.hover) {
      linkStyle = {backgroundColor: 'red'}
    } else {
      linkStyle = {backgroundColor: 'blue'}
    }
    return(
      <div>
        <a style={this.state.hover ? {"backgroundColor": 'red'}: {"backgroundColor": 'blue'}} onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>Link</a>
      </div>
    )
  }

派对迟到了,但有解决方案。您可以使用“&”来定义 hover nth Child 等的样式:

day: {
    display: "flex",
    flex: "1",
    justifyContent: "center",
    alignItems: "center",
    width: "50px",
    height: "50px",
    transition: "all 0.2s",
    borderLeft: "solid 1px #cccccc",

    "&:hover": {
      background: "#efefef"
    },
    "&:last-child": {
      borderRight: "solid 1px #cccccc"
    }
},

使用钩子:

const useFade = () => {
  const [ fade, setFade ] = useState(false);

  const onMouseEnter = () => {
    setFade(true);
  };

  const onMouseLeave = () => {
    setFade(false);
  };

  const fadeStyle = !fade ? {
    opacity: 1, transition: 'all .2s ease-in-out',
  } : {
    opacity: .5, transition: 'all .2s ease-in-out',
  };

  return { fadeStyle, onMouseEnter, onMouseLeave };
};

const ListItem = ({ style }) => {
  const { fadeStyle, ...fadeProps } = useFade();

  return (
    <Paper
      style={{...fadeStyle, ...style}}
      {...fadeProps}
    >
      {...}
    </Paper>
  );
};

我不是 100% 确定这是否是答案,但它是我用来模拟 CSS 的技巧:内联颜色和图像的悬停效果。

`This works best with an image`

class TestHover extends React.PureComponent {
render() {
const landingImage = {     
"backgroundImage": "url(https://i.dailymail.co.uk/i/pix/2015/09/01/18/2BE1E88B00000578-3218613-image-m-5_1441127035222.jpg)",
"BackgroundColor": "Red", `this can be any color`
"minHeight": "100%",
"backgroundAttachment": "fixed",
"backgroundPosition": "center",
"backgroundRepeat": "no-repeat",
"backgroundSize": "cover", 
"opacity": "0.8", `the hove trick is here in the opcaity slightly see through gives the effect when the background color changes`
    }

  return (
    <aside className="menu">
        <div className="menu-item">
          <div style={landingImage}>SOME TEXT</div>
        </div>
    </aside>
      ); 
  }
}
ReactDOM.render(
    <TestHover />,
  document.getElementById("root")
);

CSS:

.menu {
top: 2.70em;
bottom: 0px;
width: 100%;
position: absolute;
}

.menu-item {
cursor: pointer;
height: 100%;
font-size: 2em;
line-height: 1.3em;
color: #000;
font-family: "Poppins";
font-style: italic;
font-weight: 800;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}

Before hover

.menu-item:nth-child(1) {
color: white;
background-color: #001b37;
} 

On hover

.menu-item:nth-child(1):hover {
color: green;
background-color: white;
}

示例:https://codepen.io/roryfn/pen/dxyYqj?editors=0011

这是我使用 React Hooks 的解决方案。它结合了展开运算符和三元运算符。

style.js

export default {
  normal:{
    background: 'purple',
    color: '#ffffff'
  },
  hover: {
    background: 'red'
  }
}

Button.js

import React, {useState} from 'react';
import style from './style.js'

function Button(){

  const [hover, setHover] = useState(false);

  return(
    <button
      onMouseEnter={()=>{
        setHover(true);
      }}
      onMouseLeave={()=>{
        setHover(false);
      }}
      style={{
        ...style.normal,
        ...(hover ? style.hover : null)
      }}>

        MyButtonText

    </button>
  )
}
<Hoverable hoverStyle={styles.linkHover}>
  <a href="https://example.com" style={styles.link}>
    Go
  </a>
</Hoverable>

其中 Hoverable 定义为:

function Hoverable(props) {
  const [hover, setHover] = useState(false);

  const child = Children.only(props.children);

  const onHoverChange = useCallback(
    e => {
      const name = e.type === "mouseenter" ? "onMouseEnter" : "onMouseLeave";
      setHover(!hover);
      if (child.props[name]) {
        child.props[name](e);
      }
    },
    [setHover, hover, child]
  );

  return React.cloneElement(child, {
    onMouseEnter: onHoverChange,
    onMouseLeave: onHoverChange,
    style: Object.assign({}, child.props.style, hover ? props.hoverStyle : {})
  });
}

这是另一个使用 CSS 变量的选项。这需要提前 css 悬停定义,所以我猜它不是纯内联的,但代码很少且灵活。

css(设置悬停状态):

.p:hover:{
 color:var(--hover-color) !important,
 opacity:var(--hover-opacity)
}

反应:

<p style={{'color':'red','--hover-color':'blue','--hover-opacity':0.5}}>

这是用打字稿编写的悬停通用包装器。该组件将在悬停事件上应用通过道具 'hoverStyle' 传递的样式。

import React, { useState } from 'react';

export const Hover: React.FC<{
  style?: React.CSSProperties;
  hoverStyle: React.CSSProperties;
}> = ({ style = {}, hoverStyle, children }) => {
  const [isHovered, setHovered] = useState(false);
  const calculatedStyle = { ...style, ...(isHovered ? hoverStyle : {}) };
  return (
    <div
      style={calculatedStyle}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
    >
      {children}
    </div>
  );
};    

这可以通过 material-ui makeStyles 调用轻松实现:

import { makeStyles } from '@material-ui/core/styles';

makeStyles({
  root: {
    /* … */
    '&:hover': { /* … */ }
  },
});

我使用这个技巧,混合了内联样式和 css:

//inline-style:
const button = {
  fontSize: "2em",
};
return (
  <div style={button} data-hover="button">
    <style>{`[data-hover="button"]:hover {
        font-size: 2.1em !important;
    }`}</style>
    {this.props.text}
  </div>
);

我做了类似于 的事情,但我没有使用 material-ui 或 makeStyles。我在样式对象的 css 中将悬停作为条件添加:

const styles = {
  hoverStyle: {
    color: 'grey',
    '&:hover': { color: 'blue !important' },
  }
};

var NavBar = (props) => {
  const menuOptions = ['home', 'blog', 'projects', 'about'];

  return (
    <div>
      <div>
        {menuOptions.map((page) => <div style={styles.hoverStyle} key={page}>{page}</div> )}
      </div>
    </div>
  );
};

这对我有用。

我找到了一种干净的方法来使用 useState 周围的包装器来完成此操作,我称之为 useHover.
不需要额外 libraries/frameworks。

const App = () => {

    const hover = useHover({backgroundColor: "LightBlue"})

    return <p {...hover}>Hover me!</p>
}

包装代码:

function useHover(styleOnHover: CSSProperties, styleOnNotHover: CSSProperties = {})
{
    const [style, setStyle] = React.useState(styleOnNotHover);

    const onMouseEnter = () => setStyle(styleOnHover)
    const onMouseLeave = () => setStyle(styleOnNotHover)

    return {style, onMouseEnter, onMouseLeave}
}

请注意,当组件未悬停时,useHover 有一个可选的第二个样式参数。

试试看here

您可以创建一个 abstract 悬停 class 例如对于颜色。

.hoverClassColor:hover {
  color:var(--hover-color) !important;
}

然后对于您想要在悬停时将颜色更改为 red 的所有元素:

render() {
  return <a className={'hoverClassColor'} style={{'--hover-color':'red'}}>Test</a>
}

对我来说它就像内联,因为 classes 是抽象的,可以重复用于你想要实现颜色悬停的所有元素。

以下是我在功能组件中使用钩子的方法。使用onMouseEnter/Leave,我直接将颜色设置为状态并在元素的样式道具中使用它(而不是设置悬停状态并使用三元组来更改状态,如前面的答案所示)。

function App() {
  const [col, setCol] = React.useState('white');

  return (
    <div className="App">
      <button
        style={{background: `${col}`}}
        onMouseEnter={() => setCol("red")}
        onMouseLeave={() => setCol("white")}
      >
        Red
      </button>
    </div>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js" integrity="sha256-3vo65ZXn5pfsCfGM5H55X+SmwJHBlyNHPwRmWAPgJnM=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js" integrity="sha256-qVsF1ftL3vUq8RFOLwPnKimXOLo72xguDliIxeffHRc=" crossorigin="anonymous"></script>
<div id='root'></div>

onMouseEnter={(e) => {
    e.target.style.backgroundColor = '#e13570';
    e.target.style.border = '2px solid rgb(31, 0, 69)';
    e.target.style.boxShadow = '-2px 0px 7px 2px #e13570';
}}
onMouseLeave={(e) => {
    e.target.style.backgroundColor = 'rgb(31, 0, 69)';
    e.target.style.border = '2px solid #593676';
    e.target.style.boxShadow = '-2px 0px 7px 2px #e13570';
}}

在样式中设置默认属性或class然后调用 onMouseLeave() 和 onMouseEnter() 来创建悬停功能。