- 用console setting里的preserve Log可以看见过程
- 有最大宽度或者宽度固定时,居中可以用左右margin为auto
一、在figma里画出设计稿
二、手机端
记住开发者工具调到移动端查看
(一)初始化
- 新建nav文件夹,index.html style.css main.js
- 对index.html
- 把移动端设置改为不可双指缩放: 原来的移动端设置为可以双指放大,找一个不允许双指放大的网页的代码抄就完事:淘宝网,开发者工具,移动端,刷新,找到复制
- 改名字:前航--前端导航网站
- 在head里,引入css
<link rel="stylesheet" href="style.css" /> - 在body里,引入js
<script src="main.js"></script>
(二)写HTML
因为HTML很简单,所以一口气写完
(三)写CSS
- 先把所有选择器写好
- css reset
- 根据设计图来写代码就完事
- icon
- 进入iconfont ,帮助,代码应用,symbol引用
- 找到图标,加入购物车,加入项目,symbol,查看在线链接
- 引用该网址
<script src="//at.alicdn.com/t/font_1656440_l3a5g07zas.js"></script>把链接一定要放在引用js之前 - 加入通用css代码,放在head的link标签之前
- 挑选相应图标并获取类名,应用于页面:复制代码到你要加入的位置,改名字
(四)加功能
- 搜索:表单form包住input和button,记得改选择器
- 网站:用a标签包住.site完事,记得把a标签cssreset和改选择器
- 加入网站的logo:下载图片放到image文件夹,在.logo下用img标签引用图片,记得img CSS reset:max-width:100%
(五)JS
- 引入jQuery
- bootcdn,搜索jQuery
- 找最新版的min.js
- 复制
<script>标签,粘贴到main.js的<script>标签之前
- 监听addButton
- 用手机查看:
ipconfig,IPV4的地址加端口 - 当点击时,需要问用户要插入啥网站,之后在addButton前插入就完事
(六)最后看一眼刚刚写的代码,因为马上就要重写了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!--原来的移动端设置为可以双指放大,找一个不允许双指放大的网页的代码抄就完事:淘宝网,开发者工具,移动端,刷新,找到复制-->
<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 type="text" name="wd" />
<button type="submit">搜索</button>
</form>
</header>
<main class="globalMain">
<ul class="siteList">
<!--ul是站点列表-->
<li>
<a href="https://www.acfun.cn/">
<div class="site">
<div class="logo">
<img src="./image/timg.jpg" alt="" />
</div>
<div class="link">acfun.cn</div>
</div>
</a>
</li>
<li>
<a href="https://www.bilibili.com/">
<div class="site">
<div class="logo">
<img src="./image/timg (2).jpg" alt="" />
</div>
<div class="link">bilibili.com</div>
</div>
</a>
</li>
<li>
<a href="https://www.taobao.com/">
<div class="site">
<div class="logo">
<img src="./image/timg (3).jpg" alt="" />
</div>
<div class="link">taobao.com</div>
</div>
</a>
</li>
<li class="last">
<div class="addButton">
<div class="icon-wrapper">
<svg class="icon">
<use xlink:href="#icon-add"></use>
</svg>
</div>
<div class="link">新增网站</div>
</div>
</li>
</ul>
</main>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="//at.alicdn.com/t/font_1656440_l3a5g07zas.js"></script>
<script src="main.js"></script>
</body>
</html>
/*css reset*/
* {
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%;
}
/*style*/
body {
background: #eee;
}
.globalHeader {
margin: 20px; /*让header在body中居中*/
}
.searchForm {
display: flex;
justify-content: space-between; /*用flex布局让input和按钮各在两边*/
}
.searchForm > input {
width: 100%; /*让input变宽*/
margin-right: 10px; /*input有个右边距,就和button隔开了,好看*/
height: 40px;
padding: 0 10px; /*输入文字后左边不会紧粘着左边界,好看点*/
border: none;
border: 1px soli #ddd;
border-radius: 4px; /*给边界加个圆角,但是input有个默认边界,必须先覆盖,才可以在对边界加圆角*/
}
.searchForm > button {
white-space: nowrap; /*不让按钮被input挤压换行*/
padding: 0 28px; /*加了内边距,让搜索两个字不会充满整个按钮,好看点*/
border: none;
border-radius: 4px; /*给边界加个圆角,但是input有个默认边界,必须先覆盖,才可以在对边界加圆角*/
background: #0282b9;
color: white;
font-size: 16px; /*从Figmal中复制一些需要的样式代码*/
}
.globalMain {
}
.siteList {
display: flex;
flex-wrap: wrap; /*让里面的li成一行,而且折行*/
margin: 20px; /*让整个都居中,并且和上面的header有点间距*/
justify-content: space-between;
}
.siteList > li {
margin-bottom: 10px; /*上下隔开点,好看点*/
}
.siteList > li .site {
width: 160px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center; /*用flex布局让里面logo和link居中*/
background: white;
border: 1px solid #ddd; /*加边界*/
border-radius: 4px; /*给边界加个圆角*/
padding: 20px 0; /*跟着设计图来呀 我想变高点,不要用height,不得不用才用,所以这里用padding变高*/
}
.siteList > li .site > .logo {
width: 64px;
height: 64px;
display: flex;
justify-content: center;
align-items: center; /*用flex布局让里面的东西居中*/
font-size: 64px; /*设置里面的文字大小*/
}
.siteList > li .site > .link {
font-size: 14px;
margin-top: 4px; /*让logo和link隔开点,好看点*/
}
.siteList > li > .addButton {
width: 160px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
background: white;
border: 1px solid #ddd; /*加边界*/
border-radius: 4px; /*给边界加个圆角*/
padding: 20px 0;
}
.siteList > li .addButton > .icon-wrapper {
width: 64px;
height: 64px;
display: flex;
justify-content: center;
align-items: center; /*让svg在icon-wrapper中居中*/
}
.siteList > li .addButton > .icon-wrapper > .icon {
width: 64px;
height: 64px;
}
.siteList > li .addButton > .link {
font-size: 14px;
margin-top: 4px;
}
$(".addButton").on("click", () => {
let url = window.prompt("请问你要添加的网址是啥");
if (url.indexOf("http") !== 0) {
//如果url的开头不是http
// alert("请输入http开头的网址");
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);//当用户点击后,要问用户插入啥网址,我们创建个和前面一模一样的site插入到addButton前面就完事
});
(七)发现问题,重写代码
- 把当前页面里面的一个个site构成一个数据结构,存起来,当我再次回到导航页,再把该数据结构拿出来就完事
- 使用数据结构:数组的哈希表
[{site1},{site2},{site3}]
- 我们用了parcel来预览,默认在代码外面创建了个作用域,创建全局变量要用
window.xxx,不可用const xxx,否则创建的xxx是在parcel作用域里的局部变量,在控制台里找不到未定义
const $siteList = $(".siteList");
const $lastLi = $siteList.find("li.last");
window.hashMap = [
{ logo: 'A', logoType:'text',url:'https://www.acfun.cn/' },
{ logo: './image/timg (2).jpg', logoType: 'image', url:'https://www.bilibili.com/' },
{ logo: './image/timg (3).jpg', logoType: 'image', url: 'https://www.taobao.com/'}
]
//那HTML里面都不用写了,直接用这个哈希表生成就完事
//用forEach函数遍历哈希表里的每一项,让每一项都生成一个方块,然后放到ul列表里区
//我不懂${}的用法
//命名为render,方便调用
const render = () => {
$siteList.find('li:not(.last)').remove() //先把之前的li(除了最后一个新增网站)删掉,否则后面的forEach会重新再生成他们一次
hashMap.forEach( //对hashMap里的每一个元素新建一个li方块,然后放到ul列表里区
(node) => {
console.log('hi')
const $li = $(`<li>
<a href="${node.url}">
<div class="site">
<div class="logo">${node.logo[0]}</div>
<div class="link">${node.url}</div>
</div>
</a>
</li>`).insertBefore($lastLi)
}
)
}
//第一步,先弄出三个我们写好的方块
render()
//第二步:要是新建方块,就该这样做
//新建网站的流程是:用户点击后,让用户输入url,我们给hashMap添加上这个url
//然后在重新执行一遍hashMap的foreach函数.
//但是之前的旧hashMap已经执行过一遍了,重新执行之前的会重复。所以要在执行forEach函数前把之前的方块(除了新增网站那个)都删掉
//也就是render函数
$(".addButton").on("click", () => {
let url = window.prompt("请问你要添加的网址是啥");
if (url.indexOf("http") !== 0) {
//如果url的开头不是http
// alert("请输入http开头的网址");
url = "https://" + url;
}
hashMap.push({ logo: url[0], logoType: 'text', url: url })
render()
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!--原来的移动端设置为可以双指放大,找一个不允许双指放大的网页的代码抄就完事:淘宝网,开发者工具,移动端,刷新,找到复制-->
<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 type="text" name="wd" />
<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="link">新增网站</div>
</div>
</li>
</ul>
</main>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="//at.alicdn.com/t/font_1656440_l3a5g07zas.js"></script>
<script src="main.js"></script>
</body>
</html>
- 我发现当我在导航页新建网站,点进去浏览好了之后,再回到导航页,新建网站又没了????回到导航页,js代码又重新从头执行,你只是把你初始的三个方块写进了代码,又没把你新建的网页放进去,当然没有你新建的网页了
- 用户关闭当前页面前触发:把最新的hashMap储存为字符串x
- 之后回到页面,从头开始执行js代码:重新声明hashmap为x或者我们事先写好的三个方块(因为第一次x肯定是不存在的,所以第一次hashmap应该是我们写好的三个方块),后面代码不变:第一步渲染hashmap,第二步要是新建网址就把新网址推送到hashmap在去渲染hashmap。关闭页面前储存最新的hashmap。循环完事
- 从console的右边的application的Local Storage可以查看你储存的东西
const $siteList = $(".siteList");
const $lastLi = $siteList.find("li.last");
//第一步:当我们要离开导航页时,把最新的hashMap存下来,成为字符串x
window.onbeforeunload = () => {
console.log('页面要关闭了')
//local只可以存字符串,所以要把对象hashMap变成字符串
const string = JSON.stringify(hashMap)
//localStorage是全局变量,steItem()指在本地的储存里,设置一个x,他的值是string
window.localStorage.setItem('x', string)}
//第二步:定义hashMap:读取localStorage里的x,要是x为空,就是我们写的三个固定的方块,要是不为空就算上回保存的最新的hashMap
const x = localStorage.getItem('x')
const xObject = JSON.parse(x)
const hashMap = xObject ||
[
{ logo: 'A', logoType:'text',url:'https://www.acfun.cn/' },
{ logo: './image/timg (2).jpg', logoType: 'image', url:'https://www.bilibili.com/' },
{ logo: './image/timg (3).jpg', logoType: 'image', url: 'https://www.taobao.com/'}
]
//那HTML里面都不用写了,直接用这个哈希表生成就完事
//用forEach函数遍历哈希表里的每一项,让每一项都生成一个方块,然后放到ul列表里区
//我不懂${}的用法
//命名为render,方便调用
const render = () => {
$siteList.find('li:not(.last)').remove() //先把之前的li(除了最后一个新增网站)删掉,否则后面的forEach会重新再生成他们一次
hashMap.forEach( //对hashMap里的每一个元素新建一个li方块,然后放到ul列表里区
(node) => {
const $li = $(`<li>
<a href="${node.url}">
<div class="site">
<div class="logo">${node.logo[0]}</div>
<div class="link">${node.url}</div>
</div>
</a>
</li>`).insertBefore($lastLi)
}
)
}
//第三步,先弄出三个我们写好的方块||先弄出之前保存的哈希表
render()
//第四步:要是新建方块,就该这样做
//新建网站的流程是:用户点击后,让用户输入url,我们给hashMap添加上这个url
//然后在重新执行一遍hashMap的foreach函数.
//但是之前的旧hashMap已经执行过一遍了,重新执行之前的会重复。所以要在执行forEach函数前把之前的方块(除了新增网站那个)都删掉
//也就是render函数
$(".addButton").on("click", () => {
let url = window.prompt("请问你要添加的网址是啥");
if (url.indexOf("http") !== 0) {
//如果url的开头不是http
// alert("请输入http开头的网址");
url = "https://" + url;
}
hashMap.push({ logo: url[0], logoType: 'text', url: url })
render()
});
(八)代码不完美。让我们定一个个小目标去逐渐完善他
1、我想让link里的网址没有http://www.
- 首先找到link为
${node.url},对node.url进行一个函数操作,这个函数可以删掉http://www. - 写个函数
const removeX = url => {
return url
.replace("https://www.", "")
.replace("https://", "")
.replace("www.", "");
};
//返回的的新字符串,原来的url不会被改变
- 对原link
${node.url}变为${removeX(node.url)}
2、我想把logo变成网站的首字母(不用图片了,先来点简单的)
- 因为logo是首字母,所以把最开始的三个方块的logo改成首字母,把不需要的删掉,比如logoType
- 那新建网站的logo该咋办
- 好办啊!前面不是removeX函数可以删掉那些东西呀,留下来的第一个不就是该网站的首字母啦(大写!)
- 找到logo原来为
${node.url[0]}改为${removeX(node.url)[0].toUpperCase()} - 两种方法大写:js中在要大写的字符串后面
.toUpperCase();在css里找到logo,设置text.transform:uppercase
3、新建网站要是用户输入的url老长一条,但我们的link里的网址字符串只想要根网站,前面的http已经删掉了,后面的/xxxx也要删掉
- 用正则表达式(必须看三十分钟入门正则表达式)
- 补充removeX函数
const removeX = url => {
return url
.replace("https://www.", "")
.replace("https://", "")
.replace("www.", "")
.replace(/\/.*/,'') //前面已经把可以删的/善良,然后删除/开头的所有内容
};
//返回的的新字符串,原来的url不会被改变
4、删除新建的网站
- 我们想在每个方块右上角加个x,点击x就可以删掉这个方块
- 在
$li里加上一个<div class="close">x</div> - 让x(
.close)跑到方块(.site)的右上角:用绝对定位:.site在里加入position: relative;,在.close写position: absolute和right:0,top:0; - 找关闭按钮的图标,替换x
- 监听
.close - 之前方块是a标签包起来了,点击就可以跳转。但是发现无法对a标签里的
.close实现监听,所以删掉a标签,用监听li来实现跳转,然后监听.close
const render = () => { //render函数用于
$siteList.find("li:not(.last)").remove(); //删掉所有的方块li
hashMap.forEach((node, index) => { //对hashMap里的每一个元素建立对应的方块li,并且加入到新建网址方块前面
console.log(index)
const $li = $(`<li>
<div class="site">
<div class="logo">${removeX(node.url)[0].toUpperCase()}</div>
<div class="link">${removeX(node.url)}</div>
<div class="close"> <svg class="icon">
<use xlink:href="#icon-close"></use>
</svg></div>
</div>
</li>`).insertBefore($lastLi)
$li.on('click', () => { window.open(node.url) })//监听li,实现跳转
$li.on('click', '.close', (e) => { //监听.close
e.stopPropagation() //阻止冒泡,阻止li里的.close跳转
hashMap.splice(index, 1) //删掉这个方块对应的哈希表元素,然后重新执行一次render(),渲染方块
render()
})
});
};
5、我觉得哈希表里面的logo都用不着,只需要留个url。
三、pc版
关掉开发者工具,刷新页面,方块不对劲,因为之前是space-between 改css!
(一)整个的宽度要确定
1、form表单
- 宽度:当页面很宽时,我们要给它一个最大宽度。页面不是那么宽(还有手机页面),就不用给最大宽度
- 居中:我想页面很宽时居中,不太宽(还有手机页面)就不管
/*响应式页面:媒体查询*/
@media (min-width: 500px) {
/*当屏幕最小宽度为500px,也就是当页面宽度大于500px时,再给form加一个的最大宽度为400px*/
.searchForm {
max-width: 400px;
margin-left: auto;
margin-right: auto;
}
}
2、站点列表--用负margin做成平均布局
- 先设置main的最大宽度为900px,并且居中
- 每个li是160,一行有五个li,就是800,一行有四个间隔,一个间隔25。你就给每个li加上右margin25,给.siteList加上右margin-25。
- 以上都要在页面很宽时(pc页面)做,不然手机就变形了
.globalMain {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.siteList {
display: flex;
flex-wrap: wrap; /*让里面的li成一行,而且折行*/
margin: 20px; /*对于手机来说,让整个都居中,并且和上面的header有点间距*/
justify-content: space-between;
}
@media (min-width: 500px) { //媒体查询,当pc页面时
.siteList {
margin-left: 0;
margin-right: -25px;
justify-content: flex-start; //把主轴排列方式换回从左至右排序
}
.siteList > li {
margin-right: 25px;
}
.globalHeader {
margin-top: 60px;
margin-bottom: 100px;
}
}
(二)优化,定一个个小目标
1、当我们把鼠标浮到方块上去,关闭按钮才出现
- 先让.close消失
.siteList > li .site > .close {
position: absolute;
right: 4px;
top: 0;
display: none;
}
*当我们把鼠标浮到.site上去时,他的子元素.close才出现
.siteList > li .site:hover > .close {
display: block;
}
2、当鼠标浮到方块上时,鼠标应该变为手型;但是唯一注意的是,浮到.close上时变回箭头,提醒用户不要乱点删除。
- 对.site的css加上
cursor: pointer - .close的css加上
cursor: default
3、排序方块:不做了,麻烦,难
4、当我们在导航页面,用键盘敲一下方块中有的logo,就可以直接跳转
监听document的键盘事件
$(document).on('keypress', (e) => {
//按下键盘a,打印出一个对象,有个属性是keyCode: 97和属性key是a的Unicode字符编码
const {key} = e //const key = e.key
console.log(key)
for (let i = 0; i < hashMap.length;i++){
if (hashMap[i].logo.toLowerCase() === key) {
window.open(hashMap[i].url)
}
}
})
上传到github
parcel build src/index.html --no-minify --public-url ./
- 报错SVG,所以加个--no-minify
- 新建dist目录
- 打开
dist/index.html发现引用的js和css的地址都不对,修改--public-url为当前目录./(parcel build -- help)
- 上传到github就行了。注意.gitignore
- 用Github Pages预览,请点击
进阶:一键部署
yarn init -y出现package.json- 在package.json里加一个脚本
"scripts": {
"build": "rm -rf dist && parcel build src/index.html --no-minify --public-url ./"
},
- 之后
yarn build就行了 - 记得提交啊