前端正确处理“文字溢出”的思路

news2024/11/29 20:52:27

目录

前言

一. 组件效果预览

二. 单行溢出的处理

三. 前期准备

四. 理清思路

五. 完成 autoEllipsis 函数

六. 保留后缀的实现

七. 源码

八. 优化点


前言

        最近在项目中需要做到类似于 Mac 下这种,当屏幕宽度足以容下当前文件名称的时候,文件名称全部展示,不做省略。


        然而当用户缩放浏览器显示的尺寸时,我们需要做到省略中间的文字,选择保留后缀这种方案。

        我个人也是感觉这个方案是最好的,因为大部分情况下,用户更关心的是这个文件的类型,而后缀名的保留往往是最佳的选择。我个人也查阅了很多相关文章,并且借鉴了一些已有轮子的代码思路,实现了一个符合我们项目中需求的一个组件。


一. 组件效果预览

单行文字溢出时自动省略,并且不保留后缀。

单行文字溢出时自动省略,并且保留后缀。

        多行文字溢出时,然后再开始省略。这个情况是我们项目中比较特殊的场景。简单来说就是假设我现在想让文字显示两行,如果两行的时候没有溢出,那么正常显示。如果两行情况下还是溢出了,那么我再去处理溢出的文字。假设这是没有做任何操作的的效果:


使用我们的组件以后的效果:


(tips:不一定必须是两行,三行,四行都是可以的。我们接下来实现的组件会让你高度自定义去处理文字溢出的场景。) 

如果你想自己先尝试一下效果,那么你可以快速使用 npm 安装一下。

原仓库地址:🫱AutoEllipsisTxt自动省略文字[2]

  • npm i auto-ellipsis-text

  • pnpm i auto-ellipsis-text

  • yarn add auto-ellipsis-text

使用起来也非常简单,你只需要包裹住你的文字即可

        话回正题,接下来我会一步一步讲解我实现这个组件的思路,我写的这个组件不一定是最优的,你需要做到知其然并知其所以然,然后完善我写的组件的不足之处,你可以实现自己的自动省略文本方案,才是本文的目的。

二. 单行溢出的处理

        我们先只考虑单行的情况。通常我们在自己的应用中展示很多文件信息的时候,往往选择的布局方式就是高度是一定的,说白了就是高度其实我们是定死的,宽度我们不确定,因为用户有可能会在某些情况下拖动浏览器,造成宽度发生变化,但是总会给宽度一个最小值和一个最大值来保障排版的统一性。

        样式方面,在这里我使用的是 UnoCSS ,将样式內联在了标签里,如果你还不了解这种写法,你可以点击下方的文章学习。不过即使你之前从未了解过 UnoCSS ,也不会影响你下面的阅读,因为样式不是本文的重点,并不影响整体阅读。
        🫱手把手教你如何创建一个代码仓库[3]

        让我们先创造一个简单的溢出场景,代码很简单,容器是一个 width 最大值为 200pxheight 为固定 30px 的 div


现在页面上的效果如下图:

        可以很清晰的看出,由于我们文字在容器内放不下,但是我们又没对溢出这一特殊场景做出处理,所以就造成了当前页面的效果。先别急,我们一步一步来。

最开始我去查阅 MDN 的时候,查阅到了一个 “确认过眼神,你就是我要找到人” 的属性。

        什么?text-overflow,我们要找到不就是文字溢出时候的处理吗?我兴奋的赶快添加到了我的组件上。


效果如下:


        然后看着毫无变化的页面,开始怀疑我自己是不是单词拼错了,然后一个字母字母的比对,排除了单词打错字的情况,但页面还是没有变化。🤔

于是我又返回 MDN 去查看自己是否遗漏了哪些东西,发现了这样一段文字。


        这里直接说结论,其实 text-overflow 这个属性不会为了让文字省略而去创造省略的场景。它其实是在你处理过溢出场景之后,帮你做对于文字溢出的的二次特殊处理。当你对于页面溢出做没有任何操作时,这个属性其实是无效的。(注意:它仅仅只处理文字溢出的场景。)

        既然你说了,让我们添加额外的属性:overflow-hidden 和 white-space,那么我们就自己添加。我们先只添加一个 overflow-hidden 来看看会发生什么。


        我们发现,下面多出去的文字倒是被省略了,但是我们的省略号呢??我就不卖官子了,其实造成这个的原因的答案就是下面这句话:

我们仔细看上面我们溢出的场景。


        我们下面两行文字其实是溢出在了盒子下方,正好对应了上面 text-overflow 的介绍,“无法在盒子下面溢出” 这句话。

        在这里我们就需要制造一个让文字强制不换行的场景。那么就需要用到我们另外一个十分重要的属性,white-space


        我们本节只需要关系 nowrap 这一个值即可。剩下的值如果读者有兴趣可以自行了解,我们不过多解释。

        首先你要知道,其实我们 web 页面的换行,并不是毫无意义的自己就换行了,而是都有一个隐藏的换行符,你可以把这个隐藏的换行符浅浅的理解为 white-space(空格)

        理解了上面那段话,那我们的属性 white-space:nowrap 的中文含义就十分明显了。white-space对应空格no-wrap 代表不换行。连起来的意思就是,遇到空格不换行。而我们的换行其实有一个隐藏的 white-space ,那么我们添加这个属性以后,就会造成一个不会换行的场景。

        让我们先把 text-ellipsis 和 overflow-hidden 属性删除,只添加 white-space:nowrap 看看页面效果会是怎么样。


效果如下:


        可以看到,我们省略了那个隐藏的换行符,所以文字不会自动换行了,那么整段文字都显示到了一行上。此时我们再加上我们的两个属性,overflow-hidden 和 text-ellipsis,神奇的一幕就发生了。


我们仅仅只使用了几个 CSS 属性就完成了单行情况下不保留后缀的文字溢出处理。 

三. 前期准备

        首先你需要准备一个 autoEllipsis.vue 文件,首先写出下面的代码,来和我一起完成这个组件。

<template>
复制代码
```

请注意这个 id 叫做 container 的 div 元素将在接下来的内容中起到至关重要的作用。

接下来使用 ref 分别去拿到这两个 dom 元素。

最后我们需要设计一个函数,在组件挂载以后,让它去正确处理我们文字溢出的场景。

        接下来的需求就是,这个 autoEllipsis 函数如何去实现。别着急写代码,我知道你现在有可能还是一头雾水无从下手,让我先带你理清思路然后再开始写代码。

四. 理清思路

        首先我们因为要做到通用性,所以container 的宽度是不能确定的,它的宽度需要根据它外层的父元素来决定,也就是上文中我们提到的有一个最大值最小值宽度的元素。


换句话说,我们这个 container 要去动态的拿到外层父元素的宽度。

        我们先不讲代码如何实现,我们假设现在我们已经拿到了,就叫做 fatherWidth。然后我们再通过刚刚的 ref 获取到的 text dom 元素,去拿到外面传进来的文字内容。通过拿到这个 span 元素的 offsetWidth ,就可以拿到文字的长度。通过判断文字的 offsetWidth 是否大于 fatherWidth 。然后我们通过两个宽度相减,可以得出我们到底溢出的文字宽度为多少。

        拿到溢出的宽度以后,那么我们就可以用溢出宽度来除以文字大小,**(overWidth/fontSize)** ,就可以算出我们到底溢出了多少文字。

        假设现在我们现在溢出宽度为 200px。我们的文字大小为 20px,那么 200/20 就算出我们现在溢出了 10 个字。

        我们并且一开始就拿到了总的文字内容,假如我们之前的文字总数为 30 个。那么在这个情况下我们屏幕上只展示了 20 个文字,因为有 10 个字溢出被我们忽略了。

        到这里之后,我们要做的事情就非常简单了,我们只需要从原来 30 个字的中间开始做切割。一边去掉 5 个,那么此时容器恰好可以容下 20 个字。中间我们再手动加上 “...” 省略号不就完美达成了吗?

        上面想表达的意思用大白话来讲,其实也就是去掉中间的10个文字,然后随便再找一个字替换成字符串三个点 ... 。

五. 完成 autoEllipsis 函数

第一步就是为了拿到我们放入的文字宽度。注释已经写的很清楚了,就不过多赘述。

        然后我们再去拿外面父元素的宽度。此时会出现第一个分支, container 的宽度小于父元素的宽度,很容易可以猜到现在我们的文字内容是完全可以容纳的,不需要做特殊处理。

        第二个分支,当我们的 container 宽度大于了父亲元素的宽度,那么我们可以通过传递 props 来区分是否需要保留后缀,如果不需要保留后缀,我们直接给 container设置我们第二个标题讲解的知识就OK了。

六. 保留后缀的实现

        如果看到这里,你还没有正确的保留后缀思路,我建议你重新去观看一下标题四,这里我们大致的思路就是为了拿到父元素可以容纳多少文字。

这里我们的思路其实就是计算出得出我们需要删除多少个文字

很简单的思路,就是字符串使用 slice 切割我们上面计算得出的,两边需要删除多少文字。

        最后的关键一步,我们需要把 container 的 white-space 属性设置为 normal,因为我们已经正确的处理了文字数量,现在的 container 已经不会溢出了。

七. 源码

下面是本组件的核心代码 autoEllipsis 函数的源码

function autoEllipsis(container: HTMLElement, textNode: HTMLSpanElement) {
  const str = premitiveText; //1.拿到的所有文字信息
  textNode.textContent = str; //2.将所有文字放入到我们的 span 标签中
  container.style.whiteSpace = "nowrap"; //3.先将文字全部放入到《一行》中,为了计算整体宽度
  container.style.width = "fit-content"; //4. 给 container 设置 fit-content 属性,就可以拿到正确的内容宽度
  const containerWidth = container.clientWidth; //5. 拿到了 container 的宽度

  const parent = container.parentElement; // 拿到外部父元素的宽度
  const parentWidth = parent!.clientWidth || parent!.offsetWidth;
  if (containerWidth <= parentWidth) {
    //如果container 的宽度《小于》父元素的宽度,不做任何处理
    textNode.textContent = str;
    return;
  } else if (cssEntirely.value) {
    container.style.width = parentWidth + "px";
    container.style.whiteSpace = "nowrap";
    container.style.textOverflow = "ellipsis";
    container.style.overflow = "hidden";
    return;
  } else {
    const textWidth = textNode.offsetWidth; //1. 拿到文字节点的宽度
    const strNumer = str.length; //2. 拿到文字的数量
    const avgStrWidth = textWidth / strNumer; //3. 拿到平均每个文字多少宽度
    const canFitStrNumber = Math.floor(
      (parentWidth * props.startEllipsisLine) / avgStrWidth //4. 根据父元素的宽度来计算出可以容纳多少文字
    );

    const shouldDelNumber = strNumer - canFitStrNumber + 1.5; //1. 算出需要删除几个文字(1.5是为了省略号的宽度
    const delEachSide = shouldDelNumber / 2; //2. 因为要保留中间,所以我们不能只从开头删除,也需要从两头删除
    const endLeft = Math.floor(strNumer / 2 - delEachSide); //3. 因为下面要用到 slice 所以需要计算出 index
    const startRight = Math.ceil(strNumer / 2 + delEachSide); //4. 和上面同理

    switch (props.suffix) {
      case true: {
        textNode.textContent =
          str.slice(0, endLeft) + "..." + str.slice(startRight);
        break;
      }
      case false: {
        textNode.textContent = str.slice(0, -shouldDelNumber) + "...";

        break;
      }
    }
    container.style.wordBreak = "break-all";
    container.style.whiteSpace = "normal";
  }
}

八. 优化点

        这个组件目前在 ... 省略号的文字占用上,并不能准确的根据文字大小调整所需的字数。也就是下面的 1.5 这个数字无法精确的算出,但是目前我们项目的文字大小是确定的,所以我也就没有再优化了,还希望各位可以提交 Pr 来一起完善这个组件。

原仓库地址:🫱AutoEllipsisTxt自动省略文字[4]

https://github.com/hanzhenfang/auto-ellipsis-text

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

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

相关文章

小心!认证中的这几个坑别再踩了

【小心&#xff01;认证中的这几个坑别再踩了】 1.三天拿证不可信&#xff01; 想要今天办理、明天拿证是不可能实现的。一个认证需要经过前期的材料准备、现场审核、提交报告、颁发证书这一整套流程&#xff0c;拿FSC认证来说&#xff0c;就算所有环节都顺利完成&#xff0c;也…

网络安全入门学习路线,CSDN最全!建议收藏!

前言 网络安全行业热度越来越高&#xff0c;我也因此对这个领域充满了好奇。但是我也是一个完全的门外汉&#xff0c;从零开始学习网络安全有多难&#xff1f;下面是我的经验总结。 第一阶段&#xff1a;入门 我的第一步是寻找网络安全的入门书。推荐《黑客攻防技术宝典&…

Revit被遮挡的基础及快速构件显隐

一、Revit被遮挡的基础 在设计中&#xff0c;常常会有某些构件被上部的其他构件遮挡住的情况。在当前的多数样板中&#xff0c;这类构件会以隐藏线的模式表示出来。如下图所示&#xff0c;某设备下有四个条形设备基础。 这些基础被设备挡住的部分就以虚线的”隐藏线“样式显示在…

开发敏捷高效 | 云原生应用开发与运维新范式

5 月 18 日&#xff0c;腾讯云举办了 Techo Day 腾讯技术开放日&#xff0c;以「开箱吧&#xff01;腾讯云」为栏目&#xff0c;对外发布和升级了腾讯自研的一系列云原生产品和工具。其中&#xff0c;腾讯云开发者产品中心总经理刘毅围绕“开发敏捷高效”这一话题&#xff0c;分…

三天吃透Java面试八股文(2023最新整理)

文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Java的特点 Java是一门面向对象的编程语言。面…

属实不赖!Alibaba开源GitHub星标114K微服务架构全彩进阶手册

开篇必读&#xff1a; 在当今的数字化经济时代&#xff0c;微服务架构已经成为公司业务构建的主流架构模式&#xff0c;代表了未来的技术发展趋势&#xff0c;同时微服务也成为开发者的必备技能。 本书从微服务架构的设计理念和方法论切入&#xff0c;从不同角度全面介绍微服…

Flink第七章:状态编程

系列文章目录 Flink第一章:环境搭建 Flink第二章:基本操作. Flink第三章:基本操作(二) Flink第四章:水位线和窗口 Flink第五章:处理函数 Flink第六章:多流操作 Flink第七章:状态编程 文章目录 系列文章目录前言一、Keyed State(按键分区)1.KeyedStateTest.scala2.PeriodicPVEx…

土壤热通量传感器的应用

土壤热通量传感器又称“土壤热通量板”、“热流计”&#xff0c;主要用于测量土壤的能量平衡和土壤层的热导率。土壤热通量传感器采用热电堆测量温度梯度&#xff0c;该热电堆有两种不同的金属材料组成。热电堆探测器接受热辐射&#xff0c;热辐射能使两个不同材料的结点之间产…

如何在华为OD机试中获得满分?Java实现【单核CPU任务调度】一文详解

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述4. Java算法源码5. 测试6.解题思路1. 题目描述 现在有一个CPU和一些任务…

每天一道面试题之==和equals的区别是什么?

&#xff1d;&#xff1d;和equals的区别是什么&#xff1f; ""是一个关系运算符&#xff0c;关系运算符可以用来进行数据和数据之间的比较&#xff0c;而在java中数据类型大致可以分为两大类分别是基本数据类型和引用数据类型。 基本数据类型包含 byte&#xff0c…

谢谢所有可爱的粉丝友友们

前言&#xff1a; 其实我很早之前就申请了CSDN账号&#xff0c;其中陆陆续续也在写博客&#xff0c;但是没有坚持下来&#xff0c;大家可以看到我的前期博客&#xff0c;少之又少&#xff0c;我甚至只要看一下我的博客内容&#xff0c;我就知道我当时在哪里&#xff0c;我当时在…

[RSA议题分析] eBPF Warfare - Detecting Kernel eBPF Rootkits with Tracee

文章目录 简介议题分析基础知识用户空间与内核空间hookrootkit追踪技术 eBPF架构Tracee - 一个运行时安全检查工具 RootKit种类与各个阶段的攻防LD_PRELOAD RootKitKernel Module RooKitKernel RootKit HidingKernel RootKit Hooksyscall table hookingfile operations hooking…

<数据结构>NO6.堆的实现|堆的应用

&#x1f407;本文用到的所有代码都放在我的gitee仓库了&#x1f407;syseptember的gitee仓库https://gitee.com/syseptember/data-structure/tree/4f0b1f9f56e3b0bee72fa0563c23a6917b3252e8/Heap/Heap 目录 堆的概念 堆的实现 堆的应用 堆排序 时间复杂度分析 TopK问题 …

做功能测试好几年,一直都像是给人“打杂”的!直到这天我开始……

“做功能测试这几年&#xff0c;一直都像是给人打杂的&#xff01;”这句话&#xff0c;想必很多测试人员都有过同感。曾经&#xff0c;我们每天要重复执行繁琐的测试流程&#xff0c;手动输入大量数据、进行各种操作、检查每一个细节&#xff0c;整个过程反复无常&#xff0c;…

用 AI 轻松管理数据收集和分布! #TallyForms

工作中需要面对各种各样让人眼花缭乱的表格&#x1f92f; 此时一款让数据收集和整理变得轻松的平台简直就是打工人们的福音&#xff0c;TallyForms 就拥有自定义表单、实时分析和高效管理的全方位解决方案&#xff01; TallyForms TallyForms 是一个免费的在线表单生成器&am…

[论文阅读] Explicit Visual Prompting for Low-Level Structure Segmentations

[论文地址] [代码] [CVPR 23] Abstract 我们考虑了检测图像中低层次结构的通用问题&#xff0c;其中包括分割被操纵的部分&#xff0c;识别失焦像素&#xff0c;分离阴影区域&#xff0c;以及检测隐藏的物体。每个问题通常都有一个特定领域的解决方案&#xff0c;我们表明&am…

高压放大器在大学教研领域的实际应用

在大学教研领域中&#xff0c;高压放大器可以用于多种实际应用。下面将介绍其中几个典型的应用场景。 1、激光切割 适用高校学院&#xff1a;机械学院 应用场景&#xff1a;机械制造、各类材料的切割 2、超声雾化 适用高校学院&#xff1a;医学院、机械学院、物理学院 应用场景…

C4D R26 渲染学习笔记(1):C4D版本选择和初始UI框介绍

C4D版本知识 C4D通过R来进行版本区分&#xff0c;现在2023年5月22日最新版的是R26。说一下特殊版本。 C4D版本介绍特点R19OC快乐版3.07最高版本&#xff0c;OC是C4D最具性价比的渲染器&#xff0c;OC学习成本低&#xff0c;渲染速度快&#xff0c;但是注意OC 3.07只支持10系N…

MySQL8.0数据库超详细安装教程全过程

1、官网下载MySQL8.0地址&#xff1a;MySQL :: Download MySQL Installer (Archived Versions) 2、 双击安装包进行安装 3、自定义安装 4、选择MySQL Server8.0 5、创建MYSQL数据存储目录及安装目录 6、配置安装路径及数据存储目录 7、确认继续 8、选择MySQL&#xff0c;下一步…

我4年测试,已失业3个月.....

我做测试4年&#xff0c;一线城市薪水拿到15K&#xff0c;中间还修了一个专升本&#xff0c;这个年限不说资深肯定也是配得上经验丰富的。今年行情不好人尽皆知&#xff0c;但我还是对我的薪水不是很满意&#xff0c;于是打算出去面试&#xff0c;希望可以搏一个高薪。 但真到面…