
其实解决这个问题根本不用那么复杂——用JS+正则表达式就能实现最简方案。不需要遍历整个DOM树,不需要处理复杂的节点关系,只需一段精准的正则,就能快速匹配富文本中的第一张图片地址。不管你的富文本里混着多少样式代码、嵌套标签,这个方法都能“直戳重点”,帮你省掉大量调试时间。
这篇文章就把这个技巧拆解得明明白白:从正则表达式的写法逻辑(怎么匹配标签、怎么提取src属性),到JS如何调用执行匹配,再到避坑指南(比如处理没有图片的情况、过滤无效链接),一步一步教你用最少的代码解决最常见的富文本图片提取问题。哪怕是刚入门的前端新手,跟着步骤走也能立刻用上,再也不用为“找第一张图”发愁了。
做内容运营或者前端开发的朋友,肯定都遇到过这种麻烦——要从富文本内容里“抠”出第一张图片当封面,比如公众号文章的头图、电商商品详情页的预览图,或者企业新闻的缩略图。手动找太费时间,用DOM遍历吧,又总踩坑:要么遇到图片嵌套在多层
为什么提取富文本首图不用DOM遍历?先踩踩我之前的坑
其实最开始我也老用DOM遍历那套:把富文本字符串插进一个隐藏的
里,TinyMCE会加一堆style属性,甚至有些编辑器会给图片套两层
后来我查MDN文档才明白,对于已经是字符串形式的富文本(比如数据库里存的HTML字符串),直接用正则匹配比操作DOM高效得多——正则是直接在字符串上找“pattern(模式)”,不用构建DOM树。就像你找一篇文章里的第一个“的”字,直接看文字肯定比把文章做成Word文档再用“查找”功能快,一个道理。而且正则能跳过所有嵌套结构,直接定位标签,不管它被多少层标签包着,都能“穿透”找到第一个。
正则表达式怎么写?三步搞定富文本首图提取
既然正则这么好用,那具体怎么写?我把自己用了一年的“最简方案”拆成三步,连刚入门的前端新手都能跟着做。
第一步:写对正则pattern,覆盖99%的img标签情况
正则的核心是“匹配img标签的src属性”,我常用的pattern是:
]+src=["']?([^"']+)["']?[^>]>
看着复杂?我拆开给你讲,保证你能听懂:
]+
:匹配src=["']?
:找src属性,["']?
表示“src的值可以用双引号、单引号,或者不用引号”(有些编辑器写src时会省略引号);([^"']+)
:捕获src的值(也就是图片地址),用括号包起来,后面能通过JS拿到;[^>]
:匹配img标签剩下的属性(比如alt、style),直到标签结束。最后加个i
修饰符(忽略大小写),比如SRC、Src这种写法也能匹配到,更稳妥。
第二步:用JS的exec方法,精准拿到第一张图
写好pattern后,用JS的exec
方法执行正则就行。比如你有个富文本变量叫richText
,代码可以这么写:
// 定义正则pattern(加i修饰符忽略大小写)
const imgRegex = /
]+src=["']?([^"']+)["']?[^>]>/i;
//
执行正则,匹配第一个img标签
const matchResult = imgRegex.exec(richText);
//
提取src值(匹配到的话取第1个捕获组,否则用默认图)
const firstImgUrl = matchResult ? matchResult[1] 'https://example.com/default.jpg';
这里要注意:不要加g修饰符(全局匹配)!因为我们只需要第一张图,exec
第一次匹配到的就是第一个img标签,加了g反而会打乱顺序——MDN文档里明确说过,exec对于带g的正则,会记录lastIndex(上一次匹配的位置),反而容易出错。
第三步:避坑指南,这些边界情况你肯定遇到过
我用这个正则的时候,踩过几个典型的坑,提前告诉你怎么避:
坑1:src用单引号,正则匹配不到?
有些编辑器(比如TinyMCE)生成的img标签会用单引号:
。这时候正则里的["']?
就派上用场了——它能同时匹配单引号和双引号,完美解决这个问题。
坑2:隐藏的img标签,要不要排除?
如果你的需求是“只取显示的图片”,就得排除带style="display:none"
的img标签。这时候可以给正则加个负向前瞻:
]+(?!style=["']?display:none["']?)[^>]+src=["']?([^"']+)["']?[^>]>
(?!style=["']?display:none["']?)
的意思是“后面不能跟着style=”display:none”或者style=’display:none’”,这样就能过滤掉隐藏的图片。
坑3:相对路径的图片,怎么转成绝对路径?
富文本里的src经常是相对路径(比如/uploads/2023/10/1.jpg
),这时候需要拼接域名才能显示。比如你的网站域名是https://www.foodblog.com
,可以这么处理:
const baseUrl = 'https://www.foodblog.com';
const firstImgUrl = matchResult ? ${baseUrl}${matchResult[1]}
'默认图地址';
坑4:没有图片或者src为空,怎么办?
要是富文本里没有img标签,matchResult
会是null;要是src是空字符串(比如
),matchResult[1]
会是空。这时候一定要加边界判断:
let firstImgUrl;
if (matchResult && matchResult[1]) {
firstImgUrl = matchResult[1];
} else {
// 用默认图或者提示文字
firstImgUrl = 'https://example.com/no-image.jpg';
}
实测有效!常见编辑器的img标签都能匹配
为了让你更放心,我整理了几个常用富文本编辑器的img标签结构,用上面的正则测试,结果都能精准匹配到src值:
编辑器名称 | img标签示例 | 正则匹配结果 |
---|---|---|
CKEditor | ![]() |
image.jpg |
TinyMCE | ![]() |
photo.png |
微信公众号编辑器 |
![]() |
/upload/20231001.jpg |
飞书文档 | ![]() |
https://feishu.cn/abc.jpg |
其实这个方法我用了快一年,从美食博客到电商产品详情页,再到企业官网的新闻系统,都没出过错。你要是怕写错正则,可以用regexr.com在线验证——把你的富文本内容贴进去,写好pattern,能实时看到匹配结果。要是你遇到什么特殊情况,比如正则匹配不到某个编辑器的img标签,欢迎在评论区告诉我,我帮你调调~
为什么不用DOM遍历提取富文本首图?
主要是踩过太多坑啦!首先DOM遍历的结构兼容性特别差,不同编辑器生成的HTML嵌套层数、属性都不一样,比如微信公众号的图片包在
里,CKEditor会加
正则表达式里的符号和括号都是什么意思?
其实拆开很简单,比如常用的/]+src=[“‘]?([^”‘]+)[“‘]?[^>]>/i,先看
]+——匹配img标签开头,[^>]+表示后面跟任意不是>的字符(也就是img的所有属性);然后src=[“‘]?——找src属性,[“‘]?是说src的值可以用双引号、单引号或者不用引号;接着([^”‘]+)——用括号包起来的是要捕获的图片地址,[^”‘]+表示不是引号的字符,也就是src的值;最后[^>]是匹配img标签剩下的属性,直到标签结束。加i修饰符是忽略大小写,比如SRC也能匹配到。
用exec方法提取首图要注意什么?
首先别加g修饰符!因为我们要找第一张图,exec不加g的时候只会匹配第一次出现的img标签,加了g反而会记录上次匹配的位置,容易出错;然后执行正则后要判断匹配结果,比如const matchResult = imgRegex.exec(richText),如果matchResult存在,就取matchResult[1](第一个捕获组,也就是图片地址),不存在就用默认图;还要注意src为空的情况,比如,这时候就算匹配到了,matchResult[1]也是空,得再判断一下,不然会拿到无效链接。
遇到隐藏的img标签怎么过滤掉?
可以给正则加个“负向前瞻”的条件,比如把正则改成/]+(?!style=[“‘]?display:none[“‘]?)[^>]+src=[“‘]?([^”‘]+)[“‘]?[^>]*>/i,里面的(?!style=[“‘]?display:none[“‘]?)意思是“后面不能跟着style=”display:none”或者style=’display:none’”,这样就能过滤掉带display:none的隐藏img标签了。比如编辑器里的占位图、表情图标,加了这个条件就不会匹配到,保证拿到的是显示的首图。
常见编辑器的img标签都能匹配到吗?
亲测大多数常用编辑器都能用!比如CKEditor的、TinyMCE的
、微信公众号编辑器的

,还有飞书文档的,用原文的正则都能精准匹配到src值,我做过表格测试,这些情况都没问题,放心用就行。