Angular 自定义指令 Tooltip_angular trim
myzbx 2025-09-18 04:58 2 浏览
本文同步本人掘金平台的文章:
https://juejin.cn/post/7082241253819023397
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
Yeah,关注我的读者应该知道,上一篇文章了解 Angular 开发的内容,我们已经概览了 Angular 的相关内容。在自定义指令的部分,我们已经能够实现编写,但是,在实际场景中,我们还需要标准化的管理。
Angular 是 Angular.js 的升版
So,本文,我们就以 Tooltip 来讲解下自定义指令的内容。
线上效果图,如下:
目录结构
在上一篇文章的实现的代码项目基础上,执行命令行:
# 进入 directives 文件夹
$ cd directives
# 创建 tooltip 文件夹
$ mkdir tooltip
# 进入 tooltip 文件夹
$ cd tooltip
# 创建 tooltip 组件
$ ng generate component tooltip
# 创建 tooltip 指令
$ ng generate directive tooltip
复制代码
执行完上面的命令行之后,你会得到 app/directive/tooltip 的文件目录结构如下:
tooltip
├── tooltip // tooltip 组件
│ ├── user-list.component.html // 页面骨架
│ ├── user-list.component.scss // 页面独有样式
│ ├── user-list.component.spec.ts // 测试文件
│ └── user-list.component.ts // javascript 文件
├── tooltip.directive.spec.ts // 测试文件
└── tooltip.directive.ts // 指令文件
复制代码
嗯,这里我将组件放在 tooltip 的同级,主要是方便管理。当然,这个因人而异,你可以放在公共组件 components 文件夹内。
编写 tooltip 组件
在 html 文件中,有:
<div class="caret"></div>
<div class="tooltip-content">{{data.content}}</div>
复制代码
在样式文件 .scss 中,有:
$black: #000000;
$white: #ffffff;
$caret-size: 6px;
$tooltip-bg: transparentize($black, 0.25); // transparentize 是 sass 的语法
$grid-gutter-width: 30px;
$body-bg-color: $white;
$app-anim-time: 200ms;
$app-anim-curve: ease-out;
$std-border-radius: 5px;
$zindex-max: 100;
// :host 伪类选择器,给组件元素本身设置样式
:host {
position: fixed;
padding: $grid-gutter-width/3 $grid-gutter-width/2;
background-color: $tooltip-bg;
color: $body-bg-color;
opacity: 0;
transition: all $app-anim-time $app-anim-curve;
text-align: center;
border-radius: $std-border-radius;
z-index: $zindex-max;
}
.caret { // 脱字符
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid $tooltip-bg;
position: absolute;
top: -$caret-size;
left: 50%;
margin-left: -$caret-size/2;
border-bottom-color: $tooltip-bg;
}
复制代码
嗯~,css 是一个神奇的东西,之后会安排一篇文章来讲解下 sass 相关的内容...
然后,在 javascript 文件 tooltip.component.ts 内容如下:
import {
Component,
ElementRef, // 元素指向
HostBinding,
OnDestroy,
OnInit
} from '@angular/core';
@Component({
selector: 'app-tooltip', // 标识符,表明我这个组件叫做啥,这里是 app-tooltip
templateUrl: './tooltip.component.html', // 本组件的骨架
styleUrls: ['./tooltip.component.scss'] // 本组件的私有样式
})
export class TooltipComponent implements OnInit {
public data: any; // 在 directive 上赋值
private displayTimeOut:any;
// 组件本身 host 绑定相关的装饰器
@HostBinding('style.top') hostStyleTop!: string;
@HostBinding('style.left') hostStyleLeft!: string;
@HostBinding('style.opacity') hostStyleOpacity!: string;
constructor(
private elementRef: ElementRef
) { }
ngOnInit(): void {
this.hostStyleTop = this.data.elementPosition.bottom + 'px';
if(this.displayTimeOut) {
clearTimeout(this.displayTimeOut)
}
this.displayTimeOut = setTimeout((_: any) => {
// 这里计算 tooltip 距离左侧的距离,这里计算公式是:tooltip.left + 目标元素的.width - (tooltip.width/2)
this.hostStyleLeft = this.data.elementPosition.left + this.data.element.clientWidth / 2 - this.elementRef.nativeElement.clientWidth / 2 + 'px'
this.hostStyleOpacity = '1';
this.hostStyleTop = this.data.elementPosition.bottom + 10 + 'px'
}, 500)
}
// 组件销毁
ngOnDestroy() {
// 组件销毁后,清除定时器,防止内存泄露
if(this.displayTimeOut) {
clearTimeout(this.displayTimeOut)
}
}
}
复制代码
编写 tooltip 指令
这是本文的重点,具体的说明,我在代码上标注出来~
相关的文件 tooltip.directive.ts 内容如下:
import {
ApplicationRef, // 全局性调用检测
ComponentFactoryResolver, // 创建组件对象
ComponentRef, // 组件实例的关联和指引,指向 ComponentFactory 创建的元素
Directive, ElementRef,
EmbeddedViewRef, // EmbeddedViewRef 继承于 ViewRef,用于表示模板元素中定义的 UI 元素。
HostListener, // DOM 事件监听
Injector, // 依赖注入
Input
} from '@angular/core';
import { TooltipComponent } from './tooltip/tooltip.component';
@Directive({
selector: '[appTooltip]'
})
export class TooltipDirective {
@Input("appTooltip") appTooltip!: string;
private componentRef!: ComponentRef<TooltipComponent>;
// 获取目标元素的相关位置,比如 left, right, top, bottom
get elementPosition() {
return this.elementRef.nativeElement.getBoundingClientRect();
}
constructor(
protected elementRef: ElementRef,
protected appRef: ApplicationRef,
protected componentFactoryResolver: ComponentFactoryResolver,
protected injector: Injector
) { }
// 创建 tooltip
protected createTooltip() {
this.componentRef = this.componentFactoryResolver
.resolveComponentFactory(TooltipComponent) // 绑定 tooltip 组件
.create(this.injector);
this.componentRef.instance.data = { // 绑定 data 数据
content: this.appTooltip,
element: this.elementRef.nativeElement,
elementPosition: this.elementPosition
}
this.appRef.attachView(this.componentRef.hostView); // 添加视图
const domElem = (this.componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
document.body.appendChild(domElem);
}
// 删除 tooltip
protected destroyTooltip() {
if(this.componentRef) {
this.appRef.detachView(this.componentRef.hostView); // 移除视图
this.componentRef.destroy();
}
}
// 监听鼠标移入
@HostListener('mouseover')
OnEnter() {
this.createTooltip();
}
// 监听鼠标移出
@HostListener('mouseout')
OnOut() {
this.destroyTooltip();
}
}
复制代码
到这里,已经完成了 99% 的功能了,下面我们在页面上调用即可。
页面上调用
我们在 user-list.component.html 上添加下面的内容:
<p style="margin-top: 100px;">
<!-- [appTooltip]="'Hello Jimmy'" 是重点 -->
<span
[appTooltip]="'Hello Jimmy'"
style="margin-left: 200px; width: 160px; text-align: center; padding: 20px 0; display: inline-block; border: 1px solid #999;"
>Jimmy</span>
</p>
复制代码
TooltipDirective 这个指令我们已经在 app.module.ts 上进行声明,我们直接调用即可。目前的效果如下:
我们实现的 tooltip 是底部居中展示,也就是我们平常使用框架,比如 angular ant design 中 tooltip 的 bottom 属性。对于其他属性,读者感兴趣的话,可以进行扩展。
至此,我们可以很好的维护自己编写的指令文件了。
【完】
相关推荐
- 油猴脚本:净化微博界面,聚焦核心内容
-
在信息过载的社交场景中,微博原生界面的推荐流、视频入口、游戏标签及无障碍图标,常分散用户注意力,影响内容浏览效率。【移除微博推荐、视频、游戏标签和无障碍图标】油猴脚本,以精准界面优化能力,为用户打造...
- 一个月快速学习前端开发入门与学习计划,技能也能变成钱
-
快速学习前端开发(HTML/CSS/JavaScript),核心是“先搭框架、再填细节、边学边练”,按以下3步走,能高效入门:“基础→实战→进阶”为逻辑,每天学习+练习时长建议2-3小时,重点围绕“...
- HTML5 header标签的定义与规定_html中header标签的作用
-
提示:点击上方"蓝色字体"↑可以订阅噢!<header>标签定义文档的页面组合,通常是一些引导和导航信息(DOM接口、可设置属性)。<header>标签定义文档的页眉(介绍信...
- CSS 电梯:纯 CSS 实现的状态机与楼层导航
-
点击关注公众号,“技术干货”及时达!作为一个对状态机痴迷的开发者,我常常会被一些文章点燃灵感,比如那篇《用HTML复选框和CSS打造完整状态机》。纯CSS驱动的状态机...
- Vue.js源码全方位深入解析,快人一步进名企
-
Vue.js源码全方位深入解析,快人一步进名企来百度APP畅享高清图片//下栽のke:chaoxingit.com/512/Vue.js源码全方位深入解析,快人一步进名企随着互联网技术的不断发展,前端...
- 你真的会用setState吗?_setstate用法
-
setState函数是什么?1.将需要处理的变化塞入组建的state对象中2.告诉该组件及其子组件需要用更新的状态来重新渲染3.响应事件处理和服务端响应更新用户界面的主要方式setState经典...
- React 事件机制原理_react案例
-
相关问题React合成事件与原生DOM事件的区别React如何注册和触发事件React事件如何解决浏览器兼容问题回答关键点React的事件处理机制可以分为两个阶段:初始化渲染时在root...
- Vue 侦听器(watch 与 watchEffect)全解析1
-
在Vue组合式API中,当我们需要在响应式状态变化时执行“副作用”(如操作DOM、发起异步请求、修改其他状态等),watch和watchEffect是核心工具。它们能帮我们精准捕获状态...
- Github 45.9K,一款助你用 HTML 实现现代Web交互神器,开发效率飙升
-
在前端技术日新月异的今天,React、Vue、Angular等大型框架几乎成为Web开发的标配。你是否曾经因为这些复杂的工具链、繁琐的配置和“JavaScript疲劳”而感到力不从心?有没有想...
- Wijmo5 Flexgrid基础教程:动态加载右键菜单
-
WijmoEnterprise下载>在上文中我们介绍了使用wijmo3的menu给flexgrid做右键菜单。本文我们就在这个基础上,介绍如何动态的给flexgrid添加右键菜单。本文的右键菜...
- 实战 | 基于Vue语言的企业级前端开发框架Hui的应用研究
-
文/华夏银行乌鲁木齐分行信息科技部张文涛随着前端技术的迅速发展,开发模式也在不断演进。早期的Web页面由服务器端生成,浏览器负责展现,前后端高度耦合,导致业务逻辑与展现逻辑混杂在一起,代码可维护...
- Vue渲染器解析_vue渲染函数实战
-
渲染器是Vue与浏览器之间的「翻译官」。它拿到一份用JavaScript对象描述的UI(虚拟DOM),然后精准地创建、更新、销毁真实DOM,同时把响应式数据和渲染函数绑定成一条自动刷新的...
- 如何实现 Vue 自定义组件中 hover 事件以及 v-model
-
在CSS中,很容易在鼠标hover时进行更改,只需:.item{background:blue;}.item:hover{background:green;}在Vue中,它...
- Pydoll:更流畅可靠的浏览器自动化
-
无论是数据抓取,还是自动化AI助手,或是网页测试,浏览器自动化技术都是能在其中发挥关键作用的一环。然而,传统的浏览器自动化工具往往依赖于复杂的WebDriver配置,这不仅增加了使用的难度,还...
- web前端tips:js的事件循环(Event Loop)
-
一、介绍1.什么是js的事件循环JavaScript事件循环是一种处理异步事件和回调函数的机制,它是JavaScript实现异步编程的核心。它在浏览器或Node.js环境中运行,用于管理任务队列和调...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 简介 (30)
- HTML 响应式设计 (31)
- HTML URL 编码 (32)
- HTML Web 服务器 (31)
- HTML 表单属性 (32)
- HTML 音频 (31)
- HTML5 支持 (33)
- HTML API (36)
- HTML 总结 (32)
- HTML 全局属性 (32)
- HTML 事件 (31)
- HTML 画布 (32)
- HTTP 方法 (30)
- 键盘快捷键 (30)
- CSS 语法 (35)
- CSS 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)
- SVG 文本 (32)
- 时钟启动 (33)
- HTML 游戏 (34)
- JS Loop For (32)