project from jirengu.com fangyinghang Frontend class
技术栈
- jQuery
- LocalStorage
- SVG Symbols
- 媒体查询
- git&GitHub
思路
Figma作图,在线制作UI
实现手机端
- 写HTML
- 写CSS
- 写JS(事件监听、DOM操作)
实现PC端
- 通过媒体查询加CSS
- 写JS(单独处理PC上的逻辑)
发布到GitHub
注意用户体验,效果展示:预览网页链接放在明细位置,方便快捷查看。
加README.md
开发
yarn global add parcel-bundler
parcel src/index.html
build命令
yarn build
实现全程:
- 页面控件创建
1.1 登陆Figma网站,创建新的设计文件,先设计再开发,新建2个Frame,分别为手机端和PC端,用画图形工具,画出搜索输入文本框,自定义宽高,背景色填充白色#fff,边框border改为#ddd浅灰色,然后改整个页面的背景色#eee浅灰色,不要自己擅自配色,用固定模型。
1.2 按住alt键,点击已创建的文本框,向右拖动为button按钮,复制得到一个容纳button的文本框,边框去掉,背景填充为蓝色,再加上文字:搜索,文字填充白色;
1.3 把button上的文本框、文字创建一个组,可以整体操作,
1.4 创建网站导航的框,大小一般设置为8x的像素,白色背景色,边框填充#ddd浅灰,新建文字。
1.5 复制新建的导航框组,插入图片,缩放比例时,按住Shift,保证图片不变形;
1.6创建PC页面的控件,可以把手机端刚才做好的样式,直接拖拽到pc页面中,通过选中再alt复制出想要的效果,pc端和手机端的视觉页面就都创建完成。
- html代码实现(与css的顺序是,代码简单可以全部写完html,代码复杂,可以先写一部分html再写css,如此循环)
2.1 新建一个项目文件夹,vscode打开,新建src文件夹,index.html文件和style.css文件,main.js文件,把head元素中的meta viewport替换为禁止触屏缩放的效果代码:
<meta name="viewport" content="width=device-width,initial-scale=1,
minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
2.2 写html的大致思路是,根据视觉稿来添加需要的元素,head元素,改title,加style链接:
<head>
<title>前航 - 前端导航网站</title>
<link rel="stylesheet" href="style.css">
</head>
2.3 body元素添加js链接:
<script src="main.js"></script>
2.4 body里面加两个子元素
/,header里面有input和button两个标签,main里面存放网站;main里面放ul(无序列表):<main class="globalMain">
<ul class="siteList">
<li>
<div class="site">
<div class="logo">A</div>
<div class="link">acfun.cn</div>
</div>
</li>
<li>
<div class="site">
<div class="logo"></div>
<div class="link">bilibili.com</div>
</div>
</li>
<li>
<div class="text">新增网站</div>
</div>
</li>
</ul>
</main>
3 css代码实现,前提是html上的需要加样式的标签,需要加class,上面的代码已经直接加上;
3.1 预览网站,新建终端,parcel src/index.html,打开链接。
3.2 先重置css,并全局声明:
/*css reset*/
/*不能出现太多的height,用padding来撑*/
*{box-sizing: border-box;} /*所有元素使用border-box*/
*::before,*::after{box-sizing: border-box;} /*所有的伪元素使用border-box*/
*{margin:0;padding: 0;} /*干掉默认的margin和padding*/
ul,ol{list-style: none;} /*干掉默认的ul和ol的Liststyle*/
3.3 把css选择器都添加上,层级结果可以不与html一致,只关心有没有层级关系,不关系具体什么关系,先确认body的范围,
body{border:1px solid red;}
如果给body加背景色,那么浏览器会只能的把背景色扩展到整个网页,就给body加一个灰色背景:
body{background:#eee;}
3.4 打开效果页面的源代码,并切换到手机端页面预览,开始给搜索输入文本框和按钮加样式,在globalHeader元素里:
/* style */
body{background: #eee;}
.globalHeader{
margin: 20px; /*偏移20像素*/
display: flex; /*flex布局*/
justify-content: space-between; /*input和按钮分两侧摆放*/
}
.globalHeader > input{
width:100%;
margin-right: 10px; /*input按钮有空隙*/
height: 40px;
padding: 0 10px; /*上下为0,左右为10px*/
border: 1px solid #ddd;
border-radius: 4px; /*input加圆角效果*/
}
.globalHeader > button{
white-space:nowrap; /*把搜索按钮强制变一行,也就是不换行*/
padding: 0 28px;
border: none;
border-radius: 4px;
background: #0282B9; /*在figma的button样式代码里找到*/
color:white; /*按钮上的文字搜索颜色*/
line-height: 18px; /*在figma的button样式代码里找到文字高度*/
}
3.5 接下来是网站导航框的平均布局,在main元素下,加一个html里面的.siteList,再新增网站html内的div.icon元素,使用iconfont图标:把图标加入购物车,在symbol内,自动生产代码,复制到html中:<script src="//at.alicdn.com/t/font_1473630_8pfq4zss4o8.js"></script>
```style.css
.icon{
width: 1em;height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.siteList .addButton{
border :1px solid #ddd;
background:#fff;
width: 160px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 20px 0;
}
.siteList .addButton .icon{
width: 56px;
height: 56px;
}
.siteList .addButton .icon-wrapper{
width: 64px;
height: 64px;
display: flex;
justify-content: center;
align-items: center;
}
<li>
<div class="addButton">
<div class="icon-wrapper">
<svg class="icon"> /*注意classname不要与其他的重复*/
<use xlink:href="#icon-add"></use>
</svg>
</div>
<div class="text">新增网站</div>
</div>
</li>
- js功能实现
4.1 搜索文本框功能实现,新建一个form表单,searchForm,
<header class="globalHeader">
<form class="searchForm">
<input type="text">
<button>搜索</button>
</form>
</header>
对应的css需要修改,因为header与input之间增加了一个标签form:
.globalHeader{
margin: 20px; /*偏移20像素*/
}
.searchForm{
display: flex; /*flex布局*/
justify-content: space-between; /*input和按钮分两侧摆放*/
}
.searchForm > input{
width:100%;
margin-right: 10px; /*input按钮有空隙*/
height: 40px;
padding: 0 10px; /*上下为0,左右为10px*/
border: 1px solid #ddd;
border-radius: 4px; /*input加圆角效果*/
}
.searchForm > button{
white-space:nowrap; /*把搜索按钮强制变一行,也就是不换行*/
padding: 0 28px;
border: none;
border-radius: 4px;
background: #0282B9; /*在figma的button样式代码里找到*/
color:white; /*按钮上的文字搜索颜色*/
line-height: 18px; /*在figma的button样式代码里找到文字高度*/
}
4.2 告诉浏览器,用户点击搜索按钮时,跳转到那个网页?用form表单构造请求;
<header class="globalHeader">
<form class="searchForm" method="get"
action="https://www.baidu.com/s"
>
<input name="wd" type="text"> /*word*/
<button type="submit">搜索</button> /*submit要加*/
</form>
</header>
4.3 对于Acfun静态跳转到网页如何实现?用a标签包住div标签acfun。a标签是内联元素,div是块级元素,正常来说块级元素不能放在内联元素里,但是a标签特殊,默认样式需要改:
a{color: inherit;text-decoration: none;}
<li>
<a href="https://www.acfun.cn">
<div class="site">
<div class="logo">A</div>
<div class="link">acfun.cn</div>
</div>
</a>
</li>
<li>
<a href="https://www.bilibili.com">
<div class="site">
<div class="logo"></div>
<div class="link">bilibili.com</div>
</div>
</a>
</li>
4.4 插入图片,在logo标签下,放img标签,把图片拖拽到src目录里,或者直接粘贴到src/images/b.jpg
<li>
<a href="https://www.bilibili.com">
<div class="site">
<div class="logo">
<img src=".\images\b.jpg" alt="">
</div>
<div class="link">bilibili.com</div>
</div>
</a>
</li>
调整img图片的高宽:
img{max-width: 100%;max-height: 100%;}
4.5 实现新增网站功能:用jQuery监听addButton的点击事件,jQuery的引入可以用两种方法:一种是直接下载jQuery.js文件放在src里面,另一种是bootcdn,搜索jquery,找到最新版本,后缀带min.js的,目前是3.6.0,复制标签:<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
4.5.1 测试jQuery引入成功:打印出来一个函数,不报错就引入成功。
console.log(jQuery)
console.log($)
4.5.2 测试是否有监听onclick,点击新增框,控制台打印1,说明正常。
$('.addButton')
.on('click',()=>{
console.log(1)
})
4.5.3 手机端登陆ip,查看效果。命令行ipconfig获取ip地址,如果ipconfig: command not found,则配置环境变量,我的电脑-属性-高级系统设置-环境变量,在系统变量中双击变量名为path的选项,将%SystemRoot%\system32;%SystemRoot%;加入到变量值的输入框中,win7的话,就在已添加的内容后面加分号;粘贴到里面,一路确定。
修改完后就可以使用了,重启cmder,在ipconfig里面找到无线局域网适配器 无线网络连接:IPv4 地址192.168.xx.xxx,用手机浏览器访问192.168.xx.xxx:1234 就可打开项目。
4.5.4 如果用户点击了新增网站,输入的网址不是正确的格式,怎么提示?
$('.addButton')
.on('click',()=>{
let url = window.prompt('请问你要添加的网址是啥?')
console.log(url)
if(url.indexOf('http')!==0){
url = 'https://'+ url
}
console.log(url)
})
4.5.5 初步实现新增的网站直接添加到页面中,注意用反引号实现多行代码,
$('.addButton')
.on('click',()=>{
let url = window.prompt('请问你要添加的网址是啥?')
console.log(url)
if(url.indexOf('http')!==0){
url = 'https://'+ url
}
console.log(url)
const $siteList =$('.siteList')
const $lastLi = $siteList.find('li.last')
const $li = $(`<li>
<a href="${url}">
<div class="site">
<div class="logo">${url[0]}</div>
<div class="link">${url}</div>
</div>
</a>
</li>`).insertBefore($lastLi)
})
4.5.6 进入新网址后,新增的页面信息清楚,怎么存活,用数组来存,以下代码实现:
const $siteList =$('.siteList')
const $lastLi = $siteList.find('li.last')
const x =localStorage.getItem('x') //尝试读取当前网站上的x
const xObject =JSON.parse(x) //如果x能成功的变成一个对象
const hashMap = xObject ||[ //把这个对象放在hashMap里,如果不行,就初始化含2项的数组
{logo: 'A',logoType:'text',url:'https://www.acfun.cn'},
{logo: '.\images\b.jpg',logoType:'image',url:'https://www.bilibili.com'},
]
const simplifyUrl = (url)=>{
return url.replace('https://', '')
.replace('http://', '')
.replace('www.', '')
.replace(/\/.*/, '')
}
const render = ()=>{
$siteList. find('li:not(.last)').remove()
hashMap.forEach(node=>{
const $li =$(`<li>
<a href="${node.url}">
<div class="site">
<div class="logo">${node.logo[0]}</div>
<div class="link">${simplifyUrl(node.url)}</div>
</div>
</a>
</li>`).insertBefore($lastLi)
});
}
render()
$('.addButton')
.on('click',()=>{
let url = window.prompt('请问你要添加的网址是啥?')
console.log(url)
if(url.indexOf('http')!==0){
url = 'https://'+ url
}
console.log(url)
hashMap.push({
logo:url[0],
logoType:'text',
url:url
}),
render()
})
window.onbeforeunload = ()=>{ //关闭页面时会把当前的hashMap存到x里面,下次进入时会保存
console.log('页面要关闭了')
const string = JSON.stringify(hashMap) //把一个对象变成对象
localStorage.setItem('x',string)
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1,
minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<title>前航 - 前端导航网站</title>
<style type="text/css">
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="globalHeader">
<form class="searchForm" method="get"
action="https://www.baidu.com/s"
>
<input name="wd" type="text">
<button type="submit">搜索</button>
</form>
</header>
<main class="globalMain">
<ul class="siteList">
<li class="last">
<div class="addButton">
<div class="icon-wrapper">
<svg class="icon">
<use xlink:href="#icon-add"></use>
</svg>
</div>
<div class="text">
新增网站
</div>
</div>
</li>
</ul>
</main>
<script src="//at.alicdn.com/t/font_1473630_8pfq4zss4o8.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="main.js"></script>
</body>
</html>
- 下面实现logo正确显示:先在html标签上插入1...2,确定要改的位置,
<div class="logo">1${node.logo[0]}2</div>,直接再logo代码上,替换为调用简化url函数,再取大写就行;或者在css里面的 .logo样式,加上text-transform:uppercase,全大写。
$('.addButton')
.on('click',()=>{
let url = window.prompt('请问你要添加的网址是啥?')
console.log(url)
if(url.indexOf('http')!==0){
url = 'https://'+ url
}
console.log(url)
hashMap.push({
logo:simplifyUrl(url)[0].toUpperCase(), //直接调用简化url函数,再取大写
logoType:'text',
url:url
}),
render()
})
- 继续优化用户输入网址的各种格式,优化到尽量少出bug,需要正则表达式(可去学一篇文章:三十分钟入门正则表达式)
const simplifyUrl = (url)=>{
return url.replace('https://', '')
.replace('http://', '')
.replace('www.', '')
.replace(/\/.*/, '') //删除/开头的内容
- 实现删除某个导航网站,添加一个close标签,
const render = ()=>{
$siteList. find('li:not(.last)').remove()
hashMap.forEach(node=>{
const $li =$(`<li>
<a href="${node.url}">
<div class="site">
<div class="logo">${node.logo}</div>
<div class="link">${simplifyUrl(node.url)}</div>
<div class="close">xx</div> //临时用xx,后期要改成正式的close按钮样式
7.1 把close按钮的css样式定位一下:
.siteList .site{
width: 160px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background: white;
border:1px solid #ddd;
border-radius: 4px;
padding: 20px 0;
position: relative; /*相对定位close按钮*/
.siteList .site > .close{
position: absolute; /*绝对定位close按钮*/
right: 0; /*定位close按钮*/
top: 0; /*定位close按钮*/
}
7.2 用iconfont矢量图工具,搜索一个close样式按钮,添加到购物车,添加至项目,重命名新添加的图标,点击提示行更新代码,帮助文档,应用代码,复制那段到js里
<script src="//at.alicdn.com/t/font_1473630_8pfq4zss4o8.js"></script>
<div class="close">
<svg class="icon">
<use xlink:href="#icon-close"></use>
</svg>
</div>
7.3 优化一下图标的样式,
.siteList .site > .close{
position: absolute; /*绝对定位close按钮*/
right: 10px; /*定位close按钮*/
top: 5px; /*定位close按钮*/
}
7.4 因为刚才添加的close按钮在a标签里面,如果点击close就会自动执行a标签的动作,所以要阻止这个冒泡,尝试了以后发现a标签还是与close冲突;删掉a标签,换成如下代码,其中点击close,关闭网页导航小窗口其实就是数组中的一项删掉!
hashMap.forEach((node,index)=>{ /*index加完以后有个括号记得写*/
$li.on('click',()=>{
window.open(node.url)
})
$li.on('click','.close',(e)=>{
e.stopPropagation() //阻止冒泡
hashMap.splice(index,1)
render()
})
- PC端代码实现:
8.1 切换到PC端控制台,刷新页面,可以看到导航框是space-bettwen布局,需要改为平均布局,用负margin。首先把PC端的页面宽度设置一下,如果把searchForm设置为宽度400的话,可能会影响手机端,这样就换成媒体查询来实现,以避免对手机端效果的影响。
*{box-sizing: border-box;}
*:before,*:after{box-sizing: border-box;}
*{margin: 0; padding: 0;}
ul,ol{list-style: none;}
a{color: inherit;text-decoration: none;}
img{max-width: 100%;max-height: 100%;}
body{background:#eee;}
.globalHeader{
margin: 20px;
}
@media(min-width:500px){
.globalHeader{
margin: 60px 0 100px;
}
}
.searchForm{
display: flex;
justify-content: space-between;
}
@media (min-width:500px){ /*页面只支持500px宽度以上的效果实现*/
.searchForm{
max-width:400px;
margin-left: auto;
margin-right: auto;
}
}
.searchForm > input{
width: 100%;
margin-right:10px ;
height: 40px;
padding: 0 10px;
border: none;
border: 1px solid #ddd;
border-radius: 4px;
}
.searchForm > button{
white-space: nowrap;
padding: 0 28px; /*有一个计算过程*/
border: none;
border-radius: 4px;
background: #0282b9;
color: white;
font-size: 16px;
}
.globalMain{
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
.siteList{
margin: 20px;
display: flex;
flex-wrap: wrap;
}
@media(min-width:500px){
.siteList{
margin-left: 0;
margin-right: -25px;
}
}
.siteList > li{
margin-bottom: 10px;
margin-right: 25px;
}
.siteList .site{
width: 160px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background: white;
border:1px solid #ddd;
border-radius: 4px;
padding: 20px 0;
position: relative; /*相对定位close按钮*/
}
.siteList .site > .logo{
width:64px;
height: 64px;
display: flex;
justify-content: center;
align-items: center;
font-size: 64px;
text-transform: uppercase;
}
.siteList .site > .link{
font-size: 14px;
margin-top: 4px;
}
.siteList .site > .close{
position: absolute; /*绝对定位close按钮*/
right: 10px; /*定位close按钮*/
top: 5px; /*定位close按钮*/
}
.siteList .addButton{
border: 1px solid #ddd;
background: white;
width: 160px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 20px 0;
}
.siteList .addButton .icon{
width: 56px;
height: 56px;
}
.siteList .addButton .text{
font-size: 14px;
margin-top: 4px;
}
.siteList .addButton .icon-wrapper{
width: 64px;
height: 64px;
display: flex;
justify-content: center;
align-items: center;
}
8.2 PC端优化交互,隐藏close按钮,其他效果见详细代码,注意PC端的代码实现与手机端的联调,把手机端不想实现的效果,都隐藏在PC端的媒体查询中:
.siteList{
margin: 20px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
@media(min-width:500px){
.siteList{
margin-left: 0;
margin-right: -25px;
justify-content:flex-start;
}
- 键盘导航:监听document
$(document).on('keypress',(e) => {
const {key}= e /*const key =e.key 的简化写法*/
for (let i = 0; i< hashMap.length; i++) {
if (hashMap[i].logo.toLowerCase() === key) {
window.open(hashMap[i].url)
}
}
})
- 发布到Github上