【项目】导航页面(下)--全过程

434 阅读11分钟

请点击

  • 用console setting里的preserve Log可以看见过程
  • 有最大宽度或者宽度固定时,居中可以用左右margin为auto

一、在figma里画出设计稿

二、手机端

记住开发者工具调到移动端查看

(一)初始化

  1. 新建nav文件夹,index.html style.css main.js
  2. 对index.html
  • 把移动端设置改为不可双指缩放: 原来的移动端设置为可以双指放大,找一个不允许双指放大的网页的代码抄就完事:淘宝网,开发者工具,移动端,刷新,找到复制
  • 改名字:前航--前端导航网站
  • 在head里,引入css <link rel="stylesheet" href="style.css" />
  • 在body里,引入js <script src="main.js"></script>

(二)写HTML

因为HTML很简单,所以一口气写完

(三)写CSS

  1. 先把所有选择器写好
  2. css reset
  3. 根据设计图来写代码就完事
  4. icon
  • 进入iconfont ,帮助,代码应用,symbol引用
  • 找到图标,加入购物车,加入项目,symbol,查看在线链接
  • 引用该网址<script src="//at.alicdn.com/t/font_1656440_l3a5g07zas.js"></script>把链接一定要放在引用js之前
  • 加入通用css代码,放在head的link标签之前
  • 挑选相应图标并获取类名,应用于页面:复制代码到你要加入的位置,改名字

(四)加功能

  1. 搜索:表单form包住input和button,记得改选择器
  2. 网站:用a标签包住.site完事,记得把a标签cssreset和改选择器
  3. 加入网站的logo:下载图片放到image文件夹,在.logo下用img标签引用图片,记得img CSS reset:max-width:100%

(五)JS

  1. 引入jQuery
  • bootcdn,搜索jQuery
  • 找最新版的min.js
  • 复制<script>标签,粘贴到main.js的<script>标签之前
  1. 监听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也要删掉

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;,在.closeposition: absoluteright: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

  1. parcel build src/index.html --no-minify --public-url ./
  • 报错SVG,所以加个--no-minify
  • 新建dist目录
  • 打开dist/index.html发现引用的js和css的地址都不对,修改--public-url 为当前目录./(parcel build -- help)
  1. 上传到github就行了。注意.gitignore
  2. 用Github Pages预览,请点击

进阶:一键部署

  1. yarn init -y 出现package.json
  2. 在package.json里加一个脚本
  "scripts": {
    "build": "rm -rf dist && parcel build src/index.html --no-minify --public-url ./"
  },
  1. 之后yarn build就行了
  2. 记得提交啊