整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Javascript正则表达式示例之HTML标签及HTML语法树

一节聊到正则表达式的简单应用,不足之处欢迎留言交流。

Javascript正则表达式示例之基本概念

今天,我们来看一下,如何使用正则表达式,匹配HTML标签及相关信息。

为什么要加上相关信息呢?

因为,如果您想写一个HTML语法树解析库的时候,可能会用到。


下面内容用到的语法

|:表示或者,要么前面,要么后面

(?<=我前面出现的内容)要匹配的内容:只匹配前面出现的字符之后的内容。

可视图

要匹配的内容(?=我前面出现的内容):只匹配后面出现的字符之前的内容。

可视图

分组捕获:一对完整的小括号(),表示一个组。

\数字:你要使用那一个分组捕获到的内容。

.*?:在正则表达式中,. 表示匹配任意字符,* 表示匹配 0 到任意次的前一个字符,? 表示非贪婪匹配,即尽可能匹配最少的字符。因此,.*? 表示匹配任意字符零次或多次,但尽可能匹配最少的字符。这个表达式通常用于匹配一个字符串中的所有内容,但是避免贪婪匹配导致的匹配错误。

^: 表示匹配开始

[要匹配的字符]:只匹配括号中的字符。

比如[0-9]、[a-z]、[A-Z]、[0-9a-zA-Z]、[0-9abc]等等。

[^要匹配的字符]:[]中加^表示匹配不是“要匹配的字符”。


1、匹配所有HTML标签,并清空。实现innerText类似的功能。

<body><div id="left">left</div><div id="right">right</div></body>
const text = document.body.innerText;
text = text.replace(/\n/g, '');
console.log(text);
//输出: leftright

假设没有innerText的功能呢?实现这个功能,使用正则表达式无疑是最方便的。

var text = document.body.innerHTML.replace(/<[^>]+>/g,'');
text = text.replace(/\n/g, '');
console.log(text);
//输出: leftright

匹配结果

可视图


是的,这个正则表达式的意思是,查找<>并且包含他们之间不为>的一段字符串。

到这里,您以为就结束了吗?您在网上搜索匹配HTML标签,可能也会得到这么一个结果(例如:<[^>]+>、<.*?>、等等),但实际上这只是开始,我们本着只要是程序就可能有bug的原则,所以我们来看下面一个例子。

const strHtml = '<span data-code=">">>是大于符号。</span>';
const strRes = strHtml.replace(/<[^>]+>/g, '');
console.log(strRes);
// ">>是大于符号。

[可怜]bug出现了,怎么办?别着急,请看下一个知识点。


2、匹配HTML标签属性,是写一个HTML语法树要经历的事情。


2.1、首先,我们先解决第一点最后的bug。

const strHtml = '<span data-code=">">>是大于符号。</span>';
// 一个小改动即可。
const strRes = strHtml.replace(/<("[^"]*"|[^>])+>/g, '');
console.log(strRes);
// >是大于符号。

可视图

完美[打脸] ,还没结束……

const strHtml = "<span data-code='>'>>是大于符号。</span>";
const strRes = strHtml.replace(/<("[^"]*"|[^>])+>/g, '');
console.log(strRes);
// '>>是大于符号。

甲:这不是我写的HTML不标准,是你的解析库兼容性不好,浏览器都可以识别,你为什么不可以?

已:……。

const strHtml = `<i code="<"><小于符号。</i><i code='>'>>大于符号。</i>`;
// 继续改造
const strRes = strHtml.replace(/<((["'])+.*?\2|[^>])+>/g, '');
console.log(strRes);
// <小于符号。>大于符号。

匹配结果

可视图

是的,利用正则表达式分组捕获的语法,实现了上面的需求。


2.2 现在,我们来看看,如何找到某个标签的所有属性。

const strHtml = `
<input type='text' disabled value="" class="txt txt-md" v-on:click="save('button')" />
`;

上面的例子中,有多种情况,我们首先来整理出来。

属性1:type='text'

/[\w]+=(["'])+.*?/

属性2:disabled

/[\w]+/

属性3:value=""

/[\w]+=(["'])+.*?/

属性4:class="txt txt-md"

/[\w]+=(["'])+.*?/

属性5:v-on:click="save('button')"

/[\w:]+=(["'])+.*?/

其他情况:欢迎讨论。

把所有情况连起来之后。

const strHtml = `<input type='text' disabled value="" class="txt txt-md" v-on:click="save('button')" />`;
const tagAttrs = strHtml.match(/(?<=\s)[\w:-]+(=(["']).*?\2)*/g) || [];
console.log(tagAttrs);
//  ["type='text'", 'disabled', 'value=""', 'class="txt txt-md"', `v-on:click="save('button')"`]

匹配结果

可视图

人人为我,我为人人,欢迎您的浏览,我们一起加油吧。

于前端开发人员来说,css是我们再熟悉不过的朋友的,它就相当于是我们页面的衣服,页面好不好看,就看我们css运用的是否炉火纯青[小鼓掌][小鼓掌][小鼓掌]。css学起来简单,但是我们要把它“修炼”到出神入化境界,那这可不是一丁点时间就可以的,需要我们的日积月累,时刻专研。

而今天,就带给大家一个CSS特效-霓虹灯按钮,这也是我看到很不错的效果,带来分享给大家,希望大家喜欢[送心][送心][送心]

效果如下:

最终效果

那好,废话不多说,开始我们的CSS代码。

准备一个HTML标签

// 这里我们用div标签来模拟button按钮,标签可以随意,a、p、span等都可以
// 通常在开发中使用别的标签来代替button标签,是因为原始的标签样式不好看,
// 我们还得重置样式,而其他标签不带有样式,我们可以更好的控制自己想要的样式,
// 当然,button标签也是可以的,但是如前面所说,原始的样式需要我们重置。
<div class="btn">button</div>

CSS部分的代码

body {
    margin: 0;
    padding: 0;
    background: #000; // 黑色背景,只为更能突出样式效果
}
// 初始化按钮样式
.btn {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 200px;
    height: 60px;
    text-align: center;
    line-height: 60px;
    color: #fff;
    font-size: 24px;
    font-family: sans-serif;
    text-decoration: none;
    text-transform: uppercase;
    box-sizing: border-box;
    // linear-gradient() 渐变属性,函数用于创建一个表示两种或多种颜色线性渐变的图片
    background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
    background-size: 400%;
    border-radius: 30px;
}

初始样式为这样:

初始样式

然后我们给按钮加上动画,代码如下:

.btn:hover {
  	// linear: 动画从开始到结束具有相同的速度。
    // infinite: 无限次播放
    animation: animate 8s linear infinite;
}
@keyframes animate {
    0% {
        background-position: 0%;
    }
    100% {
        background-position: 400%;
    }
}

效果就变为下面这样:

最后我们给它加上鼠标移上去的效果,代码如下:

.btn::before {
    content: '';
    position: absolute;
    top: -5px;
    left: -5px;
    right: -5px;
    bottom: -5px;
    z-index: -1;
    background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
    background-size: 400%;
    border-radius: 40px;
    opacity: 0;
    transition: .5s;
}
.btn:hover::before {
    filter: blur(20px);
    opacity: 1;
    animation: animate 8s linear infinite;
}

现在这样,就是我们的最终效果了:

就此,我们的霓虹灯的按钮效果就完成了

不足百行,我们就完成了这个效果,以下是我们的CSS的全部代码:

转品牌升级后更新了全新的Logo,今天我们用纯CSS来实现转转的新Logo,为了有一定的挑战性,这里我们只使用一个标签实现,将最大化的使用CSS能力完成Logo的绘制与动画效果。

新logo保留了原本logo里转转熊的轮廓,两个熊耳是两个卫星围绕熊头旋转,是“循环”的意思。中间倾斜的转转首字母“Z”,既像二手质检的印章,又像N——NEW的首字母,代表着全新的二手生活方式。

以上是我们要还原的Logo效果动图,现阶段使用方式主要是gif和lottie SVG两种方式。因为我们的目的是使用单标签实现所有的功能点,对于一些细节部分有所取舍,并不是百分百还原,也并不代表使用CSS的成本和效果是最好的,仅表达CSS的强大能力。接下来分析我们要核心实现的功能点:

  • 开始动画时中心的大圆以及2个围绕的小圆都有放大的效果
  • 动画开始后2个小圆围绕中心的椭圆旋转
  • 字母 Z 由大变小覆盖在中心的椭圆
  • 字母 Z 出现时中心的椭圆由红色边框圆变成实心红色圆
  • 仔细观察这个字母 Z 并不是普通的字符

基于上述分析接下来开始核心的代码拆解实现,在开始前先定义一些变量方便后续使用:

:root {
  /* 主题色 */
  --mainColor: #ff483c;
  /* 字体颜色 */
  --fontColor: #fff;
  /* 字体宽度 */
  --zWidth: 260px;
  /* 中心椭圆宽高比 */
  --parentScaleY: 0.9;
  /* 子元素抵消比例 */
  --childScaleY: calc(1 / var(--parentScaleY))
}

2个小圆旋转动画

如果是围绕一个正圆的旋转那么实现就简单很多了,但是这里是一个椭圆,纯CSS应该怎么做呢?根据小编目前的了解大概有以下几种方式都可以实现,但是对应的效果有所区别:

  • 使用 transform-style: preserve-3d;开启3D模式,但是对应的小圆在旋转角度存在视角消失的情况,需要额外处理
  • 使用 cubic-bezier 贝塞尔曲线执行动画可以达到椭圆曲线的效果,但是存在动画开始停止不可控的情况
  • 使用 rotate 0-360deg旋转,对应子元素需要设置反向旋转来抵消父级的旋转带来的影响

对比各种情况最终使用第三个方案即可满足条件且实现成本较低,首先画出一个椭圆的边框效果,设置scaleYborder-radius将一个正方形变成椭圆形,核心代码如下:

  width: 570px;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 10px solid var(--mainColor);
  transform: scaleY(var(--parentScaleY));

然后给这个大圆添加旋转的动画,将整体旋转360度,这时还没有添加其他的元素,所以界面没有变化。

@keyframes circle {
  0% {
    transform: scaleY(var(--parentScaleY)) rotate(0);
  }
  100% {
    transform: scaleY(var(--parentScaleY)) rotate(360deg);
  }
}

设置大圆的动画执行相关参数。

animation: circle 1s 1 cubic-bezier(.5,.08,.52,.93) forwards;

边框上面的两个圈基于伪元素实现,绘制一个圆并使用定位将小圆定位于大圆的顶部两端。然后设置动画,这里需要注意的是需要设置反向旋转来抵消父级的旋转。

@keyframes mini-circle {
  0%{
    transform: rotate(0deg) scaleY(var(--childScaleY));
  }
 100%{
    transform: rotate(-360deg) scaleY(var(--childScaleY));
  }
}

这里为了方便查看将椭圆的参数调大进行对比,可以看到设置抵消后的区别一个圆被压缩了另一个保持了正常的圆形:

未设置抵消

已设置抵消

这里为了更好的利用元素的使用,这里将两个圆的绘制进一步优化到一个伪元素中,核心使用径向渐变背景实现,在同一个 background 中绘制两个纯色圆形,两个圆除了绘制的位置不同其他都是一样。代码如下:

background: 
  radial-gradient(circle 65px at 31% 24.5%,var(--mainColor) 0% 100%, transparent),
  radial-gradient(circle 65px at 72% 24.5%, var(--mainColor) 0% 100%, transparent);

到此这部分的内容基本功能完成,实际效果如下:

Z 字母

接下来使用另一个伪元素实现 Z 字母的效果,仔细观察 Z 字母的两个拐角都是锐角效果,并不是日常字母有拐角的效果。

基于当前的样式我们可以用3段矩形拼接完成效果,上下各一段,中间一段增加旋转角度,但是只用一个伪元素如何绘制三个矩形呢,还是用到CSS渐变,这次需要用到linear-gradient线性渐变,顶部和底部正常从上往下绘制,中间的部分需要绘制线条的旋转角度,除开需要显示的颜色其他部分用#0000透明色,为了方便看效果对三个矩形更换了不同的颜色,代码如下:

background: 
  linear-gradient(#f00 25%, #0000 25%),
  linear-gradient(#0000 75%, #29eb9a 25%),
  linear-gradient(124deg, #0000 40%, #000 40% 60%, #0000 60%);

绘制的效果如下:

可以看到目前的效果还不能满足需求,两端都出现了多余的部分,需要进一步优化将其隐藏。这时候需要用到background-sizebackground-position,通过background-size设置绘制内容的大小,通过设置background-position设置绘制内容的起点位置,因为设置了一定的空隙部分需要增加no-repeat不重复,增加以下代码:

background-size: 80% var(--w), 80% var(--w), 100% 100%;
background-position: 0 0, 100% 0;
background-repeat: no-repeat;

此时基本符合预期的效果,但实际设计图左下角的锐角部分有超出正常矩形一部分。

所以需要对刚刚设置的sizeposition部分改进,将第一个矩形左侧空出16px:

background-size: 74% var(--w), 80% var(--w), 100% 100%;
background-position: 16px 0, 100% 0;

因为空出了左侧一部分距离,导致整个内容不再是一个正方形,所以需要设置 scaleX 还原宽度将图像还原到正方形。

对于刚刚设置的background相关属性可以在代码层进一步优化,使用简写将代码合并到一行:

background:
  linear-gradient(var(--fontColor) 25%, #0000 25%) 16px 0 / 74% var(--zWidth) no-repeat,
  linear-gradient(#0000 75%, var(--fontColor) 25%) 100% 0 / 80% var(--zWidth) no-repeat,
  linear-gradient(124deg, #0000 40%, var(--fontColor) 40% 60%, #0000 60%) 0 0 / 100% 100% no-repeat;

然后继续给这个 Z 添加动画效果,默认设置 opacity: 0 隐藏,因为 Z 是小圆动画执行结束才出现的,所以还需增加动画的延迟执行时间,增加动画相关代码:

animation: z 0.3s 1s 1 ease-in-out forwards;
opacity: 0;

设置动画将内容从1.5倍缩小到正常并设置旋转角度,返抵消以及平移到Logo正中心。这里因为初始增加了1.5的放大所以设置返抵消相关参数有所不同,在设置scaleY的同时还设置了skew进一步还原尺寸。

@keyframes z {
  0% {
    transform: rotate(-45deg) scale(1.5) translate(0, -50%) skew(-13deg, 8deg);
    opacity: 1;
  }
  100% {
    transform: rotate(-42deg) scaleY(var(--childScaleY)) translate(-4%, -64%) skew(-13deg, 8deg);
    opacity: 1;
  }
}

完成后的 Z 字母动画效果:

其他细节

开始动画时中心的椭圆以及2个围绕的小圆都有放大的效果,所以需要对前面动画关键帧的定义继续完善。

小圆部分执行动画的前10%也增加scale(0.5)

@keyframes mini-circle {
  0%{
    transform: rotate(0) scale(0.5);
  }
  10%{
    transform: rotate(0) scaleY(var(--childScaleY));
  }
}

大圆执行动画的前10%部分增加scale(0.8)。大圆部分还有一个效果是字母 Z 出现的时候中心的椭圆由边框圆变成实心圆,所以是在动画结束前增加对背景色的变化,代码如下:

@keyframes circle {
  0% {
    transform: scale(0.8) rotate(0);
  }
  10% {
    transform: scaleY(var(--parentScaleY)) rotate(0);
  }
  80% {
    background-color: var(--fontColor);
  }
  100% {
    background-color: var(--mainColor);
    transform: scaleY(var(--parentScaleY)) rotate(360deg);
  }
}

最后

到此我们整个代码实现过程就结束了,基于一个标签实现了转转的Logo的绘制及动画效果。当然这只是对其主要的功能还原,如需完整还原细节还需要进一步优化。

CSS的确是足够强大且对很多复杂的图形效果都能实现,这里我们主要是使用渐变背景实现图案的绘制,除了渐变还可以使用CSS阴影也能达到类似的效果。使用CSS对比使用SVG或GIF在资源体积上有很大的提升,本次实现的Logo使用lottiejson需要100KB左右,使用GIF大约需要27KB左右,但是纯CSS实现仅1KB不到即可完成,但是对于较为复杂的场景对应的代码也提升了不少的复杂度,大家要根据实际的情况使用,感兴趣的同学欢迎留言交流。


作者:大转转FE

来源:微信公众号:大转转FE

出处:https://mp.weixin.qq.com/s/CUfAKou5LEYtwzYB83LWdA