做前端这些年,我越来越觉得页面里最难处理的不是复杂交互,而是那些“看起来是内容、其实是广告”的模块。今天想聊一个老生常谈但又经常被忽视的问题:HTML怎么区分正文与广告,以及<aside>标签在广告位语义里到底该怎么用。这个问题说大不大,但直接影响网站的可访问性、SEO、广告落地效果,甚至广告拦截器的识别逻辑。这篇文章不是教科书,是我在实际项目里踩坑之后的经验汇总,适合正在做门户、资讯站、后台系统或者内容社区的开发者,也适合那些被“广告位代码怎么写才规范”问住的新手。
我见过太多页面把广告写成一堆<div class="ad-box">,写的时候挺爽,后面重构、对接第三方、做无障碍适配的时候全得回来补课。与其后面返工,不如一开始就把正文和广告的边界用标准语义划清楚。下面我把这套思路拆开讲,基本照着做就能用。
1. 为什么“区分正文与广告”是一件正经事
很多人觉得这是个伪需求:反正用户能看见广告不就行了?但你要知道,页面的读者不只是人。浏览器、搜索引擎爬虫、屏幕阅读器、内容提取工具、广告拦截插件,全都在读你的HTML。它们读不懂“视觉上是个广告”,只能靠标签、属性和结构来判断。
1.1 浏览器、爬虫与屏幕阅读器怎么理解页面
先说浏览器。浏览器拿到HTML之后会生成DOM树,CSS只是负责把树“画”出来,画完就丢了。你看到的“这个模块在右侧、加了虚线边框、背景灰底”,这些视觉特征在DOM里并不存在。对浏览器里的自动化工具来说,它只认标签语义和结构关系,你的广告放左边右边,它根本不在乎,它在乎的是这个模块是什么角色。
再说搜索引擎爬虫。爬虫抓取页面之后要抽取正文、计算关键词权重、判断页面主题。现在的爬虫已经不傻到只会数关键词了,它会分析HTML结构。如果你把广告内容堆在<main>或者<article>里,爬虫很可能把广告文案当成正文的一部分,导致关键词密度虚高,还可能被判定为“内容与广告混杂”,影响页面质量评分。反过来,如果你把正文写进一个没有语义的<div>里,而把广告放进看似正常的段落里,那爬虫抽取正文时就会张冠李戴。
屏幕阅读器更直接。视障用户用读屏软件浏览页面时,它会按照DOM顺序把内容读出来,并且可以通过“地标(landmark)”快速跳转。如果页面上唯一的语义地标只有<header>和<footer>,中间全是裸<div>,读屏用户根本没法快速跳过广告区块,每次进页面都要忍受一段超长的广告朗读。这不是体验问题,是可用性问题。
1.2 广告拦截器与内容过滤的底层逻辑
你可能听过“广告拦截器会屏蔽带ad字样的class”这个说法。这话对,但不全对。现在主流的广告拦截插件(比如uBlock Origin、AdGuard)用的是“过滤列表”机制,列表里有一堆规则,这些规则会匹配资源地址、DOM节点的特征、脚本行为。很多规则确实会看类和ID,比如class="ad"、id="banner"这类,但这只是最基础的一层。
真正强的是请求层面的拦截。你页面里某个<script>如果去请求了某个广告联盟的域名,拦截器在浏览器发请求之前就能掐断,这个不管你的HTML标签写得再语义化都拦不住。所以网上那些“把广告class改成 content 就能绕过拦截”的说法,基本只对规则比较弱的拦截器有效,遇到基于请求域名拦截的就歇菜了。
这里要厘清一个概念:让广告在语义上“像广告”,不是为了让广告更隐蔽,反而是为了让内容识别工具更准确地剥离它。广告位语义化的价值,是让浏览器扩展、爬虫、读屏软件能明确知道“这片区域是广告,不属于正文内容”,这样它们可以按自己的策略去处理——该跳过的跳过,该标记的标记,该保留的保留。所以说,语义化不是对抗广告拦截器的工具,而是让整个生态更有序的基础。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. aside标签的语义与正确姿势
讲完背景,该说主角了。<aside>是HTML5新增的语义化标签,但很多人对它的理解停留在“侧边栏”三个字上,用起来就容易跑偏。
2.1 aside到底算什么:从定义说起
W3C对<aside>的官方解释是:它表示一个与页面主要内容“间接相关”的部分。注意,是间接相关,不是无关。换句话说,<aside>里的内容单独拿出来看,也是完整可用的,但它跟当前页面的核心主题只是支撑、补充或者附带的关系。
举几个典型场景:
- 一篇技术文章旁边的“推荐阅读”列表,跟文章主题相关,但不是正文本身。
- 正文里穿插的“作者简介”卡片,读者可以直接跳过不看,不影响理解正文。
- 页面右侧的“热门标签云”,聚合了站内多个主题,和当前页只有部分关联。
- 还有最常见的一个:广告位。广告内容本质上是“与页面主题无关的商业信息”,它不参与正文叙事,放
<aside>里非常合适。
我见过有人把整个侧边栏都包进<aside>,这也是对的,但要注意:侧边栏里如果既有“热门文章”又有“广告”又有“关注公众号”,那它整体是一个<aside>,里面各个模块再用<section>去做子分区,逻辑上更清楚。别把一个<aside>写成所有杂物的垃圾桶。
2.2 aside、section、article怎么选
这几个标签的区分,前端新人经常搞混。我给你的判断方法很简单:看内容能不能独立存在。
<article>:内容脱离当前页面后依然完整、自洽。比如一篇博文、一条新闻、一个评论、一个产品卡片。如果把这个区块复制到另一个网站,它还是能单独阅读,那就该用<article>。<section>:内容是一个主题分节,通常要有标题,它可以拆开看,但一般不会脱离页面单独发布。比如一篇文章的“操作步骤”部分、侧边栏里的“热门文章”列表。<aside>:内容与主内容是间接关系,属于“旁边的话”。注意,“旁边的话”不一定是视觉上的旁边,正文中间插入的“补充说明”框也适合用<aside>。
广告位为什么用<aside>而不是<section>?因为<section>暗示“这页面的内容分成几块,你都得看”,而<aside>明确表示“这块和主内容不是一路的,可看可不看”。广告恰好是后者。
再说一个容易翻车的点:不要为了语义化强行把广告区块的标题用<h2>一直往上提。有个说法是“广告要有标题方便读屏跳过”,这个思路没错,但标题层级要克制。侧边栏的“赞助商”标题用一个<h2>就行,它的层级代表“这是侧边栏区域的小标题”,不该和正文的<h2>抢层级。
3. 广告位语义化的完整实现方案
理论讲完了,直接上实操。这一节的内容是我在自己项目里验证过的,你照着写基本不会出大问题。
3.1 基础结构:aside+role=complementary
最简单的广告位结构可以这么写:
html复制<aside class="ad-slot" role="complementary" aria-label="广告">
<div class="ad-slot__container">
<p class="ad-slot__label">广告</p>
<!-- 广告联盟代码或自营广告 -->
<a href="https://example.com/landing" rel="sponsored nofollow">
<img src="ad-banner.png" alt="某产品宣传">
</a>
</div>
</aside>
这里几个点拆开讲。
role="complementary"是一个ARIA地标角色,它告诉辅助技术:这个区域是补充内容,和主内容分开,读屏用户可以用快捷键直接跳到这,扫一眼再跳走。这个角色跟<aside>的语义天然匹配,加上去之后兼容性更好,尤其是老版本浏览器里<aside>的隐式语义还没被读屏软件支持时,ARIA能兜底。
aria-label="广告"给这片区域一个可访问名称。为什么要有这个名字?因为读屏用户快速浏览地标列表时,能看到一串“complementary、complementary、complementary”,根本分不清哪个是广告哪个是推荐阅读。有了aria-label,地标列表里显示的就是“广告、推荐阅读、最新文章”,用户能快速决定跳过哪里。
rel="sponsored nofollow"必须养成习惯。这是给搜索引擎看的:“这个链接是付费或赞助性质的”。其中sponsored自2019年起被Google支持,nofollow是告诉爬虫不要传递权重。这两个属性同时加上,等于在SEO层面把广告链接和正常外链区分开了,不会因为广告位权重传递问题被算法处罚。
广告标签<p class="ad-slot__label">广告</p>不是你随便加着玩的。国内对互联网广告的标识有明确要求,页面上标注“广告”两个字既能满足合规需求,也能让视觉用户第一时间意识到这是广告,减少误点导致的负面体验。我见过有些网站怕用户不点广告,故意把广告样式做得跟正文一模一样,结果用户投诉率飙升,广告主也流失了,得不偿失。
3.2 让广告位更规范的几个细节
除了基础结构,还有几个细节我强烈建议你加上。
第一,给广告位容器设置明确的尺寸。广告位和正文不一样,正文高度是内容撑开的,但广告位通常要求固定宽高,否则广告加载的时候页面会跳。你可以在CSS里这么写:
css复制.ad-slot {
min-height: 250px;
display: flex;
align-items: center;
justify-content: center;
background: #f7f7f7;
}
@media (max-width: 768px) {
.ad-slot {
min-height: 120px;
}
}
min-height的意义是占位:广告还没加载时,这个区域已经保留了空间,页面不会上下跳动。背景色是给用户一个视觉反馈:“这块有内容,正在加载”。如果你不预留空间,广告一到就顶一下,阅读中的用户会很难受。
第二,广告位尽量放在<aside>内部,别直接裸放在<main>里。有些开发者图省事,在正文中间直接塞一个广告<div>。这在技术上没问题,但语义上有代价:正文中间插广告,爬虫和读屏软件很难区分“这是正文还是广告”。如果你非得在正文中间放广告,至少包一层<aside>,让工具知道这是“插入的补充内容”。
第三,如果广告位本身又是一个大的广告容器,比如整页是“专题合作页”,那整个容器也可以提升为<article>,因为那条广告本身就是一个独立内容。但这属于极端情况,普通站点不用想这么远。
3.3 动态广告与垃圾内容的处理
现在很多广告位是动态加载的,也就是页面先渲染,然后广告脚本异步填充内容。这就带来一个问题:如果广告脚本出错,或者广告内容被拦截器拦截,你的<aside>里可能是一大片空白甚至一堆错误提示。我的建议是给广告位做一个“空态设计”。其实就是判断广告容器有没有内容,没有就隐藏整个区块,别留一个带着背景色的空框在页面上。用JavaScript几行就能做:
js复制const adSlots = document.querySelectorAll('.ad-slot');
adSlots.forEach(slot => {
// 延迟到广告脚本执行完后再检查
setTimeout(() => {
if (slot.innerText.trim() === '' && slot.querySelectorAll('iframe, img, ins').length === 0) {
slot.style.display = 'none';
}
}, 2000);
});
这段代码有个注意点:2秒延迟不能太短,太短了广告还没填充进去,页面刚加载完广告位就被隐藏了;太长又会闪一下空白。2秒是经验值,你可以根据广告联盟脚本的加载速度微调。另外检查条件里我加了iframe, img, ins,因为有些广告代码是这种元素承载的,只判断文本会漏掉。
还有一类问题:广告位里偶尔会出现诱导点击、伪装成关闭按钮的引导,这是广告主素材的问题,不是你标签的问题。但从技术角度,你可以在接入广告时约束素材规范,或者通过title属性给广告位添加提示文字,让辅助技术能识别异常内容。这块跟HTML语义关系不大,但跟“广告位健壮性”有关系,顺带提一嘴。
4. 正文与广告的布局与样式区分技巧
语义是骨架,样式是皮肉。用户看到的还是视觉布局,所以这一节我讲三个和“正文与广告视觉区分”最常打交道的布局、交互问题。
4.1 两侧栏广告的三种布局实操
最常见的是左右两侧栏。老办法是浮动或者position: absolute,现在清一色用Grid和Flexbox。我推荐Grid做整页骨架,Flexbox做模块内部排列。
一个经典的双侧栏布局:
css复制.page {
display: grid;
grid-template-columns: 260px 1fr 260px;
gap: 20px;
max-width: 1200px;
margin: 0 auto;
}
.page__left-aside {
grid-column: 1;
}
.page__main {
grid-column: 2;
}
.page__right-aside {
grid-column: 3;
}
配合HTML:
html复制<div class="page">
<aside class="page__left-aside" role="complementary" aria-label="左侧广告">
...
</aside>
<main class="page__main">
<article>正文内容</article>
</main>
<aside class="page__right-aside" role="complementary" aria-label="右侧广告">
...
</aside>
</div>
这里要注意:<main>和<article>的嵌套关系。<main>是页面主区域的语义容器,一个页面只能有一个;<article>是内容容器,可以有多个。正常项目里<main>包<article>,完全没有问题。如果你用了grid-template-columns: 260px 1fr 260px,在窄屏上要记得做响应式适配,把grid-template-columns改成单列,侧栏广告移到正文下方。
我踩过的一个坑是:两侧栏加正文,三列布局在桌面端很漂亮,但一旦在移动端没处理好,260px的侧栏会在正文下面挤成一团。你的CSS要这么补一刀:
css复制@media (max-width: 900px) {
.page {
grid-template-columns: 1fr;
}
.page__left-aside,
.page__right-aside {
grid-column: auto;
}
}
这样移动端会变成正文优先,然后左广告、右广告依次排在下面。如果你想在移动端优先展示右侧栏广告,可以把order属性加上,控制视觉顺序,但记得DOM顺序还是保持正文在前。
4.2 sticky广告位与滚动冲突处理
广告位喜欢用position: sticky跟着用户滚动,这本身没问题,但有个典型冲突:当广告位在一个高度很短的容器里,或者容器父级有overflow: hidden时,sticky就不动了。
先说overflow: hidden。这是个非常阴的坑:父级如果有overflow: hidden,sticky子元素只能在父级可视范围内粘住,超出父级边界就会跟着滚走。很多前端写了半天sticky没效果,一查发现是某个父级模块为了圆角、清除浮动加了overflow: hidden。解决办法是去掉那个overflow,或者把sticky元素往上挪一层。
再有一个问题是sticky和flex的组合。在Flex布局里,align-items的默认值是stretch,子元素会被拉伸成跟容器一样高,sticky的“粘住”效果有时就失效了。你可以在广告容器上加一个align-self: flex-start,让它的高度不被拉伸,然后再配合top: 20px工作。完整示例:
css复制.sticky-ad {
position: sticky;
top: 20px;
align-self: flex-start;
}
还有一个经验:sticky的top值要大于头部导航的高度,否则广告粘到顶部时会被导航盖住。我一般取导航高度加10到20px的余量,让广告和一个固定头部之间保留一道缝,视觉上不打架。
4.3 广告位与正文的视觉区隔
语义上分清了,视觉上也得让用户一眼就能判断出“这是广告”。我的原则有两条:广告不要伪装成正文,广告也不要丑到刺眼。
具体做法可以参考一个通用方案:背景色用浅灰或浅黄,四周加一条细虚线边框,左上角放一个“广告”标签。这个标签我在3.1已经提过了,视觉上也要配合。我通常给.ad-slot__label写成小的圆角胶囊样式:
css复制.ad-slot__label {
display: inline-block;
margin-bottom: 8px;
padding: 2px 8px;
font-size: 12px;
color: #999;
border: 1px solid #ddd;
border-radius: 10px;
}
很多人担心“广告太明显了没人点”,但从实操看,正儿八经标注“广告”的区域,点击率不一定高,但转化来的用户更精准,广告主也更满意。那些靠混淆视觉引来的误点,投诉和退款比什么都快。
正文这边,恰恰相反,要尽量干净。正文的字号、行高、段落间距都要和广告位形成对比。比如正文用16px字号、1.7倍行高,广告素材通常是图片或富媒体,本身视觉重量就高,你再加粗加边框就喧宾夺主了。做一个简单的“正文规范类”也很有用:
css复制.article-body {
font-size: 16px;
line-height: 1.75;
color: #2c2c2c;
max-width: 720px;
}
.article-body p {
margin: 1em 0;
}
给正文设置max-width一是提升阅读体验,二是避免广告位和正文抢宽度。——对,很多广告位其实就是“正文写完剩下的一小块”,你不给正文限制宽度,广告位就会被挤得变形。
5. 常见问题与排查技巧实录
前面是正面讲法,这节专门说反面的、容易踩的坑。我是把这些年做过的广告位改造项目里总结出来的问题,一条一条列出来。
5.1 为什么广告总是被拦截
有读者问过我:明明我用了<aside>,广告还是会被拦截,是不是语义化没用?
这里要理清拦截逻辑。你页面里如果直接放的是第三方广告联盟的脚本,比如请求一个adservice.xxx.com的JS文件,那拦截器在浏览器发请求之前就拦截掉了,跟你的HTML怎么写没有半点关系。<aside>是让你的广告在DOM结构上“像广告”,但拦截器判断广告不只看DOM,它看网络请求、看资源域名、看行为模式。
那语义化就没价值了吗?不是。一个页面如果广告位语义清晰,用户可以通过扩展规则更精准地屏蔽它;搜索引擎也能更好地把它从正文权重里剥离;读屏用户能快速跳过。所以语义化的目标不是“防止被拦”,而是“被拦的时候别连累正文”。
我在实际排查时遇到过一种情况:广告位容器里没有广告内容时,某个第三方脚本还会去请求一个统计接口,导致广告位被拦截之后页面一直报错。解决方法是给广告位加一个全局开关配置,当广告内容为空时,直接不加载对应的脚本。这属于页面健壮性问题,但经常被误以为是广告被“拦坏了”。
5.2 aside会不会影响SEO
这是个很实在的顾虑。我直接给结论:正常使用<aside>不会对SEO产生负面影响,反而可能有好处。
搜索引擎爬虫解析页面时,会对不同语义区域做区分处理。你把广告放在<aside>里,等于告诉爬虫“这里不是核心内容,别把权重算进去”。这其实是合理隔离:页面主题由<article>里的正文支撑,主题关键词自然就聚焦了,不会因为侧边栏广告文案里有别的关键词而分散页面主题。
有一种情况要注意:<aside>里如果放了大量“推荐阅读”的链接,通常带nofollow或者不加,这没问题。但如果<aside>里放了大家默认会爬的正文链接(比如文章内的相关阅读),你要确认这些链接指向的是有效页面,别做了一堆404的死链。死链对SEO的伤害比放广告大得多。
另外,不要在<aside>里放隐藏文本——比如颜色跟背景一样、字体大小0、display:none的SEO关键词。搜索引擎对隐藏文本是零容忍的,轻则降权,重则整站处罚。想做SEO,请把精力放在正文质量和站内结构上,不要走这些歪路。
5.3 屏幕阅读器咬文嚼字的问题
有一次我在测试时发现,屏幕阅读器会把我广告位里的图片alt文本一字不落地读出来。如果广告素材的alt是“点击购买超级优惠仅限今日”,读屏用户就会听到一条广告在里面吼。这个体验非常差。
解决办法是,广告素材的alt文本尽量精简,或者如果是纯装饰性的图片,用空alt加上aria-hidden="true"。这样读屏软件会跳过它:
html复制<aside class="ad-slot" role="complementary" aria-label="广告">
<a href="https://example.com" rel="sponsored nofollow">
<img src="promo.jpg" alt="" aria-hidden="true">
</a>
</aside>
但你要注意:如果这个图片是用户唯一能获取内容的途径,就不能用空alt。比如一个推广产品的图片,产品名称和关键信息你要写在alt里,否则视障用户完全不知道广告讲了什么。我通常的做法是:广告是有实际意义的,就写简短描述,比如“某品牌手机新品预售广告”;广告只是氛围渲染,就空掉alt。
还有一种情况是广告位里有动态加载的内容,读屏软件可能读不到新内容。这个问题不在HTML标签上,需要你配合ARIA的aria-live属性设置。但广告位加aria-live有个风险:广告如果频繁刷新,读屏用户会一直被插播新内容,反而更烦。我的建议是不要给广告位加aria-live,广告信息的更新不需要实时播报给用户,给用户一个有aria-label="广告"的区域就够他们主动浏览了。
5.4 移动端广告位布局翻车
移动端广告位的问题,十个里有八个是尺寸和加载顺序。
常见的一幕:移动端屏幕宽度只有375px,但设计稿里广告位是728x90的PC横幅,结果一缩放,广告就变形了。正确做法是给广告位容器做多尺寸适配,用媒体查询控制宽高:
css复制.ad-slot {
width: 100%;
min-height: 90px;
}
@media (max-width: 480px) {
.ad-slot {
min-height: 60px;
}
}
不要试图用transform: scale()去缩放广告内容,那样会让广告清晰度下降,而且占位高度不对,页面跳动更严重。
加载顺序也容易翻车。移动端网络不稳定,一个几MB的广告素材如果放在正文前面,会卡住正文渲染。你可以把广告位放在正文后面,然后用JavaScript把广告位移动到正文前面(如果你确实想让它靠前展示)。逻辑是:正文先出来,广告慢慢加载。这种方式叫“后加载前置渲染”,核心代码是用requestAnimationFrame或setTimeout延迟插入广告容器。
html复制<div id="ad-placeholder"></div>
<script>
setTimeout(() => {
const adContainer = document.createElement('aside');
adContainer.className = 'ad-slot';
adContainer.setAttribute('role', 'complementary');
adContainer.setAttribute('aria-label', '广告');
adContainer.innerHTML = '广告内容';
document.getElementById('ad-placeholder').replaceWith(adContainer);
}, 1500);
</script>
这个1.5秒是给正文渲染让路的时间,实际值你可以根据页面性能调整。你要知道一点:广告哪怕晚一秒出现,也比让用户对着白屏等广告好。这个顺序问题我在好几个资讯类项目里用过,实测对首屏渲染时间改善很明显。
6. 写给最后落地的几个建议
聊了这么多,最后再补充几个我在实际项目中的感受。
第一,语义化不是一天做完的。如果你的老项目里已经有大量<div class="ad">,不用推倒重来,可以逐步换成<aside>和ARIA属性。改的时候注意一次改一个模块,改完用读屏软件或者浏览器扩展测一下,确认没有引入新的问题。这个过程不需要太久,关键是别想一口吃成胖子。
第二,代码写完之后,建议用HTML验证工具检查一下。W3C的Nu Html Checker可以帮你找出标签嵌套错误、ARIA属性冲突这些低级问题。尤其要注意role="complementary"和<aside>的隐式语义是否冲突,有时候老旧代码里残留的role是main或者banner,会覆盖标签语义。
第三,多和运维、广告投放的人沟通。前端把广告位语义化做好,运营那边替换广告素材时也会更顺手。你可以给出一个简单的广告位接入规范文档,写清楚:广告素材的图片要带描述性alt,链接要加rel="sponsored",容器不要随意改成display:none。文档不用很长,能让运营少踩坑就行。
最后一个小技巧,给你留个压箱底的:在开发环境里把广告位全隐藏,专注测试正文的可访问性。等正文部分完美了,再打开广告位,一点点排查广告对页面性能、滚动体验和读屏顺序的影响。这样一步步来,页面质量和广告收入是可以兼顾的。
我最初入行的时候,也觉得HTML标签怎么用不影响大局,反正浏览器都能渲染。后来被一个接一个的线上问题打脸,才明白语义化不是玄学,是工程师给机器写的注释。机器读懂了,人才能省心。希望这篇内容能让你少走一点我走过的弯路。
