localeCode: zh-CN order: 81 category: 展示类 title: Tooltip 工具提示 icon: doc-tooltip width: 65%
import { Tooltip } from '@douyinfe/semi-ui';
Tooltip 需要将 DOM 事件监听器应用到 children 中,如果子元素是自定义的组件,你需要确保它能将属性传递至底层的 DOM 元素
同时为了计算弹出层的定位,需要获取到 children 的真实 DOM 元素,因此 Tooltip 支持如下类型的 children
真实 DOM 节点, 如 span,div,p...
import React, { forwardRef } from 'react';
import { Tooltip, Space } from '@douyinfe/semi-ui';
const style={ border: '2px solid var(--semi-color-border)', paddingLeft: 4, paddingRight: 4, borderRadius: 4 };
// 将props属性传递,绑定ref
const FCChildren = forwardRef((props, ref) => {
return (<span {...props} ref={ref} style={style}>Functional Component</span>);
});
// 将props属性传递
class MyComponent extends React.Component {
render() {
return (<span {...this.props} style={style}>ClassComponent</span>);
}
};
function Demo() {
return (
<Space>
<Tooltip content={'semi design'}>
<FCChildren />
</Tooltip>
<Tooltip content={'semi design'}>
<MyComponent />
</Tooltip>
<Tooltip content={'semi design'}>
<span style={style}>DOM</span>
</Tooltip>
</Space>
);
}
render(Demo);
可以通过 position 配置弹出层方向以及对齐位置,position 详细可选值请参考下方 API 文档
配置为 top
时 向上弹出
配置为 topLeft
时,向上弹出,且弹出层与 children 左对齐(当arrowPointAtCenter=false时)
配置为 topRight
时,向上弹出,且弹出层与 children 右对齐(当arrowPointAtCenter=false时)
其他方向同理
import React from 'react';
import { Tooltip, Tag } from '@douyinfe/semi-ui';
function Demo() {
const tops = [
['topLeft', 'TL'],
['top', 'Top'],
['topRight', 'TR'],
];
const lefts = [
['leftTop', 'LT'],
['left', 'Left'],
['leftBottom', 'LB'],
];
const rights = [
['rightTop', 'RT'],
['right', 'Right'],
['rightBottom', 'RB'],
];
const bottoms = [
['bottomLeft', 'BL'],
['bottom', 'Bottom'],
['bottomRight', 'BR'],
];
return (
<div>
<div style={{ marginLeft: 80, whiteSpace: 'nowrap' }}>
{tops.map((pos, index) => (
<Tooltip
content={
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
}
arrowPointAtCenter={false}
position={Array.isArray(pos) ? pos[0] : pos}
key={index}
>
<Tag style={{ margin: 8, padding: 20 }}>{Array.isArray(pos) ? pos[1] : pos}</Tag>
</Tooltip>
))}
</div>
<div style={{ width: 80, float: 'left' }}>
{lefts.map((pos, index) => (
<Tooltip
content={
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
}
arrowPointAtCenter={false}
position={Array.isArray(pos) ? pos[0] : pos}
key={index}
>
<Tag style={{ margin: 8, padding: 20, width: 60 }}>{Array.isArray(pos) ? pos[1] : pos}</Tag>
</Tooltip>
))}
</div>
<div style={{ width: 40, marginLeft: 300 }}>
{rights.map((pos, index) => (
<Tooltip
content={
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
}
arrowPointAtCenter={false}
position={Array.isArray(pos) ? pos[0] : pos}
key={index}
>
<Tag style={{ margin: 8, padding: 20, width: 60 }}>{Array.isArray(pos) ? pos[1] : pos}</Tag>
</Tooltip>
))}
</div>
<div style={{ marginLeft: 80, clear: 'both', whiteSpace: 'nowrap' }}>
{bottoms.map((pos, index) => (
<Tooltip
content={
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
}
arrowPointAtCenter={false}
position={Array.isArray(pos) ? pos[0] : pos}
key={index}
>
<Tag style={{ margin: 8, padding: 20, width: 60 }}>{Array.isArray(pos) ? pos[1] : pos}</Tag>
</Tooltip>
))}
</div>
</div>
);
}
默认情况下 arrowPointAtCenter=true
,小三角始终指向 children 元素中心位置。
你可以将其设置为 false,此时小三角将不再保持指向元素中心。弹出层与 children 边缘对齐
import React from 'react';
import { Tooltip, Button } from '@douyinfe/semi-ui';
function Demo() {
return (
<>
<div>
<Tooltip
position='topLeft'
content='semi design tooltip'>
<Button type='secondary' style={{ marginRight: 8 }}>指向元素中心</Button>
</Tooltip>
</div>
<div style={{ marginTop: 20 }}>
<Tooltip
content='semi design tooltip'
arrowPointAtCenter={false}
position='topLeft'
>
<Button type='secondary' style={{ marginRight: 8, width: 120 }}>边缘对齐</Button>
</Tooltip>
</div>
</>
);
};
hover
,可选 hover
/focus
/click
/custom
/ 'contextMenu'custom
时,需要配合 visible
属性使用,此时显示与否完全受控contextMenu 右键触发在 v 2.42.0 后开始提供
import React, { useState } from 'react';
import { Tooltip, Button, Input, RadioGroup, Radio } from '@douyinfe/semi-ui';
function Demo() {
const [visible, setVisible] = useState(false);
// container 需要设置 position: relative
const getPopupContainer = () => document.querySelector('#tooltip-container');
return (
<div style={{ width: '100%', height: '100%', overflow: 'hidden', position: 'relative' }} id="tooltip-container">
<div style={{ width: '150%', height: '150%', paddingLeft: 50, paddingTop: 50 }}>
<Tooltip content={'hi bytedance'} getPopupContainer={getPopupContainer}>
<Button theme='solid' type='tertiary' style={{ marginBottom: 20 }}>悬停显示</Button>
</Tooltip>
<br />
<Tooltip content={'hi bytedance'} trigger="click" getPopupContainer={getPopupContainer}>
<Button style={{ marginBottom: 20 }}>点击显示</Button>
</Tooltip>
<br />
<Tooltip content={'hi bytedance'} trigger="focus" getPopupContainer={getPopupContainer}>
<Input style={{ width: 100, marginBottom: 20 }} placeholder="聚焦显示" />
</Tooltip>
<br />
<Tooltip content={'hi bytedance'} trigger="contextMenu" getPopupContainer={getPopupContainer}>
<Button theme='solid' type='secondary' style={{ marginBottom: 20 }}>右键点击展示</Button>
</Tooltip>
<br />
<Tooltip
content={'hi bytedance'}
trigger="custom"
visible={visible}
getPopupContainer={getPopupContainer}
>
<span style={{ display: 'inline-block' }}>
<RadioGroup type='button' onChange={(e) => setVisible(e.target.value)} value={visible}>
<Radio value={true}>受控显示</Radio>
<Radio value={false}>受控隐藏</Radio>
</RadioGroup>
</span>
</Tooltip>
</div>
</div>
);
}
你可以通过 className、style 为弹出层配置特定样式,例如覆盖默认的 maxWidth (240px)
import React from 'react';
import { Tooltip, Tag } from '@douyinfe/semi-ui';
() => {
return (
<div>
<Tooltip
style={{
maxWidth: 320
}}
className='another-classname'
content={'hi semi semi semi semi semi semi semi'}
>
<Tag style={{ marginRight: '8px' }}>Custom Style And ClassName</Tag>
</Tooltip>
</div>
);
};
传入 getPopupContainer
,弹层将会渲染至该函数返回的 DOM 中。 这会改变浮层 DOM 树位置,但不会改变视图渲染位置。
需要注意的是: 返回的容器如果不是 document.body
,position
需要设为 "relative"
import React from 'react';
import { Tooltip, Tag } from '@douyinfe/semi-ui';
function Demo() {
return (
<div id="tooltip-wrapper" style={{ position: 'relative' }}>
<Tooltip
position="right"
content="浮层被渲染至#tooltip-wrapper元素中"
trigger="click"
getPopupContainer={() => document.querySelector('#tooltip-wrapper')}
>
<Tag>点击此处</Tag>
</Tooltip>
</div>
);
}
Tooltip、Popconfirm、Popover 都需要劫持 children 的相关事件(onMouseEnter/onMouseLeave/onClick....),用于配置 trigger。
如果直接嵌套使用的话,会使外层 trigger 失效。
需要在中间加一层元素(div 或 span)以防止 trigger 的事件劫持失效。
import React from 'react';
import { Tooltip, Popconfirm, Button } from '@douyinfe/semi-ui';
() => (
<Popconfirm content="是否确认删除" title='确认' style={{ width: 320 }}>
<span style={{ display: 'inline-block' }}>
<Tooltip content={'删除评价'}>
<Button type="danger">删除</Button>
</Tooltip>
</span>
</Popconfirm>
);
Semi 为这种场景提供了 Typography 组件,可以更简单快捷地满足需求。不需要自己再对 Tooltip 的出现做条件判断,详细的使用请参考Typography 组件文档
import React from 'react';
import { Typography } from '@douyinfe/semi-ui';
function Demo() {
const { Paragraph, Title, Text } = Typography;
return (
<div>
<Title heading={5} ellipsis={{ showTooltip: true }} style={{ width: 250 }}>
是一个很长很长很长很长5号标题
</Title>
<br />
<Text link ellipsis={{ showTooltip: true, pos: 'middle' }} style={{ width: 150 }}>
是一个很长很长很长很长的链接
</Text>
<br />
<br />
<Paragraph
ellipsis={{ rows: 3, showTooltip: { type: 'popover', opts: { style: { width: 300 } } } }}
style={{ width: 300 }}
>
多行截断,展示 Popover:Semi Design 是由抖音前端团队与 UED
团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的
Web 应用。
</Paragraph>
</div>
);
}
属性 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
autoAdjustOverflow | 弹出层被遮挡时是否自动调整方向 | boolean | true | |
arrowPointAtCenter | “小三角”是否指向元素中心,需要同时传入"showArrow=true" | boolean | true | |
content | 弹出层内容 | string|ReactNode | ||
className | 弹出层的样式名 | string | ||
clickToHide | 点击弹出层及内部任一元素时是否自动关闭弹层 | boolean | false | |
disableFocusListener | trigger为hover 时,不响应键盘聚焦弹出浮层事件,详见issue#977 |
boolean | false | 2.17.0 |
getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 position: relative 这会改变浮层 DOM 树位置,但不会改变视图渲染位置。 |
function():HTMLElement | () => document.body | |
keepDOM | 关闭时是否保留内部组件不销毁 | boolean | false | 2.31.0 |
margin | 计算溢出时的增加的冗余值,详见issue#549 | number | MarginObject | 0 | 2.23.0 |
mouseEnterDelay | 鼠标移入后,延迟显示的时间,单位毫秒(仅当 trigger 为 hover/focus 时生效) | number | 50 | |
mouseLeaveDelay | 鼠标移出后,延迟消失的时间,单位毫秒(仅当 trigger 为 hove/focus 时生效),不小于 mouseEnterDelay | number | 50 | |
motion | 是否展示弹出层动画 | boolean | true | |
position | 弹出层展示位置,可选值:top , topLeft , topRight , left , leftTop , leftBottom , right , rightTop , rightBottom , bottom , bottomLeft , bottomRight |
string | 'top' | |
prefixCls | 弹出层 wrapper div 的 className 前缀,设置该项时,弹出层将不再带 Tooltip 的样式 |
string | 'semi-tooltip' | |
preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean | ||
rePosKey | 可以更新该项值手动触发弹出层的重新定位 | string|number | ||
style | 弹出层的内联样式 | object | ||
spacing | 弹出层与 children 元素的距离,单位 px(object类型自 v2.45后支持) |
number | SpacingObject | 8 | |
showArrow | 是否显示箭头三角形 | boolean | true | |
stopPropagation | 是否阻止弹层上的点击事件冒泡 | boolean | false | |
transformFromCenter | 是否从包裹的元素水平或垂直中心处变换,该参数仅影响动效变换的 transform-origin ,一般无需改动 |
boolean | true | |
trigger | 触发展示的时机,可选值:hover / focus / click / custom / contextMenu (v2.42后提供) |
string | 'hover' | |
visible | 是否展示弹出层, 需配合 trigger='custom' 使用 | boolean | ||
wrapperClassName | 当 children 为 disabled ,或者 children 为多个元素时,外层将会包裹一层 span 元素,该 api 用于设置此 span 的样式类名 | string | ||
wrapperId | 弹出层 wrapper 节点的 id,trigger 的 aria 属性指向此 id,若不设置组件会随机生成一个 id | string | 2.11.0 | |
zIndex | 弹层层级 | number | 1060 | |
onVisibleChange | 弹出层展示/隐藏时触发的回调 | function(isVisible:boolean) | ||
onClickOutSide | 当弹出层处于展示状态,点击非Children、非浮层内部区域时的回调(仅trigger为custom、click时有效) | function(e:event) | 2.1.0 |
tooltip
role,遵循 WAI-ARIA 规范中对于 Tooltip 的定义Tooltip 的 content 与 children
aria-describedby
匹配,关联 content 与 children关于 children
aria-describedby
属性,值为 content wraper的 id若你 Tooltip的children 是Icon,不包含可见文本,我们推荐你在 children 上添加 aria-label
属性进行相应描述
// Good practices, add aria-label to description tooltip children
/* eslint-disable */
<Tooltip content={<p id='description'>Edit your setting</p>}>
<IconSetting aria-label='Settings'>
</IconSetting>
</Tooltip>