Day1—对象、数组和排序

对象

在JS中只要使用了.就是对象

能用.就能用[]操作,且在一些操作下必须使用[],比如属性名为

  • 变量
  • 参数
  • 特殊符号

对象嵌套的时候,对内层属性的读取也可以嵌套

比如obj.children.children.children.name代表读取obj对象下的childern对象下的childern对象下的childern对象下的name属性,也可以切换成中括号obj['children']['children']['children']['name']

对象里面可以存储任何数据类型,包括函数、数组和对象等,他们被统一称为对象的属性,但当对象存储的是函数时,这个函数也可以被称为方法

在表面的区别就是使不使用()调用,比如Math.random()是一个方法,而Math.PI是一个属性

去重——对象方法

    let Arr2 = ['d', 'a', 'c', 'a', 'd', 'b', 'a', 'b']
    let obj = {} 
    // console.log(obj.a)   //如果对象没有对应属性就会返回undefined
    for (let i = 0; i < Arr2.length; i++) {
      let key = Arr2[i]
      if(obj[key] === undefined){
        obj[key] = 1  //因为key是变量,必须使用中括号,不能用(.)
      }else{
        obj[key]++
      }
    }
    console.log(obj)

    let arr = []
    for(let key in obj){  //for in循环遍历对象
      arr.push(key)  //把属性名存入数组,
    }
    console.log(arr)

上例利用了对象属性名唯一的特性来去重

数组

数组常用操作

方法 作用 特点
push() 后面添加 返回值为数组长度,可以同时添加多个
shift() 前面删除 返回值为删除的元素,不需要参数
unshift() 前面添加 返回值为数组长度,可以同时添加多个
pop() 后面删除 返回值为删除的元素,不需要参数
slice() 复制 不改变原数组
concat() 拼接 不改变原数组
reverse() 反转
join() 转字符串 不改变原数组,默认加逗号
sort() 排序(默认字典序) 有固定写法,可以排对象数组
splice() 截取,可以在截取时插入内容 (截取位置,截取长度,拼接内容)
indexOf() 查找返回下标 查找失败返回-1
lastIndexOf() 查找最后一次返回下标 查找失败返回-1
includes() 是否包含 返回值为布尔值
        slice()     // 复制
        var newArr = arr.slice()
        console.log(newArr)
        console.log(arr == newArr)  // false
        //--------------------------------------------------------------
        concat()    // 拼接数组
        console.log(arr.concat('hello', 'a', 'b'))
        //--------------------------------------------------------------
        reverse()   // 反转数组
        console.log(arr.reverse())
        //--------------------------------------------------------------
        join()      // 转字符串
        console.log(arr.join())  //默认每个元素之间由逗号连接
        console.log(arr.join('')) //元素之间什么都没有
        console.log(arr.join('-')) //元素之间由-号连接
        console.log(arr.join('/'))
        //----------------------------------------------------
        sort()      // 排序  可以排对象数组,根据每个对象的某个属性来排序
        console.log(arr.sort())     // '默认字符串格式排序'
        arr.sort(function (a, b) { return a - b })      // 固定写法,从小到大
        arr.sort(function (a, b) { return b - a })      // 固定写法,从大到小
        console.log(arr)
        //------------------------------------------------------
        splice()  // 截取
        // - 第一个参数, 开始位置
        // - 第二个参数,截取个数
        // - 第三个参数,添加
        console.log(arr.splice(1, 2))  //从下标1开始截取长度为2
        console.log(arr)
        console.log(arr.splice(1, 2, 'hello'))  //从下标1开始截取长度为2,并且在截取位置添加hello
        console.log(arr)
        //--------------------------------------------------------------
        新增
        indexOf()       // 查找        返回下标        找不到返回-1
        lastIndexOf()   // 查找最后一次出现    返回下标    找不到返回-1
        includes()      // 包含    返回布尔值

冒泡排序

冒泡排序因为过程类似于气泡逐渐上浮的过程,因此被称为冒泡排序

冒泡排序的过程是从头开始两两比较,根据比较结果来交换数据,直到遍历完数组算是完成了一次冒泡排序

冒泡排序每一次排序都唯一确定一个数值的最终位置

时间复杂度O($n^{2}$)

冒泡排序的优化是,在一次排序中如果从头到尾都没有交换过,说明数组已经有序,因此直接结束排序

    function bubbleSort(arr) {
      let Arr = arr.slice()
      let flag = true
      for (let i = 0; i < Arr.length - 1; i++) {
        for (let j = 0; j < Arr.length - 1 - i; j++) {
          if (Arr[j] < Arr[j + 1]) {
            let temp = Arr[j]
            Arr[j] = Arr[j + 1]
            Arr[j + 1] = temp
            flag = false
          }
        }
        if (flag) break
      }
      return Arr
    }

选择排序

选择排序是从未排序数组的头到尾遍历数组,每次遍历都找到未排序数组中的最小值或者最大值,然后和未排序数组的首位交换,此时未排序数组长度减一,有序数组长度加一,直到未排序数组被有序数组代替

时间复杂度O($n^{2}$)

    function selectionSort(Arr) {
      let arr = Arr.slice()
      for (let j = 0; j < arr.length; j++) {
        let max = j
        for (let i = j; i < arr.length; i++) {
          if (arr[max] < arr[i]) {
            max = i
          }
        }
        swap(arr[max], arr[j])
      }
      return arr
    }

引用关系

数组本质上存的是地址,因此我们在定义一个数组等于另一个数组的时候,这两个数组变量是指向一个数组的,我们改变其中一个的内容会导致另一个也变化,而变量就不会这样,因为他们是两份变量,只是值相同,不存在引用关系

引用并非一个优点,有时我们需要去除引用关系,可以使用复制slice来复制一个数组,此时两个数组之间不再存在引用关系

练习

练习一对象排序

<body>

  <button id="math">数学</button>
  <button id="cn">语文</button>
  <button id="en">英语</button>
  <script>
    let arr = [
      {
        math: 99,
        cn: 88,
        en: 55,
      },
      {
        math: 100,
        cn: 91,
        en: 67,
      },
      {
        math: 74,
        cn: 90,
        en: 66,
      },
      {
        math: 89,
        cn: 92,
        en: 80,
      },
    ]
    function bubbleSort(arr, m) {
      let Arr = arr.slice() //复制数组
      let flag = true  //开关,检测一趟排序后是否已经有序
      for (let i = 0; i < Arr.length - 1; i++) {
        for (let j = 0; j < Arr.length - 1 - i; j++) {
          if (Arr[j][m] < Arr[j + 1][m]) {
            let temp = {}
            temp = Arr[j]
            Arr[j] = Arr[j + 1]
            Arr[j + 1] = temp
            flag = false
          }
        }
        if (flag) break
      }
      console.log(Arr)
    }

    math.onclick = function () {
      bubbleSort(arr, 'math')
    }
    cn.onclick = function () {
      bubbleSort(arr, 'cn')
    }
    en.onclick = function () {
      bubbleSort(arr, 'en')
    }
  </script>
</body>

练习2 使用splice去重

    //splice会遇到数组塌陷
    let arr = ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'b', 'b', 'a', 'a']

    function noRepeat(arr) {
      // let arr = Arr.slice()
      for (let i = 0; i < arr.length; i++) {
        for (let j = i+1; j < arr.length; j++) {
          if (arr[i] === arr[j]) {
            arr.splice(j--, 1)
            console.log(arr)
          }
        }
      }
      return arr
    }

    console.log(noRepeat(arr))

练习3 对象转为url格式

    let obj = { name: '张三', age: 18, id: 9527 }

    function outputObj(obj) {
      let arr = []
      for (let key in obj) {
        let out = `${key}=${obj[key]}`
        arr.push(out)
      }
      console.log(arr.join('&'))
    }

    outputObj(obj)

Day2—数组循环和字符串操作

数组循环

DOM循环

ES5新增了forEach()方法,可以循环一类标签

如果我们给每一个标签加上id然后一个个设置点击事件会很麻烦,所以我们会想到利用循环来设置,但这样我们就会发现一个bug,如下例

        // 拿到所有标签 for循环DOM有BUG
        let divAll = document.querySelectorAll('div')  //选中所有div
        for (let i = 0; i < divAll.length; i++) {  //循环list
            divAll[i].onclick = function () {  //为每个list设置点击事件
                console.log(i)  //当我们点击div时显示相应的i值
            }
        }
        console.log('循环结束')

上例中循环了所有的div,但我们会发现在点击任何div时显示的i值都是3,并非像我们想的那样显示相应标签序号

而新增的DOM循环.forEach()则不会产生这样的问题

    let divAll = document.querySelectorAll('div')
    divAll.forEach(function (item, index) {      //循环DOM
      console.log(item)   //item是每个标签
      item.onclick = function () {
        console.log(index)  //index是每个标签的序号
      }
    })

循环数组

使用.map()来循环遍历数组,返回值为经过回调函数操作的新数组,不改变原数组

    let arr = [12, 5, 7]
    let res = arr.map(function(item , index){    //循环数组,并组成一个新数组,和原来一样,返回值为新数组
      return item
    })

过滤

.filter()根据条件过滤数组元素,满足条件的元素组成一个新数组并返回,不改变原数组

    let arr = [12, 5, 7 ,99, 103]
    var res = arr.filter(function(item){     //过滤,不改变原数组
      return item>50
    })
    console.log(arr)
    console.log(res)

判断

.some()循环遍历数组,并查找是否有元素满足条件,有一个元素满足就返回true

.every()循环遍历数组,并查找是否所有元素都满足条件,都满足条件才会返回true,否则返回false

        var arr = [12, 5, 7, 99, 103]
        var flag = arr.some(function (item) {
            return item > 50
        })
        console.log(flag) //true

        var flag = arr.every(function (item) {
            return item > 50
        })
        console.log(flag)  //false

字符串操作

需要注意的是,字符串的操作都不会改变原字符串

常用

  • .replace(),替换数组元素

                console.log(str.replace('e', '*'))  //使用*替换e,默认只替换一个
                console.log(str.replace(/e/g, '*')) //全局替换
                document.write(str.replace(/e/g, '<img width="20" src="图片位置" />'))  //替换为图片
    
  • .includes(),包含,和数组的操作一样

  • .toUpperCase(),所有英文转换为大写

  • .toLowerCase(),所有英文转换为小写

  • .slice(),复制,和数组操作一样

    • console.log(str.slice(1, -1)),这样表示从下标为1开始到字符串倒数第一个结束
  • .split(),转数组,可以设置分隔符

                console.log(str.split())        //默认直接转成长度为一的数组
                console.log(str.split(''))      //每个元素占一个数组元素
                console.log(str.split(' '))        //以空格为分隔符
                console.log(str.split('e'))        //以e为分隔符,e消失
    

不常用

方法 作用 特点
charAt() 根据下标查找并返回字符串
charCodeAt() 根据下标查找并返回对应ASCII码
search() 查找返回下标
indexOf() 查找返回下标 和数组一样
lastIndexOf() 查找最后一次 和数组一样
substring() 复制 参数一是起点,参数二是结束下标,长度到结束下标前
substr() 复制 参数一是起点,参数二是复制个数
concat() 拼接 和数组一样
trim() 去除首尾空格
trimStart() 去除首空格
trimEnd() 去除尾空格

练习

练习1 判断是否为回文字符串

    let str = 'abcdedcba'
    let newStr = str.split('').reverse().join('')
    console.log(str)
    console.log(newStr)
    if(str === newStr){
      console.log('回文字符串')
    }else{
      console.log('不是回文字符串')
    }

练习2 创建二维数组

    function Matrix(n, m) {
      let count = 1
      let arr = []
      for (let i = 0; i < n; i++) {
        let arrRow = []
        for (let j = 0; j < m; j++) {
          arrRow.push(count++)
        }
        arr.push(arrRow)
      }
      return arr
    }

    console.log(Matrix(5, 5))

练习3 字符串转对象

    let str = 'a="jack"&b=2&c=3&d=4'
    function toObj(str) {
      let arr = str.split('&')
      console.log(arr)
      let obj = {}
      arr.map(function (item) {
        // console.log(typeof(item))  //item已经是string格式了
        let newArr = item.split('=')
        obj[newArr[0]] = newArr[1]
      })
      console.log(obj)
    }
    toObj(str)

Day3—Math对象和日期对象

Math对象

方法 作用
pow()
abs() 绝对值
PI π
sqrt() 开平方
ceil() 向上取整
floor() 向下取整
round() 四舍五入
max() 最大值
min() 最小值
random() 随机数

其中random()是最重要的也是最常用的,他的返回值范围是0~1,且1永远不会出现

自定义随机数范围方法

    function random(a, b) {
      let n = parseInt(Math.random() * (b - a) + a)
      return n
    }

随机数案例—双色球

  <div class="box">
    <span id="s1"></span>
    <span id="s2"></span>
    <span id="s3"></span>
    <span id="s4"></span>
    <span id="s5"></span>
    <span id="s6"></span>
    <span id="blue"></span>
    <button id="btn">再来一注</button>
  <style>
    .box {
      display: flex;
    }

    .box span {
      width: 30px;
      height: 30px;
      background: red;
      margin: 0 10px;
      border-radius: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
    }

    .box #blue {
      background: blue;
    }
  </style>
  <script>
    function random(a, b) {
      let n = parseInt(Math.random() * (b - a) + a)
      n = n > 0 && n < 10 ? '0' + n : n
      return n
    }
    function fn(){
      let arr = []
      for(let i = 0 ; i < 6 ; i++){
        let n = random(1,34)
        if(arr.includes(n) === false){
          arr.push(n)
        }else{
          i--
        }
      }
      s1.innerHTML = arr[0]
      s2.innerHTML = arr[1]
      s3.innerHTML = arr[2]
      s4.innerHTML = arr[3]
      s5.innerHTML = arr[4]
      s6.innerHTML = arr[5]
      blue.innerHTML=random(1,17)
    }
    fn()
    btn.onclick = function(){
      fn()
    }
  </script>

日期对象

创建一个日期对象的方法是let date = new Date(),可以在创建的时候给Date()提供参数,如果没有参数则实例化一个本地时间

如果提供参数,参数的格式为Date(年,月,日,时,分,秒)其中年、月是必填,且月份参数为0~11,其他可以选填,如果不填则默认最小值

参数的格式也可以是Date('年-月-日 时:分:秒')

对于日期对象的获取方法

方法 作用
date.toLocaleDateString() 返回年月日
date.toLocaleTimeString() 返回时分秒
date.toLocaleString() 返回年月日时分秒
date.getFullYear()
date.getMonth() 月,返回值0~11,0表示1月
date.getDate(day) 日期/天数
date.getDay() 星期
date.getHours()
date.getMinutes()
date.getSeconds()
date.getMilliseconds() 毫秒

所有月份参数设置都是从0开始,范围是0-11

日期对象参数如果超过合理值则自动更新,比如setDate()的参数是一个月的第几天,但如果参数超过当前月则自动延伸到下月。如果是0则自动延伸到上一月最后一天,如果是负数则延伸到上个月最后一天的前一天

以上日期方法都是基于Date实例的,也就是当Data实例化的那一刻时间就不会继续变化了,无论用计时器如何循环获取都不会变,如果想让时间变化就需要重新实例化

    let time = new Date();  // 实例化在定时器外面也就是只实例化一次
    setInterval(() => {
      let a = time.getSeconds();
      let b = time.toLocaleTimeString();
      let c = time.toLocaleString();
      console.log(a, b, c)  // 时间并不会变化
    },1000)
      setInterval(() => {
      let time = new Date(); // 当实例化也在定时器里面的时候,获取的时间就可以变化了
      let a = time.getSeconds();
      let b = time.toLocaleTimeString();
      let c = time.toLocaleString();
      console.log(a, b, c)  // 获取到的数值根据时间变化
    },1000)

对于日期对象的设置方法

和获取方法一一对应

方法 作用
date.setFullYear() 设置年,月,日
date.setMonth() 设置月,日,月份参数为0~11
date.setDate() 设置日
date.setDay() 设置星期
date.setHours() 设置小时
date.setMinutes() 设置分钟
date.setSeconds() 设置秒
date.setMilliseconds() 设置毫秒

时间戳

通过使用+new Date(),可以获得从1970年1月1日0时0分0秒到现在的毫秒数,被称为时间戳,我们可以用这个毫秒数来计算倒计时,获取目标时间的时间戳然后减去当前时间戳得到毫秒数转化为时分秒

也可以使用Date.now()来获得时间戳,不过这种方法只能得到当前的时间戳,而上一种方法可以获得指定时间的时间戳

定时器和延时器

定时器setInterval(函数名, 延迟时间毫秒),多次循环执行函数,返回值为一个唯一的ID值

清除定时器clearInterval(定期的变量名)使用前必须让定时器绑定变量名

延时器setTimeout(函数名, 延迟时间毫秒),只执行一次,如果想多次执行需要写到函数体中,返回值为一个唯一的ID值

清除延时器clearTimeout(名字)使用前必须让延时器绑定变量名

延时器和定时器共用一个ID池

定时器一旦设立就不能更改,不能多次调用,只能清除重新设立

练习

练习一

将今天的案例中设计个位数全部补零

练习二

以偶数小时为一个节点,设置倒计时,比如0,2,4,6,8,10,12,14,16,18,20,22,显示现在处于哪个节点后,并显示距离下一个节点还有多长时间

  <div class="box">
    <h2 id="hour"></h2>
    <h3 id="time">000000000</h3>
  </div>
  <style>
    .box{
      text-align: center;
      display: flex;
      flex-direction: column;
      justify-content: space-around;
      align-items: center;
    }
  </style>
  <script>
    function countDown(){
      let date = new Date()
      let now = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds()  //当前时间转换成的秒数
      let hours = date.getHours()  //获取当前小时
      if(hours % 2 !== 0){  //判断如果不是偶数就只可能是和目标差1小时,所以减去1,此时得到的就是上一个节点
        hours--
      }
      let last = hours * 3600  //当前时间的上一个整时节点化成的秒数
      let res = now - last  //两者相减得到距离上一个节点过去了几秒
      let target = 7200 - res  //用两个小时固定的秒数7200秒减去过了多久就是距离下一个节点还剩多久
      let arr = []  //将秒数化成时分秒并使用数组存放
      arr[0] = parseInt(target / 3600) 
      arr[1] = parseInt(target % 3600 / 60) 
      arr[2] = target % 60
      for(let i = 0 ; i< arr.length;i++){  
        if(arr[i] < 10 ){             //判断如果是个位数就在前面填0
          arr[i] = '0' + arr[i]
        }
      }

      hour.innerHTML = `当前${hours}点场`
      time.innerHTML = `距离下一场还有${arr[0]}时${arr[1]}分${arr[2]}秒`
      setTimeout(countDown , 1000)
    }

    countDown()
  </script>

练习三

将当前时间转成中文汉字并输出

  <script>
    //传入一个数字,将其转换为汉字
    function toChinese(str) {
      let flag = +str  //将字符串转成数字
      let numStr = '零一二三四五六七八九十'  //用来转换汉字的字符串
      let arr = []  //存结果的数组
      if (flag > 60) {  //如果是年份,一定大于60
        for (let i = 0; i < str.length; i++) {  //年份直接每个数字对应一个汉字
          arr[i] = numStr[str[i]]
        }
        arr = arr.join('')
      } else if (flag <= 10) {  //0 - 10,可以直接对应汉字
        arr = numStr[flag]
      } else if (str[1] === '0') { // 20 30 40 50,整数需要加上  十
        arr = numStr[str[0]] + '十'
      } else if (flag < 20) {           //11 - 19
        arr = '十' + numStr[flag - 10]
      } else if (flag < 30) {           //21 - 29
        arr = '二十' + numStr[flag - 20]
      } else if (flag < 40) {           //31 - 39
        arr = '三十' + numStr[flag - 30]
      } else if (flag < 50) {           //41 - 49
        arr = '四十' + numStr[flag - 40]
      } else if (flag < 60) {           //51 - 59
        arr = '五十' + numStr[flag - 50]
      }
      return arr
    }
    // 获取当前时间,并将其转换为汉字
    function chineseTime() {
      let date = new Date()
      let now = date.toLocaleString()
      let arr = now.split(' ')  //把时间分成年月日  时分秒两组
      let cn = []
      console.log(arr)
      arr.map(function (item, index) {  //循环遍历数组分别取年月日和时分秒
        let newArr = []
        if (index === 0) {  //如果是年月日
          newArr = item.split('/')  //使用/分开
        } else {                    //如果是时分秒
          newArr = item.split(':')  //使用冒号分开
        }
        newArr.map(function (item, index) {  //循环遍历数组并将每个数字转换成中文
          cn.push(toChinese(item))  //将转换完的汉字压入数组
        })
      })
      time.innerHTML = `${cn[0]}年${cn[1]}月${cn[2]}日${cn[3]}时${cn[4]}分${cn[5]}秒` //输出转换后的结果
      setTimeout(chineseTime, 1000) //定时器,1s重复执行
    }
    chineseTime()
  </script>

Day4—BOM

BOM,全名为浏览器对象模型,可见其是对浏览器的各种操作

弹窗

方法 作用
alert() 提示框
prompt() 输入框
confirm() 确认框

前两个我们已经接触过了,确认框是弹出一个提示框并让用户选择确认或者取消,返回值是布尔值,选择确认返回值为true

浏览器

方法 作用
open() 打开
close() 关闭

open()在输入url后会打开网址,如果不填写则默认打开一个空白页,可以通过设置target值来选择如何打开网页

事件

事件 作用
onload() 加载完成,需要等待所有资源加载后触发
onresiz() 改变大小,浏览器大小改变时触发
onscroll() 滚动,在浏览器滚动时触发

常用属性

属性 作用
innerWidth 获取浏览器宽度
innerHeight 获取浏览器高度
scrollX 获取文档横向滚动距离,只读属性
scollY 获取文档纵向滚动距离,只读属性
scollTo(x,y) 设置滚动距离
scrollTop 获取滚动元素纵向滚动距离,可读写
scrollLeft 获取滚动元素横向滚动距离,可读写

scrollYscrollTop的区别是,scrollY是文档滚动距离,是window对象独有的只读属性,而scrollTop是滚动元素的属性,且他可读写,同理X和Left也一样

获取浏览器宽高度结合前面的onresize事件可以实现元素自适应

        window.onresize = function () {
            if (window.innerWidth < 1080) {
                // 页面宽度 / 设计图宽度 * 你设置的字体大小
                root.style.fontSize = window.innerWidth / 750 * 100 + 'px'
            }
        }

滚动相关的属性可以实现在滚动一定距离后显示对应元素,并且在点击某些元素后可以快速回到顶端

    header {
      height: 100px;
      width: 100%;
      background: #ddd;
      box-shadow: 0 10px 10px #ccc;
      position: fixed;
      top: -100px;
      left: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      transition: .3s;
    }

    div {
      height: 50px;
      width: 50px;
      display: none;
      align-items: center;
      justify-content: center;
      background: pink;
      color: #fff;
      position: fixed;
      right: 50px;
      bottom: 20px;
      cursor: pointer;  /*鼠标手型*/
    }
    window.onscroll = function () {
      if (scrollY > 300) {
        header.style.top = 0
        backTop.style.display = 'flex'
      } else {
        header.style.top = '-100px'
        backTop.style.display = 'none'
      }
    }
    backTop.onclick = function () {
      scrollTo(0, 0)
    }

昨天练习思路

练习二

HTML结构

  <div class="box">
    <img src="jdseckill.png" alt="" width="63" height="12">
    <img src="sec.png" alt="" width="24" height="24">
    <p><b id="start">00:00</b>场</p>
    <p>
      <span id="domHours">00</span>
      :
      <span id="domMin">00</span>
      :
      <span id="domSec">00</span>
    </p>
  </div>

CSS样式

    * {
      margin: 0;
      padding: 0;
    }
    .box {
      width: 83px;
      height: 94px;
      background: url(seckill-bg.png) no-repeat;
      background-size: 100% 100%;
      margin: 100px auto;
      text-align: center;
      font-size: 12px;
    }

    .box img {
      padding-top: 5px;
    }

    .box p {
      padding-bottom: 3px;
    }

    .box span {
      background: #000;
      color: #fff;
      padding: 1px;
      border-radius: 3px;
      overflow: hidden;
      font-weight: 700;
    }

JavaScript

    function addZero(n) {  //补零
      return n < 10 ? '0' + n : '' + n
    }

    function countDown() {
      let target = new Date()
      let h = target.getHours()
      if (h % 2 !== 0) {
        h--
      }
      target.setHours(h + 2, 0, 0, 0)

      start.innerHTML = addZero(h) + ':00'

      let now = new Date()
      let time = parseInt((target - now) / 1000)

      let day = parseInt(time / 3600)
      let minutes = parseInt(time % 3600 / 60)
      let seconds = time % 60

      domHours.innerHTML = addZero(day)
      domMin.innerHTML = addZero(minutes)
      domSec.innerHTML = addZero(seconds)

      setTimeout(countDown, 1000)
    }
    countDown()

效果

练习三

    //传入一个数字字符串,将其转换为汉字
    function toChinese(n) {
      let flag = +n
      let numStr = '零一二三四五六七八九十'  //用来转换汉字的字符串
      let arr = []  //存结果的数组

      if (flag > 60) {  //如果是年份,一定大于60
        let str = flag + ''
        for (let i = 0; i < str.length; i++) {  //年份直接每个数字对应一个汉字
          arr[i] = numStr[str[i]]
        }
        arr = arr.join('')
      } else if (flag <= 10) {  //0 - 10,可以直接对应汉字
        arr = numStr[flag / 1]
      } else if (flag % 10 === 0) { // 20 30 40 50
        arr = numStr[flag / 10] + numStr[10]
      } else if (flag < 20) {           //11 - 19
        arr = numStr[10] + numStr[flag - 10]
      } else if (flag < 60) {           //21 - 59
        arr = numStr[parseInt(flag / 10)] + numStr[10] + numStr[flag % 10]
      }
      return arr
    }
    function tick(){  //获取时间
      let now = new Date()
      time.innerHTML = 
          toChinese(now.getFullYear())+'年'
          +toChinese(now.getMonth())+'月'
          +toChinese(now.getDate())+'日'
          +toChinese(now.getHours())+'时'
          +toChinese(now.getMinutes())+'分'
          +toChinese(now.getSeconds())+'秒'
      setTimeout(tick , 1000)
    }

    tick()

效果

练习

将对象数组中的内容打印出来,在点击相对应的科目时自动根据对应关键字排序

  <table>
    <thead>
      <tr>
        <th id="ID" class="click">ID</th>
        <th id="math">数学</th>
        <th id="cn">语文</th>
        <th id="en">英语</th>
        <th id="all">总分</th>
      </tr>
    </thead>
    <tbody id="tbody"></tbody>
  </table>
  <style>
    th,
    tr,
    td {
      text-align: center;
      border: 1px solid #000;
      padding: 10px;
    }

    table {
      border-collapse: collapse;
    }

    th {
      cursor: pointer;
      background: #fff;
    }

    .click {
      background: #ccc;
    }
  </style>
    // 先把总分算出来
    // 把数据显示到页面上
    // 把内容排序
    // 排序好的内容重新显示到页面上
    var data = [
      {
        id: 9527,
        math: 99,
        cn: 83,
        en: 56,
      },
      {
        id: 9528,
        math: 91,
        cn: 92,
        en: 89,
      },
      {
        id: 9529,
        math: 100,
        cn: 39,
        en: 77,
      },
      {
        id: 9530,
        math: 50,
        cn: 58,
        en: 48,
      },
      {
        id: 9531,
        math: 96,
        cn: 93,
        en: 84,
      },
    ]

    function showHTML(list) {  //传入一个数组,将数组内对象显示在页面上
      tbody.innerHTML = ''
      for (let i = 0; i < list.length; i++) {
        let obj = list[i]
        let total = 0
        let arr = []
        for (let key in obj) {
          total = key !== 'id' && key !== 'all' ? total + obj[key] : total
          arr.push(obj[key])
        }
        data[i]['all'] = total
        console.log(data[i]['all'])
        tbody.innerHTML += `<tr><td>${arr[0]}</td><td>${arr[1]}</td><td>${arr[2]}</td><td>${arr[3]}</td><td>${total}</td></tr>`
      }
    }

    //排序
    function objSort(arr, key) {
      arr.sort(function (a, b) {
        return b[key] - a[key]
      })
      showHTML(arr)
    }

    let name = ['id','math','cn','en','all']


    showHTML(data)


    let thAll = document.querySelectorAll('th')
    thAll.forEach(function (item, index) {      //循环DOM
      item.onclick = function () {
        for (let i = 0; i < thAll.length; i++) {
          thAll[i].className = ''
        }
        this.className = 'click'
        objSort(data, name[index])
      }
    })

Day5—BOM和DOM

BOM的所有窗口操作都可以省略window

浏览器版本信息

window.navigator.userAgent

可以获取到浏览器的版本信息,包括型号和内核

通常使用来获取设备型号,然后根据不同的型号做不同的操作

        if (navigator.userAgent.includes('Android')) {
            body.style.marginTop = '20px'
        } else if (navigator.userAgent.includes('iPhone')) {
            body.style.marginTop = '30px'
        } else {
            body.style.marginTop = '0'
        }

浏览器地址栏信息

window.location

获取当前网页的地址信息,使用.herf方法可以单独获取地址

可以通过地址栏信息获取到数据(query)

        let index = location.href.indexOf('?')
        console.log(index)
        console.log(location.href.slice(index + 1))

需要注意的是,当前JS已经有新方法替代上例了,location.search.slice(1)可以直接获取?后的内容

使用.reload()方法可以重新加载当前页面,等于F5刷新,括号内可以填参数,填true表示强制刷新,等于ctrl+f5

使用.hash属性来获取到#后面的值

浏览器会话历史

history

配合不同的方法使用

.back()转到会话历史的上一页,等价于用户点击浏览器的后退按钮

.forward()转到会话历史的下一页,等价于用户点击浏览器的前进按钮

.go()参数为正数时等价于.back(),参数为负数时等价于.forward(),参数为0时刷新

DOM

DOM,全称为文档对象模型,是操纵标签的

获取DOM对象

在前文中我们直接使用ID操纵标签,这是不规范的,正确的做法应该是先获取再进行操作

获取方法有如下几种

方法 作用
document.getElementById() 根据id名获取元素,id唯一但如果有相同则默认第一个
document.getElementsByTagName() 根据标签名获取一组元素
document.getElementsByClassName() 根据class名获取一组元素
document.querySelector() 使用选择器获取一个元素,如果相同则默认第一个
document.querySelectorAll() 使用选择器获取一组元素

后两个方法实际上已经完美替代了前三种方法

我们获取到的DOM元素是对象形式,可以使用一系列方法或属性

操作DOM对象

之前我们用过innerHTML在DOM对象内输出一些东西,而他就是DOM对象提供的方法之一

还有一种打印文本的方法是innerText

它们两者的区别是innerHTML可以解析标签,而innerText不解析标签,只负责打印本文

如果直接使用innerHTML或者innerText而不进行赋值操作,他会获取当前DOM对象的子节点内容

只有赋值才会修改内容,这点和对象的读取和修改很像,因此获取到的DOM元素也被称为DOM对象

同时需要注意innerHTML是赋值操作,他会覆盖之前的内容,如果是想添加则需要使用+=

CSS属性

需要注意的是,HTML中在标签中直接书写的是属性,比如src、alt、href、style、title、name、value、placeholder、id、width、height

而CSS样式表内的是样式

两者中有起相同作用的属性和样式,但决不能将两者混淆