快收藏!超实用标签title属性重写,让同事对你刮目相看

news2024/11/24 10:51:42

原生title属性的弊端

日常开发中,我们经常会遇到hover文本,显示其全部内容的需求。但是原生的title属性有两个很大的缺点

  • 样式丑陋,无法更改

windows下的样式

mac下的样式

  • 不够智能,属性显影只能人为控制

只要写了title属性,hover文本一定会展示。但有些场景,我们需要的功能是,文本超出后,才显示title属性;文本不超出后,不显示title属性。

如图,我们期望的是,被圈起来的部分,hover文本后出现title属性的提示;为圈起来的文字不需要title提示。

但是,title属性很那用,一旦你写了,它永远都会hover显示。

那么,针对上述问题,本文将对title属性进行优化,模拟实现一个更好用的title属性,它具备下面的功能。

  • 样式好看(支持自定义):

  • 仅超出的文本显示title提示:

相信阁下使用后,一定会让你们的同事领导对你刮目相看,想起他们故乡的妈妈桑!

为什么不使用第三方组件

有人一定会问,为什么不使用elementPlus的toolTip组件

我的回答是:

  • 项目中没有或不能引入其他UI库
  • 它的样式不满足我的需求
  • 我想实现一些个性化功能

如果还有人和我杠,我就一句话: 我就想手搓一个,我乐意!

实现思路

首先,我们要明确的是,重写原生标签的 title 属性样式在纯 HTML 和 CSS 中是不可能的。不过,我们需要使用自定义的提示框(tooltip)来实现类似的效果,并对其样式进行完全控制。

大致思路就是:获取文本的鼠标hover位置,全局创建一个div盒子,用fixed布局浮动显示。

本文只抛砖引玉,做基础的代码演示,方便各位读者阅读。大家可以根据业务情况自行完善。

为了提升大家的学习效果,大家可以使用云VCSODE运行下面的demo代码。

云vscode参考地址:https://juejin.cn/post/7388753413309349903(文末链接)

云vscode免配任何环境,可快速运行demo。

技术方案

我们对下面的代码模拟hover实现title属性,hover提示:只要肯吃苦,吃得苦中苦,就有吃不完的苦

原生html

<div class="title">
  奥德彪语录
</div>

我们的大致方案是,hover时(mouseenter),获取当前dom元素,计算出它的位置。然后创建一个全局div,自定义一些样式,写入文本。鼠标 离开时,销毁当前元素。

基础框架

大致的逻辑框架代码如下:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom Tooltip</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="title" οnmοuseenter="createTip">奥德彪语录</div>
</body>

<script>
const titleDom = document.querySelector('.title');
let tipDiv= null
titleDom.addEventListener('mouseenter', ()=>{
  // 创建一个tip提示div
  tipDiv = document.createElement('div');

})
titleDom.addEventListener('mouseleave', ()=>{
  // 销毁tip提示div
})


</script>
</html>

完善mouseenter方法

const titleDom = document.querySelector('.title');
let tipDiv= null
titleDom.addEventListener('mouseenter', ()=>{
  // 创建一个tip提示div
  tipDiv = document.createElement('div');
  tipDiv.className = 'custom-tooltip';
  tipDiv.innerText = '只要肯吃苦,吃得苦中苦,就有吃不完的苦';
  document.body.appendChild(tipDiv);
  // 计算tip提示框的位置
  // 获取 titleDom 元素的边界矩形信息
  const rect = titleDom.getBoundingClientRect();
  // 获取 tipDiv 元素的边界矩形信息
  const tipRect = tipDiv.getBoundingClientRect();
  tipDiv.style.left = rect.left +'px';
  tipDiv.style.top = `${rect.top - tipRect.height -10}px`;
})

我们完善一下custom-tooltip的样式

<style>
  .title{
    margin: 100px;
  }
  .custom-tooltip{
    position: fixed;
    padding: 8px 12px;
    background: rgba(27, 33, 41, .8);
    color: #ffffff;
    border-radius: 5px;
    border: 1px solid rgba(27, 33, 41 .8);
    z-index: 99;
  }
</style>

试试效果,看起来还不错。

完善mouseleave方法

现在,我们实现离开dom,移除tip提示的效果。

titleDom.addEventListener('mouseleave', ()=>{
  // 销毁tip提示div
  tipDiv.remove();
})

完善文本超出才显示tip效果

要想实现这个效果,首先我们需要判断文本是否超出显示。如果超出,hover显示tips提示,否则不显示。

先改造样式,让文本超出显示…

.title{
  margin: 100px;
  width: 50px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

现在hover,文本一定是一直显示的。

判断文本是否超出其容器,可以通过比较文本内容的实际尺寸和容器的尺寸来实现。

const titleDom = document.querySelector('.title');

if (titleDom.scrollWidth > titleDom.clientWidth) {
  console.log('文本超出');
} else {
  console.log('文本没有超出');
}

那么,代码的修改就非常容易了

titleDom.addEventListener('mouseenter', ()=>{
  if (titleDom.scrollWidth <= titleDom.clientWidth) {
    return
  } 
  // ...原来的逻辑
})
titleDom.addEventListener('mouseleave', ()=>{
  // 销毁tip提示div 兼容写法
  tipDiv?.remove?.();
})

完整代码


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom Tooltip</title>
    <link rel="stylesheet" href="styles.css">
    <style>
      .title{
        margin: 100px;
        width: 50px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .custom-tooltip{
        position: fixed;
        padding: 8px 12px;
        background: rgba(27, 33, 41, .8);
        color: #ffffff;
        border-radius: 5px;
        border: 1px solid rgba(27, 33, 41 .8);
        z-index: 99;
      }
    </style>
</head>
<body>
    <div class="title">奥德彪语录</div>
</body>

<script>
const titleDom = document.querySelector('.title');
let tipDiv= null
titleDom.addEventListener('mouseenter', ()=>{
  if (titleDom.scrollWidth <= titleDom.clientWidth) {
    return
  } 
  // 创建一个tip提示div
  tipDiv = document.createElement('div');
  tipDiv.className = 'custom-tooltip';
  tipDiv.innerText = '只要肯吃苦,吃得苦中苦,就有吃不完的苦';
  document.body.appendChild(tipDiv);
  // 计算tip提示框的位置
  // 获取 titleDom 元素的边界矩形信息
  const rect = titleDom.getBoundingClientRect();
  // 获取 tipDiv 元素的边界矩形信息
  const tipRect = tipDiv.getBoundingClientRect();
  tipDiv.style.left = rect.left +'px';
  tipDiv.style.top = `${rect.top - tipRect.height -10}px`;
})
titleDom.addEventListener('mouseleave', ()=>{
  // 销毁tip提示div
  tipDiv?.remove?.();
})


</script>
</html>

封装成一个方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tooltip Component</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <script>
        class Tooltip {
            constructor(titleDom, text) {
                this.titleDom = titleDom;
                this.text = text;
                this.tipDiv = null;
                this.titleDom.addEventListener('mouseenter', () => {
                    this.show();
                });
                this.titleDom.addEventListener('mouseleave', () => {
                    this.hide();
                });
            }

            show() {
                if (this.titleDom.scrollWidth <= this.titleDom.clientWidth) {
                    return;
                }
                this.tipDiv = document.createElement('div');
                this.tipDiv.className = 'custom-tooltip';
                this.tipDiv.innerText = this.text;
                document.body.appendChild(this.tipDiv);
                const rect = this.titleDom.getBoundingClientRect();
                const tipRect = this.tipDiv.getBoundingClientRect();
                this.tipDiv.style.left = `${rect.left}px`;
                this.tipDiv.style.top = `${rect.top - tipRect.height - 10}px`;
            }

            hide() {
                if (this.tipDiv) {
                    this.tipDiv.remove();
                }
            }
        }

        const titleDom = document.querySelector('.title');
        const tooltip = new Tooltip(titleDom, '只要肯吃苦,吃得苦中苦,就有吃不完的苦');
    </script>
</body>
</html>

优化项

对于这个demo其实还有很多优化项,比如

  • 如何让控制tip的位置居中
  • 主题设置(黑白主题)

由于原生html用的很少,这里只简单展示一些技术思路,其他效果大家自行实现。

vue框架中实现

封装成一个组件

在vue中实现就简单很多了,比如,我们可以像下面一样简单粗暴,直接控制tooltip文本的显示隐藏即可。

<template>
  <div class="title-wrapper" @mouseenter="showTooltip" @mouseleave="hideTooltip">
    <div class="title">{{title}}</div>
    <span v-if="showTooltipFlag" class="tooltip">{{tooltipText}}</span>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const props = defineProps({
  title: String,
  tooltipText: String
});

const showTooltipFlag = ref(false);

function showTooltip() {
    if (this.titleDom.scrollWidth <= this.titleDom.clientWidth) {
      return;
    }
    showTooltipFlag.value = true;
}

function hideTooltip() {
    showTooltipFlag.value = false;
}
</script>

<style scoped>
.title-wrapper{
  width: 50px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  position: relative;
}

.title{
  margin: 100px;
}

.tooltip{
  position: absolute;
  padding: 8px 12px;
  background: rgba(27, 33, 41,.8);
  color: #ffffff;
  border-radius: 5px;
  border: 1px solid rgba(27, 33, 41.8);
  z-index: 99;
  white-space: normal;
  top: -30px;
  left: 0;
  width: 200px;
  pointer-events: none;
}
</style>

如果你觉得这种组件的形式不够优雅,我们可以直接封装成一个vue指令。

封装成一个指令

定义自定义指令

v-tooltip.js 中定义自定义指令,并将样式动态添加到页面。

// src/directives/v-tooltip.js
const tooltipStyles = `
.custom-tooltip {
  position: fixed;
  padding: 8px 12px;
  background: rgba(27, 33, 41, 0.8);
  color: #ffffff;
  border-radius: 5px;
  border: 1px solid rgba(27, 33, 41, 0.8);
  z-index: 99;
}
`;

function addTooltipStyles() {
  const styleElement = document.createElement('style');
  styleElement.innerHTML = tooltipStyles;
  document.head.appendChild(styleElement);
}

export default {
  mounted(el, binding) {
    addTooltipStyles();
    
    let tipDiv = null;

    const showTooltip = () => {
      if (el.scrollWidth <= el.clientWidth) {
        return;
      }
      tipDiv = document.createElement('div');
      tipDiv.className = 'custom-tooltip';
      tipDiv.innerText = binding.value || '只要肯吃苦,吃得苦中苦,就有吃不完的苦';
      document.body.appendChild(tipDiv);
      const rect = el.getBoundingClientRect();
      const tipRect = tipDiv.getBoundingClientRect();
      tipDiv.style.left = `${rect.left}px`;
      tipDiv.style.top = `${rect.top - tipRect.height - 10}px`;
    };

    const hideTooltip = () => {
      if (tipDiv) {
        tipDiv.remove();
        tipDiv = null;
      }
    };

    el.addEventListener('mouseenter', showTooltip);
    el.addEventListener('mouseleave', hideTooltip);

    el.__showTooltip__ = showTooltip;
    el.__hideTooltip__ = hideTooltip;
  },
  unmounted(el) {
    el.removeEventListener('mouseenter', el.__showTooltip__);
    el.removeEventListener('mouseleave', el.__hideTooltip__);
  }
};
注册指令

main.js 中注册这个自定义指令。

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import vTooltip from './directives/v-tooltip';

const app = createApp(App);

app.directive('tooltip', vTooltip);

app.mount('#app');
使用指令

在你的组件中使用这个自定义指令。

<!-- src/components/TooltipExample.vue -->
<template>
  <div class="title" v-tooltip="'只要肯吃苦,吃得苦中苦,就有吃不完的苦'">
    奥德彪语录
  </div>
</template>

<style scoped>
.title {
  margin: 100px;
  width: 50px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>

总结

本文介绍了原生title属性的一些优化思路和方法,相信对大家有一定帮助。由于时间问题,本文没有做组件或指令的深度封装,比如提示框出现的位置等等还不支持个性化自定义位置等等。但是,基于demo,已经达到项目可用的地步了,大家可以基于自己的项目自行完善。

如果你很懒,想让我帮你实现更多具体功能,请评论区留言,我会抽空完善上述代码,增加更过功能。如果有人想要react版本的,请留言,我根据评论情况添加。

关注我!前端不迷路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2209126.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Provide和Inject设计Vue3插件

使用provide和inject的Vue依赖项注入非常适合构建Vue3插件或避免prop多层传递。 尽管不经常使用它&#xff0c;但是您可以仅使用两个内置方法来实现依赖项注入&#xff1a;provide和inject。 查看Composition API文档&#xff0c;在Vue 3.0中&#xff0c;使用Provide和Inject进…

【笔记】Day2.5.1查询运费模板列表(未完

&#xff08;一&#xff09;代码编写 1.阅读需求&#xff0c;确保理解其中的每一个要素&#xff1a; 获取全部运费模板&#xff1a;这意味着我需要从数据库中查询所有运费模板数据。按创建时间倒序排序&#xff1a;这意味着查询结果需要根据模板的创建时间进行排序&#xff0…

汉语言文学做大数据七年实际工作经验分享普通人快来围观

&#xff08;一&#xff09;没有人带你 社会上&#xff0c;都很现实。就是进了公司&#xff0c;有师傅&#xff0c;师傅也没空带你&#xff0c;最多就是有空的时候帮你解决问题。 无论是做啥工作&#xff0c;都要靠自己努力。努力不会成为笑话&#xff0c;不努力就是笑话。就…

Crypto虐狗记---”你“和小鱼(五)

前言&#xff1a;剧情五 提示&#xff1a; 一种食物&#xff1f; 一种食物——培根&#xff1a;&#xff08;A B 也暗示是培根加密&#xff09; cyberpeace{attackanddefenceworldisinteresting} 密码学笔记——培根密码 - ILK - 博客园 (cnblogs.com)

Windows如何手动编辑右键上下文菜单 - 注册表通用方法

通过注册表编辑右键菜单的方法 文章目录 前言文件夹空白位置右键列表文件夹选中右键列表&#xff0c;有两个不同的路径&#xff1a;单个文件选中右键列表如何手动创建新的右键快捷按键&#xff1a; 前言 右键菜单有三类&#xff08;具体可以自己分别按下面的类型点击尝试&…

uniapp 设置 tabbar 的 midButton 按钮

效果展示&#xff1a; 中间的国际化没生效&#xff08;忽略就行&#xff09; 示例代码&#xff1a; 然后在 App.vue 中进行监听&#xff1a; <script>export default {onLaunch(e) {// #ifdef APPuni.onTabBarMidButtonTap(()>{console.log("中间按钮点击回调…

禁用微软的windos安全中心

目录 一、为什么禁用 二、WDControl_1.5.0程序禁用windows安全中心 步骤1--- 步骤2--- 三、禁用widows安全中心成功 一、为什么禁用 描述&#xff1a;下载第三方软件常常会收到病毒防护秒杀&#xff0c; 第1---直接无法下载 第2---提前下载在U盘解压会被干掉程序文件 …

SMU Autumn 2024 div2 1st

文章目录 The First Week一、前言二、算法1.逆序对<1>&#xff08;2024牛客国庆集训派对day2 I&#xff09; 2.图论<1>&#xff08;2024牛客国庆集训派对day2 F&#xff09; 3. 二分<1>&#xff08;AcWing 102. 最佳牛围栏&#xff09;<2>&#xff08;…

第17课-C++【模板进阶】

&#x1f307;前言 模板作为搭建STL的关键工具以及泛型编程思想的核心体现&#xff0c;对提高程序灵活性和推动高效迭代开发具有重要意义。除了基本的类型替换功能外&#xff0c;模板还具备如非类型模板参数、全特化、偏特化等高级操作。同时&#xff0c;模板声明与定义不能分…

聚类分析 | AP近邻传播聚类算法

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 AP近邻传播聚类算法 AP&#xff08;Affinity Propagation&#xff09;近邻传播聚类算法是一种基于数据点之间的相似度矩阵来进行聚类的算法。该算法不需要事先设定聚类簇的个数&#xff0c;而是通过在数据点之间传播…

JavaScript 第7章:字符串处理

第7章&#xff1a;字符串处理 在 JavaScript 中&#xff0c;字符串是一个非常常用的数据类型&#xff0c;用于表示文本信息。JavaScript 提供了许多内置的方法来处理字符串&#xff0c;包括操作、搜索、替换和格式化等。 一、字符串操作方法 1. charAt charAt(index) 方法返…

Java面向对象编程--高级

目录 一、static关键字 1.1 静态变量 1.2 静态内存解析 1.3 static的应用与练习 二、单例设计模式 2.1 单例模式 2.2 如何实现单例模式 三、代码块 3.1 详解 3.2 练习&#xff0c;测试 四、final关键字 五、抽象类与抽象方法 5.1 abstract 5.2 练习 六、接口 6.…

基于机器视觉的水果品质检测研究进展

摘 要&#xff1a;水果品质检测关系到水果的包装运输贮藏和销售的效果和收益。传统的外观品质检测主要是利用分级机械&#xff0c;其存在很多不足之处&#xff0c;因此提出了利用机器视觉进行无损检测的技术。利用机器视觉技术主要是检测水果的大小、形状、颜色和表面缺陷四个…

106. 从中序与后序遍历序列构造二叉树【 力扣(LeetCode) 】

文章目录 零、LeetCode 原题一、题目描述二、测试用例三、解题思路四、参考代码 零、LeetCode 原题 106. 从中序与后序遍历序列构造二叉树 一、题目描述 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵…

Static修饰不同对象

目录 Static修饰局部变量 Static修饰全局变量 Static修饰函数 Static修饰成员 Static修饰成员变量 Static修饰成员函数 Static修饰成员的特性&#xff1a; 静态成员变量和静态成员函数的使用案例&#xff1a; 案例1&#xff1a;求1 2 3...n 案例2&#xff1a;单例模…

【学术会议投稿链接】React前端框架:构建现代Web应用的强大工具

【即将截稿】第五届经济管理与大数据应用国际学术会议&#xff08;ICEMBDA 2024&#xff09;_艾思科蓝_学术一站式服务平台 更多学术会议请看&#xff1a;https://ais.cn/u/nuyAF3 目录 引言 一、React简介 二、React的核心概念 1. 组件化 2. 虚拟DOM&#xff08;Virtua…

LOID:有效提升遮挡条件下的车道检测精度

1.论文信息 论文标题&#xff1a;LOID: Lane Occlusion Inpainting and Detection for Enhanced Autonomous Driving Systems 作者&#xff1a;Aayush Agrawal, Ashmitha Jaysi Sivakumar, Ibrahim Kaif∗, Chayan Banerjee† 作者单位&#xff1a;印度马德拉斯印度理工学院&…

数学建模算法与应用 第12章 现代优化算法

目录 12.1 粒子群优化算法 Matlab代码示例&#xff1a;粒子群优化算法求解函数最小值 12.2 遗传算法 Matlab代码示例&#xff1a;遗传算法求解函数最小值 12.3 蚁群算法 Matlab代码示例&#xff1a;蚁群算法求解旅行商问题 12.4 Matlab 遗传算法工具 使用遗传算法工具箱…

java的LinkedList

java的LinkedList 什么是LinkedListLinkedList的模拟实现LinkedList的使用ArrayList和LinkedList的区别 什么是LinkedList LinkedList的官方文档 LinkedList的底层是双向链表结构&#xff0c;由于链表没有将元素存储在连续的空间中&#xff0c;元素存储在单独的结点中&#xf…

一维数组的引用

#define SIZE 5 int main(void) { int i 0; int arr[SIZE] { 86,85,85,896,45 };//同理五个数据只是偶然&#xff0c;可能会更多 //输入 for (i 0;i < SIZE;i) { printf("请输入你的第%d个值&#xff1a;",i1); scanf_s(&…