最新要闻
- 腾势N7盲订破2万台 增换购用户55%来自BBA等豪华品牌
- 世界短讯!特斯拉充电标准一天内被四家公司接受 网友:马始皇?
- 学习Win12好榜样?国产OS系统deepin成立AI社区:未来更智能_世界热头条
- 69元 小米车载充电器特惠开售:100W快充 配有炫彩灯效 时讯
- 环球热讯:历史性变革 大众汽车计划进行数十年来最大规模重组
- 年仅52岁!知名男演员在睡梦中去世... 今日热讯
- 国内第四大运营商 中国广电终于支持携号转网:但只入不出
- 刚考完科目一就买车上路 男子:想提前预习科目二、科目三
- 还说5G没用?全国5G平均下行速率348Mbps 比4G快6倍
- 环球视讯!被动散热靠谱么?博主实测15英寸MacBook Air性能表现
- 【新要闻】蔚来号召员工为车主献血 称“伙伴们都在积极报名”
- 焦点速讯:洛阳老城区:拆违治乱顺民意
- 天天微资讯!2023油价调整窗口时间表一览
- 马斯克 一统海外充电江湖!
- 新增自动拍摄功能:尼康Z9获4.00版本固件更新
- 90后小伙中1000万大奖 淡定回应先去上个班:不着急领
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
焦点速看:用纯HTML,JS,CSS实现横向滚动标签页
前言
前不久,在我的一个项目中,需要展示一个横向滚动的标签页,它支持鼠标横向拖动和点击切换。在实现的过程中,我发现这个小功能需要同时用到前端的三辆马车,但是实现难度不高,而且最终效果还不错,是个难得的初学者项目,于是萌生了写这篇文章的想法,希望对初学者有所帮助。同时为了避免初学者学习框架,我打算用纯原生的方式实现它。
我们最终的效果应该类似于下面:
(资料图片仅供参考)
需求分析
需求分析就是细化我们需要完成的功能,某个功能的完成需要哪些技术的参与。对于初学者,需求分析至关重要,它可以帮助我们理清思路,找到解决问题的突破口,所以应该引起足够的重视。以本篇目标为例,标签页的需求分析就可以像下面这样:
- 我们的展示主体是标签页,HTML就是实现主体的主要技术;
- 标签页需要可以拖动和点击,这涉及到鼠标事件的监听和处理,是JS的主场;
- 既然标签页可以拖动了,那是否要隐藏那个丑陋的滚动条,加个活动指示器,给鼠标变一个样式?很明显,这些都是CSS的优势。
如上,通过对展示,操作,样式的划分,我们进一步明确了HTML,JS,CSS需要完成的工作,甚至连实现都明朗了,所以对需求拆分得越详细,对实现就越有掌控力。
基本框架
对于前端来说,HTML始终是万物之源,所以一言不合先构筑个标准的HTML页面总是没错的。为了便于演示,我将所有的内容都放在一个HTML文件中,文件结构如下
Tab演示 <script type="text/javascript"></script>
这里和以往不同,我将script
放到了最后,这是因为我想在写脚本的时候,页面标签直接可用,减少对页面加载的监听,降低复杂性。
实现基本功能
有了基本结构,下一步当然是画页面啦。从效果图中不难看出,页面主要包括一个一个的选项卡,对于HTML来说,这不就是列表嘛。于是,突破口就出现了,我们先往HTML里面加入列表
- 肖申克的救赎
- 霸王别姬
- 阿甘正传
- 泰坦尼克号
- 这个杀手不太冷
- 美丽人生
- 千与千寻
- 辛德勒的名单
- 盗梦空间
- 忠犬八公的故事
于是,我们有了原始的标签页。但是标签页是竖向的,并且有着丑陋的小黑点,不符合需求。发现了这些问题,下一步当然解决这些问题了,这当然就是CSS的强项啦。首要问题就是让列表横过来。横过来就是改变了元素的相对位置,也就是对应CSS的布局功能。那说起布局,CSS的布局方式有很多,像float
,position
等等。标签页是横向多个紧密排列的,一个挨着一个,这当然是用flex
啦。至于讨厌的小黑点,这是新东西,需要百度一下。查阅文档发现,ul
有个属性list-style-type
,只需把它设置为none
就可以去除小黑点。此时,页面上的所有选项卡都紧密排列了。为了让它更像一个选项卡,需要给它居中,限制一下宽度,加个背景色,加点padding。下面就是改完样式的代码
ul{display: flex;justify-content: center;align-content: center;list-style-type: none;background-color: #2397f3;width: 600px;overflow-x: scroll;}li{padding: 16px;flex-shrink: 0;}
值得注意的地方有两点。在ul
的样式中,由于给ul
加了宽度限制,导致它的内容超出了内容区,所以要给ul
加上overflow-x
的属性。同样由于宽度的原因,flex
子项在宽度不够的情况下会默认缩小,表现在标签上就是文字换行啦,flex-shrink: 0;
就是让子项保留原有大小。此时,再来刷新页面,可以看到选项卡的基本雏形已经出来了。虽然简陋,但是可以拖动滚动条左右滚动了。下一步,我们的目标就是去除这个丑陋的滚动条。网上搜索一番,发现火狐,IE和Chrome的方式不尽相同,为了兼容性,我们就都给写上。
ul{scrollbar-width: none; /* Firefox */-ms-overflow-style: none; /* IE 10+ */}ul::-webkit-scrollbar { display: none; /* Chrome Safari */}
滚动条去除后,UI好看了,但是新问题出现了——选项卡滚动不了了。别着急,下一步就是添加鼠标拖动功能。
实现交互
在浏览器中,HTML标签有对系统事件的监听能力,响应这些事件,可以使页面实时响应用户的操作。通过对不同的事件的组合,可以实现各种丰富、有趣的功能,标签页也一样。
标签页的首要功能是可以随着鼠标的拖动而滚动元素,那么,首要任务就是监听鼠标的移动事件啦。但是光监听移动还不行,因为通常来说,用户在鼠标左键按下后才希望真正拖动,鼠标左键抬起后结束拖动。所以,这个拖动动作其实需要组合鼠标按下(mousedown
),移动(mousemove
),抬起(mouseup
)三个事件。那么这三个方法加在哪,怎么加呢?
在Web API中,JS操作HTML的入口点是Document
对象,Document
提供了操作(增删改查)HTML元素的API。这一过程是有标准流程的。
- 通过
Document
查找目标元素; - 对目标元素进行元素,样式变更等操作;
- 变更完成;
这一过程是重复且繁杂的,为了减少编写这样的样板代码,加快开发速度,一大堆前端框架应运而生。所以,在学习前端框架时,牢记这一基本步骤,有助于快速理解框架的运行原理。毕竟无论框架怎么变,最终都是要落实到这一过程上。
算法明确后,接下来就是具体实现。
查找目标元素
在查找目标前,需要首先明确目标是谁。用户肯定不希望在页面的其他地方拖动鼠标,标签页跟着滚动了,这很奇怪。所以我们的目标元素应该是无序列表。那么,怎样通过Document
知道无序列表呢,查阅Document
的API,发现它有个querySelector
的方法,这个方法会从上到下查找满足条件的选择器,并返回第一个满足条件的元素,参数则是选择器的名称。上面已经明确过我们的目标是无序列表,所以查找目标元素的最终代码如下
const ul=document.querySelector("ul");
为列表滚起来
每一个HTML元素,在JS中都是Element
的对象。上一步我们已经得到了一个Element
对象ul
,注意,这里的ul
对象和ul
标签不尽相同。一个是JS的对象对HTML标签的表示,一个是HTML标签。现在有了一个对象,那么就可以通过调用合适的方法来操作这个对象了。通过查阅Element
对象的API,发现它有个addEventListener()
的方法,这个方法可以完成该对象表示的HTML标签对某些事件的监听。这个方法接收两个参数,第一个参数是事件名称,这在上一节已经说过。第二个参数则是对这个事件的处理,这也是我们实现魔法的地方。
首先,在用户按下鼠标左键后,开始记录鼠标移动情况。在鼠标左键抬起后,停止记录。所以按下和抬起的主要功能就是维护记录开关,控制标签滚动的动作得在鼠标移动的回调里处理。
但在真正写逻辑前,还有两个问题没有处理。1、怎样让标签滚动?2、滚动的逻辑怎样写?问题一当然需要查阅Element
的API啦。搜索滚动相关的,发现两个相关性比较大的方法——scrollBy()
,scrollTo()
,都可以滚动内容。唯一的区别是前者的参数是滚动的偏移,后者是最终值。由于鼠标移动是一点一点的,所以选择前者会更方便一点。确定了方法,也就解答了问题一。对于问题二,简单来说就是怎样提供问题一所需的参数。scrollBy()
需要两个参数,横向和纵向的滚动偏移值,由于我们只希望标签页可以横向滚动,所以纵向的偏移始终是0,那么横向的呢?通常事件回调都会传递一个事件对象,称作MouseEvent
,我们去查查事件对象的API,发现里面带有好几个关于坐标的属性——clientX
,movementX
,screenX
。movementX
直接就满足我们的需求,它代表上一次鼠标移动到这一次移动间的偏移,而刚好scrollBy()
需要的参数就是偏移,妥了。综上,得出以下代码
const ul=document.querySelector("ul");let isMouseDown=false;ul.addEventListener("mousedown",(e)=>{isMouseDown=true;})ul.addEventListener("mousemove",(e)=>{if(isMouseDown){ul.scrollBy(-e.movementX,0);}})ul.addEventListener("mouseup",(e)=>{isMouseDown=false;})
可以看到,在mousemove
的处理上,偏移加了个负号。因为在HTML页面中左上角为坐标原点,右边为X轴正方向。一直往右,则X坐标是增大的,而movementX
的值是当前鼠标坐标与上一次坐标点的差值,上一次肯定比这一次小,两者的差值肯定是正值。基于同样的原因,scrollBy()
参数正值代表增大X值,也就是显示右边的内容,隐藏左边的内容。两者结合的效果就是,鼠标往右拖,标签页右边隐藏的内容展示了出来,这和直觉相悖。通常我们希望鼠标往右拖,页面展示左边的内容,隐藏右边的。基于这样的分析,我们需要给movementX
的值取反。
显示当前选中的标签页
现在,标签页可以滚动了,但是还不能选中。我希望点击某个标签时,标签下方出现一个小横条表示选中状态。很明显,显示小横条是一个CSS的问题,而点击标签切换小横条是JS的问题,这一次我们需要同时处理JS和CSS的问题。
首先来显示小横条。显示小横条有两个思路,一种是在HTML中搞个div
标签,另一种是使用::after
伪元素。我选择后一种,这样可以保持HTML的干净。接下来需要确定小横条的样式
- 覆盖在选中的标签上
- 位置是标签底部
- 和标签一样长
我们知道正常的HTML文档流是从左到右,从上到下的,新加的元素会追加到已有元素的右边或者下边。小横条需要覆盖在标签上,那么就要改变这一默认行为,position
属性就是实现这个功能的关键。absolute
,fixed
都可以脱离正常文档流,使元素覆盖在祖先元素上,不同的是前者是相对于最近的定位祖先,后者是相当于视口的。小横条是跟随着标签显示的,显然要使用前者。确定了位置,还有大小和样式。既然使用了绝对定位,那么bottom
,"left"
,right
相应就能限定它的位置和大小了,小横条的样式就直接用border-bottom
吧。于是,小横条的样式就出来了
.current::after{content: "";position: absolute;border-bottom: 4px solid #FFC109;border-radius: 2px;bottom: 0;left: 0;right: 0;}
结束了吗,还没有!使用了绝对定位,必须时刻记得给绝对定位的元素找个锚点,也就是参照,不然top
,left
,right
,bottom
去参考谁呢?那么怎样告诉绝对定位的参照物呢,还是position
属性。只不过这一次它要出现在参照物的CSS里面。而由前面的样式分析,小横条始终跟着标签页走,也就是说小横条的参照物就是标签页。所以,还要在标签页的样式上加上position
的属性。当然,为了区分更明显,我还改变了一下颜色。
.current{color: white;position: relative;}
至此,小横条可以正常显示出来了。
小横条跟随鼠标点击显示
有了前面拖动功能的经验支持,这一次轻车熟路了,鼠标点击某个标签页,小横条显示在对应的标签页下方。这一次事件的对象变成了单个标签页,所以点击事件要加在单个标签页上。但是这一次标签页太多了,我们不能还是按照之前的查找-设置方法,这样太繁杂了。巧合的是,前面我们已经得到了ul
对象了,通过它的children
属性,可以得到所有的li
,这不就妥了吗。小横条要切换到不同的标签页上显示,也就是小横条这个样式要根据点击对象的不同而动态增加或者删除。查阅Element
的API,发现有个className
的属性,改变它的值就可以增减样式了。
let last=null;for(let l of ul.children){l.addEventListener("click",(e)=>{if(last){last.className="";}e.target.className="current";last=e.target;})}
代码的实现中,多了个last
对象。因为通常标签页只能同时选中一个,当新的标签页被选中之后,上一个选中的标签页应该恢复原始样式,这就是last
对象的作用。我们先取消选中上一个元素,然后再选中当前点击的对象,这样就完成了小横条跟随点击选中的效果了。
总结
总的来说,这个项目的难点不在于实现有多难,而是新。很多初学者,面对这种新问题往往束手无策,找不到切入点。本篇尝试以例子的形式,以初学者的思维方式分析需求,拆解问题,提炼方法,最终解决问题。从最朴素的直觉出发,引导思考,找到一条易于接受和理解的方法。
所以,遇到新问题不要慌,对问题拆解后,看能不能找到突破口,如果找不到,再从涉及到的几个主要对象中寻找灵感,通常都会有所收获。最后就是多逛逛MDN,关键时刻真能派上大用场。
最后,情人节快乐,祝有情人终成眷属!
参考
- [1] 使用CSS隐藏元素滚动条
- [2] Element
- [3] 事件参考
- [4] MouseEvent
关键词:
焦点速看:用纯HTML,JS,CSS实现横向滚动标签页
CSS常用属性
腾势N7盲订破2万台 增换购用户55%来自BBA等豪华品牌
世界短讯!特斯拉充电标准一天内被四家公司接受 网友:马始皇?
学习Win12好榜样?国产OS系统deepin成立AI社区:未来更智能_世界热头条
69元 小米车载充电器特惠开售:100W快充 配有炫彩灯效 时讯
环球热讯:历史性变革 大众汽车计划进行数十年来最大规模重组
年仅52岁!知名男演员在睡梦中去世... 今日热讯
国内第四大运营商 中国广电终于支持携号转网:但只入不出
刚考完科目一就买车上路 男子:想提前预习科目二、科目三
还说5G没用?全国5G平均下行速率348Mbps 比4G快6倍
环球视讯!被动散热靠谱么?博主实测15英寸MacBook Air性能表现
【新要闻】蔚来号召员工为车主献血 称“伙伴们都在积极报名”
焦点速讯:洛阳老城区:拆违治乱顺民意
【快播报】Collections类源码初探
天天微资讯!2023油价调整窗口时间表一览
马斯克 一统海外充电江湖!
新增自动拍摄功能:尼康Z9获4.00版本固件更新
90后小伙中1000万大奖 淡定回应先去上个班:不着急领
看热讯:阵容强大!长城汽车17位高管集体入驻微博:吉利副总裁盛赞
史低价!小米米家无线洗地机2开启618预售:仅1799元-当前视点
视点:过山车行情后,建筑钢材市场行情或易涨难跌
今日精选:Koordinator 最佳实践系列:精细化 CPU 编排
天天观察:Android RIL&IMS源码分析
天天即时:5款超级好用的开发效率工具,建议收藏!
世界观点:记录--为什么推荐用svg而不用icon?
油价小幅下调 加一箱油将少花2元 全球最资讯
惠誉博华:48家银行未赎回二级资本债券,合计达364.8亿元
恒生指数收涨0.6% 恒生科技指数大涨重回4000点上方
中海达(300177)6月13日主力资金净买入525.51万元_天天通讯
PMR机械硬盘走到尽头了!西数/希捷正疯狂自救
6旬老人守株待兔式飞奔撞车碰瓷索赔 车主吓坏:监控还清白 环球精选
广东有多热?网友在广东买虾 还没到家就熟了 世界今日讯
美国再将31家中企列入“实体清单”!
2999元的国产显卡值不值得冲?实测3A大作给你答案
焦点信息:天津:“多卡合一”创新服务“城市小蜜蜂”
今日热文:线段树学习笔记
每日速看!尚医通-day10【微信扫码登录】(内附源码)
linux iptables安全技术与防火墙_快播报
收评:创业板指涨0.68%收获三连阳 半导体行业涨幅靠前
全球快报:小米卢伟冰:小米13 Ultra在意法西德及香港地区正式开售 销售超预期
首发预装鸿蒙OS 4.0!华为Mate60 Pro概念图出炉 全球热资讯
天天精选!NASA决定造访遍地黄金的“灵神星”:平均每位美国人能分300亿美元
美系硬派SUV福特探险者谍照曝光!外观内饰全面升级 预计将在年内首次亮相_今日热讯
今日热搜:8GB显卡卖到3199元 显存成本曝光:英伟达实在太赚了
视点!男生抠掉脸上痘痘流血近1小时:用了一包400张抽纸
每日报道:6月13日华鲁恒升尿素价格暂稳
焦点信息:市场监管总局:瞄准先进材料、人工智能等领域推动建立国家标准参考数据中心
烟台大学附属中学石明校区举行垃圾分类科普讲座|天天时快讯
速递!8GB内存笔记本卖到10499元起 苹果被批吃相难看:应该破发
货车高速上连续疯狂别车被撞停 官方:未造成伤亡、已找到肇事者|当前播报
天天热讯:马斯克相中的男人!14岁成SpaceX最年轻工程师、岗位年薪百万
环球即时看!小米平板6 Pro两个月使用心得:找不到短板的安卓板皇
Arm发布全新智能视觉参考设计 首次整合第三方IP核心
2023安洵杯 re复现
每天一道面试题:Spring的Bean生命周期
Axure RP教程_编程入门自学教程_菜鸟教程-免费教程分享_环球今日讯
环球今日报丨哥伦比亚4名空难获救儿童的母亲生前或遭家暴,孩子外公和父亲欲争夺抚养权
当前关注:你收益多少?余额宝上线第十年:每天为国人赚1亿零花钱 网友狂晒单
排队5小时!浙江网红面包黄牛加价上百元 网友吐槽:消协回应
【当前独家】中轴线文化遗产有了常设讲堂
三种方法让.NET轻松实现Excel转PDF
天天快看点丨docker-compose搭建wordpress
【播资讯】比亚迪执行副总裁:美国市场不在我们考虑范围内
石家庄迈入“刷脸”乘车时代:买一根火腿肠就能免费坐地铁活动结束了_环球动态
制作成本16.5亿!《封神三部曲》第一部7月20上映:角色海报公布 太强大 今日报
吹牛还是玩真的?丰田下一代电动汽车续航达1500公里
【天天报资讯】小米发布米家旅行箱:顶部嵌平设计 行走的小桌板
市场监管总局:到2035年 计量数据归集共享规模显著提升-快播
【技术积累】软件设计模式中的工厂模式【一】-独家
STM32F429 Discovery开发板应用:使用FreeRTOS队列+DMA双缓存实现串口数据接收
【寻味中华丨饮食】蔡甸藕带:白若玲珑玉 丝缕皆故乡
【天天时快讯】699元!XREAL Beam投屏盒子发布:随身携带的“可悬停AR空间屏”
AMD今晚发布新CPU Intel急了:至强性能比EPYC快7倍
【世界新视野】4-1战胜热火!掘金队夺队史首个NBA总冠军:网友发帖祝贺 约老师太强
小区门口连装8条减速带 物业回复让业主无语:为防业主逃费
贵1000元值不值?i7-13700H和i5-13500H对比实测 世界观点
我在塞尔维亚寻找约基奇-每日速递
全球微速讯:“铁榔头”郎平重返中学校园,为学弟学妹成长“支招”
世界最新:深度学习应用篇-推荐系统[11]:推荐系统的组成、场景转化指标(pv点击率,uv点击率,曝光点击率)、用户数据指标等评价指标详解
flutter 日志打印三種方法
最新:Linux根文件制作
热推荐:一对一直播源码平台搭建的关键条件,成败在此。
真刑!几行代码端了整个教务系统。。
启明星辰(002439)6月12日主力资金净卖出1310.11万元
长安欧尚Z6新能源半年降价3万多被集体投诉 车主:坑惨我们了
每日热讯!2折!115网盘618大促:10年VIP只要1000元 赠100TB空间
全脂/低脂可选:特仑苏纯牛奶2.7元/盒抄底(商超6元)
腾势N7赛道远超宝马X3 赵长江:意向客户看到展车后几乎全下单了
Fold5、Flip5换壳!三星W24系列折叠屏手机通过认证:25W快充
吴尚垠 吴尚_每日消息
JAVA非递归生成无限级菜单树的较简代码实现。(非泛用型工具包,仅总结逻辑)
每日关注!低代码开发平台为数智赋能,让开发变得更简单
奶我一口是什么意思网络用语_奶你一口是什么意思简介介绍
腾讯祭出的大招《无畏契约》 能不能成为下一个《英雄联盟》?-环球微速讯
环球观点:最大内存+最美拍照手机!小米Civi 3 1TB上市:2999元
当前消息!满级玩家有盼头了 暗黑世界V等你来
28.98万起 智己LS7都市版上市 CEO刘涛:现在买增程过几年就会焦虑 每日热点
男子洗澡被闯入的两匹“狼”吓坏 经辨认是阿拉斯加 焦点精选
C天键(301383)6月12日主力资金净买入2791.69万元 焦点快看