DOM&BOM总结

110 阅读15分钟

DOM&BOM总结

BOM

Browser Object Model 浏览器对象模型

用于操控浏览器相关的内容(地址栏 前进 后退 版本...)

1.window

js的顶级对象
全局的变量会作为window的属性
全局的函数会作为window的方法
​
    window.onresize
    //监听浏览器的大小变化 如果变化了 会自动执行后面的回调函数
    window.onresize=function(){
      console.log(111)
    }
    window.onscroll
    //监听浏览器的滚动 会触发的回调函数
    window.onscroll=function(){
      console.log(222)
    }

2.history 历史记录***

    // history 历史记录 ********
    console.log(window.history)
    console.log(history)//简写
    history.back() //后退一页
    history.forward() //前进一页
    history.go() //任意前进或者后退  里面写数字 1前进一页  -2后退两页

3.location 地址栏 ****** **

    
    console.log(window.location)
    console.log(location)//简写
    console.log(location.href) //当前的 url=》统一资源定位符
    location.href = 'http://www.baidu.com' //修改url
    console.log(location.protocol) //拿的是协议名
    console.log(location.host) //拿的是主机名称和端口号
    console.log(location.hostname) //拿的是主机名称
    console.log(location.port) //拿的是端口号
    console.log(location.pathname) //拿的是路径部分
    console.log(location.search) //拿的是查询部分
    console.log(location.hash) //那的是锚点部分

4.navigator 浏览器版本名称等

​
    console.log(navigator.appName) //浏览器的名称(mdn已弃用,在任何浏览器该属性的值都是Netscape,此属性仅出于兼容性目的而保留)
    console.log(navigator.appCodeName) //浏览器的内核 同上 Mozilla
    console.log(navigator.appVersion) //浏览器的版本 
    

DOM

1.事件

    事件的三要素:
    事件源(承受的这一方)     
    事件类型 点击 移入移出...
    事件回调 放置你想要执行的功能 回调函数
    //事件是可以重复执行的

2.修改元素的属性

2.1天生属性之普通属性

如src href  id ...  元素对象本来就自带的属性
    window.onload = function () {
      var imgNode = document.getElementById('imgNode')
      console.log(imgNode.scr = 'https://www.baidu.com') //修改天生属性之普通属性的值
​
      // 如果一个元素的id属性存在 那么可以不用dom方法获取 直接使用id那个名字就可以了
      btn.onclick = function () {
        aNode.href = 'http://www.jd.com' //点击修改href属性值
        pNode.id = 'ppppppp'//点击修改p元素的id属性值
      }

2.2天生属性之特殊属性

特殊属性 checked  disabled 如果属性和属性值同名 尽量用truefalse表示
class  操作类名属性不可以用class 因为他是js中的一个关键字 你必须用className这个名字
如 p.class='pp' ×,p.className='pp' √
​
        可以使用.语法 也可以使用[]语法  dom对象元素对象 本质也是对象
        pNode.id = 'ppp'
        pNode['id'] ='pppp'#p.clssList 可以拿到一个元素的所有类名,p.classList.add('a'),p.classList.remove('a') 添加移出类名

2.3自定义属性

为了区分自定义属性和天生属性的区别,用p.dataset.attr='自定义属性' ,浏览器显示:data-attr='自定义属性'
pNode.getAttribute('class')//获取自定义属性,特殊的是其操作class不需要写为'className'
pNode.setAttribute('xxx', 'zzz')//修改自定义属性

3.获取DOM元素的方法

1.通过id名获取 
document.getElementById('divNode') //id值唯一
2.通过标签名获取 
document.getElementByTagName('div') //不管是多少div 都会返回一个伪数组
3.通过类名获取 
document.getElementByClassName('divNodes') //不管是多少div 都会返回一个伪数组
4.通过css选择器获取
 document.querySelector('#ulNode')  //通过id选择器获取
 document.querySelectorAll('.one')//返回为数组

4.操作元素内容

读:
内部没有其他元素:innerHTML可以拿到换行和空白以及文本,innerText只能文本(若设置隐藏,则读取不到)
内部有其他元素:  innerHTML不光能拿到文本内容还可以拿到内部的标签, innerText只能拿到文本      
改:
内外没有其他元素:两者一样都会替代原有的
内部有其他元素:  innerHTML 可以让元素渲染到页面上;innerText 只能让内容渲染到页面上 元素当作普通文本对待
               如:box.innerHTML = '<h1>哈哈哈</h1>' 会以h1的样式显示‘哈哈哈’,而innerText则是'<h1>哈哈哈</h1>'
textContent可以拿到隐藏元素的内容,其他与innerText一样
innerText拿不到隐藏元素的内容

5.操作元素样式

#js中操作的样式都是行内样式 确保优先级!!
    window.onload = function () {
      //获取元素节点
      var btn = document.querySelector('#btn')
      var box = document.querySelector('#box')
      //添加事件
      btn.onclick = function () {
        //事件回调
        box.style.width = '300px'
        box.style.height = 300 + 'px'
        box.style.backgroundColor = '#0ff'    //-不符合变量写法,这里统一遇到改字母大写   
        //我试了下用background复合属性的写法也可以,颜色模式浏览器都解析成rgb()

      }

6.排他、标志位等

      //点击某个li  让这个li内容变呵呵 其他的变嘿嘿         练习 方法1
      var liNodes = document.querySelectorAll('li') //获取所有li 一个伪数组

      for (var i = 0; i < liNodes.length; i++) {       //点击某个 说明可能每个都点击,那么全要访问到
        liNodes[i].onclick = function () {             //确定事件源和点击事件以及事件回调
          for (var j = 0; j < liNodes.length; j++) {   //排他思想就是先全部 再排它  所有再for
            liNodes[j].innerHTML = '嘿嘿'
          }	
          this.innerHTML = '呵呵'        //在事件回调当中this指向你当前的事件源     这里不能直接用liNodes【i】去获取li了,因为i在点击前全部循环完了
        }
      }
方式二:
    window.onload = function () {
      var liNodes = document.querySelectorAll('li') //获取所有事件源,返回一个伪数组
      for (i = 0; i < liNodes.length; i++) {
        liNodes[i].index = i     //给每一个li加一个index属性 用于记录i 是几,因为循环不等点击 会喀喀喀全执行,到时候下面的liNodes[i],都是4
        //都用到了this,但是方向不一样,这里是通过对象加属性 来记录下标 调用属性
        liNodes[i].onclick = function () {
          for (j = 0; j < liNodes.length; j++) {
            liNodes[j].className = ''
          }
          liNodes[this.index].className = 'changeRed'

        }

      }

    }
````````````````````````````````````````````````````````````````````````````````````````````````````````````
2.二级菜单:
      1.获取dom对象
      var liNodes = document.querySelectorAll('.list>li')
      var ulNodes = document.querySelectorAll('li ul')
      // 2.给每个li绑定移入移出事件
      for (var i = 0; i < liNodes.length; i++) {
      // 给每个li对象添加index属性  属性值为下标 取下标的意义在于 li和li内部的ul用的下标是同一个
        liNodes[i].index = i
        liNodes[i].onmouseenter = function () {
          // ulNodes[this.index] 就是咱们想显示的ul
          ulNodes[this.index].style.opacity = 1
        }
        liNodes[i].onmouseleave = function () {
          ulNodes[this.index].style.opacity = 0
        }
      }
````````````````````````````````````````````````````````````````````````````````````````````````````````````
3.幽灵按钮
      //获取dom对象
      var wrap = document.querySelector('#wrap')
      var boxs = document.querySelectorAll('#wrap div')
      //添加事件
      wrap.onmouseover = function () {
        boxs[0].style.width = '200px'
        boxs[1].style.width = '200px'
        this.style.backgroundColor = '#f0f'
        this.style.color = '#fff'
      }
      wrap.onmouseout = function () {
        boxs[0].style.width = '0'
        boxs[1].style.width = '0'
        this.style.backgroundColor = '#069A8E'
        this.style.color = 'red'
      }

7.事件相关

7.1事件对象

事件对象:

每个事件回调 都不是咱们自己调用 是系统帮你调用 那么系统调用得时候给你偷摸塞了个东西

每一个事件回调 所有跟这个事件回调相关的信息都在事件对象身上

inp.onkeyup = function (e) {
    // e 就是事件对象  形参
    console.log(e)
    }

7.2鼠标事件

window.onload=function(){
      var box = document.querySelector('#box')
      // onclick 点击
      // 如果说对某个dom对象绑定同一事件多次 最后的那一次会执行 覆盖
      box.onclick=function(){
        console.log('我是点击2')
      }
      box.onclick=function(){
        console.log('我是点击1')
      }
      box.onclick=function(){
        console.log('我是点击3')
      }
    
     // 移入移出 onmouseover  onmouseout 成对使用
      box.onmouseover=function(){
        console.log('我是移入')
      }
      box.onmouseout=function(){
        console.log('我是移出')
      }

     // 移入移出 onmouseenter onmouseleave 成对使用
      box.onmouseenter=function(){
        console.log('我是移入')
      }
      box.onmouseleave=function(){
        console.log('我是移出')
      }
#注意:enter leave这一对切换看不到  不能用这一对去做事件委派!!! 父子切换 不显示的  
   //时间对象兼容性处理
    window.onload = function () {
      var box = document.getElementById('box')
      // 事件对象 每一个事件都会有各自的事件对象 系统给的
      box.onclick = function (e) {
        // 兼容性处理
        e = e || window.event // 能拿到e就拿,拿不到用winddow.event
        // e = event||window.event
        // event = event||window.event

        console.log(e) //低级浏览器不能通过形参拿到事件对象
        console.log(window.event) //低级浏览器 事件对象在window身上的event属性
      }
    }
7.2.1鼠标事件练习
1.表格隔行变色
    window.onload = function () {
      var trNodes = document.querySelectorAll('tr')
      //每行都要变颜色 
      for (var i = 0; i < trNodes.length; i++) {
        //隔行变色 需要判断单双 取余判断
        if (i % 2 == 0) {
          trNodes[i].style.backgroundColor = '#0ff'
        }
        else {
          trNodes[i].style.backgroundColor = '#f00'
        }
      }
    }
--------------------------------------------------------------------
2.开关控制盒子变色
window.onload=function(){
      var box = document.querySelector('.box')
      var flag = true //用flag这个变量来模拟开关
      box.onclick=function(){
        if(flag){//true 蓝色变成红色
          box.style.backgroundColor = 'red'
          // 更改开关得状态
          // flag = false
        }else{//false  红色变成蓝色
          box.style.backgroundColor = 'skyblue'
          // 更改开关得状态
          // flag = true
        }
        // 不管颜色变成哪个 if或者else之后得代码一定会执行
        // 状态取反
        flag = !flag
      }
    }
--------------------------------------------------------------------
3.开关控制风车停转
    window.onload = function () {
      var btn = document.querySelector('button')
      var ulNode = document.querySelector('ul')
      var flag = true
      btn.onclick = function () {
        if (flag) { //默认是true  true代表正在转
          ulNode.style.animationPlayState = 'paused'
        } else { //false 停 
          ulNode.style.animationPlayState = 'running'
        }
        // 状态取反
        flag = !flag
      }
    }

7.3键盘事件

onkeydown 键盘按下 //按键不抬起就会一致触发
onkeyup 键盘抬起  //只会执行一次 (常用)
*****************学键盘事件就是在学习区分是哪个键
场景:百度的搜索框 输入内容 自动联想

      // 判断是不是回车抬起事件
      inp.onkeyup = function (e) {
        // e 就是事件对象  形参
        var a = 0
        if (e.keyCode == 13) {
          console.log('回车抬起事件')
        } else if (e.keyCode == 32) {
          console.log('空格抬起事件')
        }
        console.log(e)
      }

7.4焦点事件

    onfocus 获取焦点
    onblur 失去焦点

var inp = document.querySelector('input')
      // onfocus 获取焦点
      inp.onfocus = function () {
        console.log('获取焦点')
      }
      // onblur 失去焦点
      inp.onblur = function () {
        console.log('失去焦点')
      }
7.4.1焦点事件练习
// 当失去焦点的时候让input的背景颜色随机改变(变色龙)
    window.onload = function () {
      var inp = document.querySelector('input')
      // 获取焦点
      inp.onfocus = function () {
        this.style.backgroundColor = 'skyblue'
        this.style.color = 'cyan'
      }
      // 失去焦点
      inp.onblur = function () {
        // 这三个变量 给随机背景色用
        var r1 = Math.floor(Math.random() * 256)
        var g1 = Math.floor(Math.random() * 256)
        var b1 = Math.floor(Math.random() * 256)

        // 这三个变量 给随机的字体颜色去用
        var r2 = Math.floor(Math.random() * 256)
        var g2 = Math.floor(Math.random() * 256)
        var b2 = Math.floor(Math.random() * 256)
        // 拼串得时候 要注意 最好按我得方式来  不容易拼错
        this.style.backgroundColor = 'rgb(' + r1 + ',' + g1 + ',' + b1 + ')'  //除了变量 其他都是字符串 
        this.style.color = 'rgb(' + r2 + ',' + g2 + ',' + b2 + ')'       //这里也可以改变r1,g1,b1的顺序 但我觉得不严谨
      }
    }

7.5案例

window.onload = function () {
      var btns = document.querySelectorAll('button')
      var inps = document.querySelectorAll('input')
      // 全选的逻辑
      btns[0].onclick = function () {
        for (var i = 0; i < inps.length; i++) {
          inps[i].checked = true
        }
      }
      // 全不选的逻辑
      btns[1].onclick = function () {
        for (var i = 0; i < inps.length; i++) {
          inps[i].checked = false
        }
      }
      // 反选的逻辑
      btns[2].onclick = function () {
        for (var i = 0; i < inps.length; i++) {
          #新的状态要依托于之前的状态 取反
          inps[i].checked = !inps[i].checked
        }
      }
    }

8.兼容性问题

    浏览器的兼容性:
    Chrome  fireFox   IE   safari  opera
    浏览器分为高级浏览器和低级浏览器
    以IE8为分水岭,包含IE8在内以下的就是低级浏览器
    IE8以上和其它的都是高级浏览器
    (兼容性处理将会越来越少)

    window.onload = function () {
      // 兼容性问题 了解即可 不用你刻意得去记 
      // getElementsByClassName 高级可以用 低级用不了
      // querySelector  高级可以用 低级用不了
      // querySelectorAll  高级可以用 低级用不了
 
      var box = document.getElementById('box')//id名获取 ,高低都行
      // var box1 = document.getElementsByClassName('content')//类名获取
      
      var box2 = document.getElementsByTagName('div')//标签名获取  高低都行
      // var box3 = document.querySelector('.content')//选择器获取
      
      console.log(box)
      // console.log(box1)
      console.log(box2)
      // console.log(box3)
    }

#通过兼容性练习函数封装
innerText和innerHTML 高级浏览器和低级浏览器都可以用
textContent 只能高级浏览器去用
      function clearContent(node, content) {
        // 在读取内容
        if (arguments.length == 1) {        //arguments属性获取函数实际参数 低级浏览器是undefined 布尔值就是false
          // return node.textContent ? node.textContent : node.innerText 优化1
          return node.textContent || node.innerText           //也可以这样写
          //在设置内容 
        } else if (arguments.length == 2) {
          node.textContent ? node.textContent = content : node.innerText = content
          // (node.textContent || node.innerText )=content   //这样写报错的,因为做左边是一个表达式的值,值赋值肯定不对的
        }
      }

9.节点 、元素

//节点在dom中的展示
                      nodeName(次要关注)     nodeType(主要关注)   nodeValue
      元素节点           标签名大写              1                 null
      文本节点            #text                 3               文本内容
      注释节点           #comment               8               注释内容
 
//每一个换行都是是文本节点 text     
  1.子节点 childNodes(包括文本 元素 注释)
  2.子元素节点 children(只拿子元素节点)
  
//父节点和父元素节点 出来的结果都是父元素节点  
  3.父节点  parentNode
  4.父元素节点 parentElement

//特殊节点
document.documentElement 获取页面当中的html元素
document.body            获取页面当中的body元素 
document.head            获取页面当中的head元素

//查找节点的其他方式
var liNode = document.getElementById('item') 获取这些节点的时候 只要名字带Element 低级浏览器全用不了(兼容性现在考虑的少)
   // 父子关系
      // childNodes  children  parentNode   parentElement
      // 第一个子节点  firstChild
      console.log(list.firstChild)
      // // 第一个子元素节点  firstElementChild
      console.log(list.firstElementChild)
      // // 最后一个子节点  lastChild
      console.log(list.lastChild)
      // // 最后一个子元素节点  
      console.log(list.lastElementChild)

      // 兄弟关系
      // 上一个兄弟节点  previousSibling
      console.log(liNode.previousSibling)
      // // 上一个兄弟元素节点  previousElementSibling
      console.log(liNode.previousElementSibling)
      // 下一个兄弟节点
      console.log(liNode.nextSibling)
      // 下一个兄弟元素节点
      console.log(liNode.nextElementSibling)
``````````````````````````````````````````````````````````````````````````````````````````
      //封装获取最后一个子元素节点
     (获取这些节点的时候 只要名字带Element 低级浏览器全用不了)
      var ulNode = document.getElementById('list')
      // 封装函数返回某个节点的最后一个元素节点
      function getElementLastChild(node) {       //函数声明
        // 区分开是低级还是高级
        if (node.LastElementChild) {
          return node.LastElementChild //高级浏览器 直角返回就好
        } else {//低级

          var result = node.lastChild//拿节点的最后一个节点
          // 特殊处理 某个节点当中没有子元素节点
          while (result !== null && result.nodeType !== 1) {//如果不是子元素节点 那么就取他的上一个兄弟节点再进来判断

            result = result.previousSibling
          }
          return result
        }
      }
      console.log(getElementLastChild(ulNode))
    }
    //重在练习函数封装  兼容性处理会越来越少了

10.添加节点的方式

  <script>
    window.onload = function () {
      var pNode = document.getElementById('pp')
      // 添加节点的三种方式
      // 1. 基本不用 可以把里面的标签渲染到结构中  但是会覆盖我之前就渲染好的结构
      document.write('<h2>我是内容</h2>')

      // 2. innerHTML += 很少用
      document.body.innerHTML = '<h2>我是第二种方式</h2>'
      document.body.innerHTML += '<h2>我是第二种方式</h2>' 不覆盖

      // 3. 常用  三个步骤 缺一不可 ************
      // 3.1 创建dom对象
      var divNode = document.createElement('div') //创建一个元素  传递的参数是你要创建的元素名字
      // 3.2 给这个新创建的dom对象增加内容
      divNode.innerHTML = '我是新创建的div'
      // 3.3 把这个新创建的dom对象 放在dom树上 *******
      document.documentElement.appendChild(divNode)//appendChild是dom对象身上的方法  可以再某个节点中(内部)末尾增加一个子节点
    }

11.操作节点的方法

    // 节点增删改  让父元素去使用的!!!
    window.onload=function(){
      // 增加节点 (末尾) 父元素调用
      var list =document.querySelector('ul')
      var liNode = document.createElement('li')
      liNode.innerHTML = '星际穿越'
      // list.appendChild(liNode)

      var item = document.getElementById('item')
      // 插入节点
      // insertBefore 父元素调用  传参两个 第一个是你要加的节点 第二个是参照节点(新节点要加到哪个节点前面)
      list.insertBefore(liNode,item)

      // 替换节点 
      // replaceChild  父元素调用  第一个是你新节点 第二个是要被替换的节点
      list.replaceChild(liNode,item)

      // 删除节点
      // removeChild 父元素调用 参数是你要删除的那个节点
      list.removeChild(item)

      // 删除ul
      document.body.removeChild(list)
    }

12.综合案例

str.trim()      //trim是字符串的一个方法  去除掉左右两边的空白
str.trimLeft()  //trim是字符串的一个方法  去除掉左边的空白
str.trimRight() //trim是字符串的一个方法  去除掉右边的空白
//动态添加歌曲
window.onload = function () {
      // 1.获取input
      var inp = document.querySelector('input')
      // 7. 获取ul
      var ulNode = document.querySelector('ul')
      // 2.绑定键盘抬起事件

      inp.onkeyup = function (event) {
        // 3.判断是不是按的回车 
        if (event.keyCode == 13) {
          //4.获取input的输入内容
          var content = inp.value //content拿出来是字符串格式
          // 10.有些用户可能输入了一些空白
          if (content.trim()) { //走到这个判断里面的  一定是有输入内容的
            // 5. 创建li标签
            var liNode = document.createElement('li')
            // 6.添加li的内容
            liNode.innerHTML = content
            // 12.每次只要创建了li 我就给他添加移入移出事件
            liNode.onmouseenter = function () {
              this.style.backgroundColor = 'red'
            }
            liNode.onmouseleave = function () {
              this.style.backgroundColor = ''
            }
            // 8.把新建的li加入dom树当中
            ulNode.appendChild(liNode)
          } else {
            alert('请输入你喜欢的歌曲')
          }
          // 不管是有美欧输入正确的歌曲名字  都要清空
          inp.value = ''
        }
      }
    }

13.DOM事件

DOM级别一共可以分为4个级别:DOM0级「通常把DOM1规范形成之前的叫做DOM0级」,DOM1级,DOM2级和 DOM3级,而DOM事件分为3个级别:DOM0级事件处理,DOM2级事件处理和DOM3级事件处理。如下图所示:

DOM级别与DOM事件.jpg

13.1 dom0事件绑定和解绑

//如果给某个dom对象 绑定多次同一事件 只会执行最后一次 后面对的覆盖前面的
      box.onclick = function () {
        console.log('点击1')

      }
box.onclick = null //通常解绑一个dom对象的事件 咱们会赋值为null,但是并不是只有null可以 只要是让他断掉与函数的连接就可以!

13.2 dom2事件绑定和解绑

//dom2绑定事件,第三个参数默认false 代表冒泡,true捕获
box.addEventListener('click', function () {
        console.log('dom2方式   点击1')
      })

解绑事件 //解绑的时候传递的参数必须和绑定的参数一摸一样(留意第二个参数 回调函数)
      function fn() {
        console.log('dom2方式   点击4')
      }
      box.addEventListener('click', fn)
      btn.onclick = function () {
        box.removeEventListener('click', fn)
      }

14.事件流

1)捕获事件流(网景)    最终很少用几乎不用
2)冒泡事件流(ie)     最终我们所用的事件传播都是冒泡
3)标准DOM事件流      //这个是我们现用的标准事件流,里面包含三个阶段: 有捕获  再去获取目标元素   最后冒泡,这个三个阶段当中的捕获和冒泡可以由程序员自己选择。但是通常情况我们都是使用默认 (冒泡);

14.1 dom0事件流

dom0当中事件流只有冒泡 没有捕获
      冒泡是一个过程  从内向外  (从自身向外)
      事件流(事件传播)是客观存在的 和事件监听一毛钱关系都没有   //(同一类型就会触发,并不是非要同一个,例如鼠标点击事件和鼠标按下事件)

14.2 dom2事件流

one.addEventListener('click', function () {
        console.log('我是老大')
      }, false) 
/*
冒泡 由内向外依次传递
捕获 从外向内依次传递
标准事件流  三个阶段 捕获 目标 冒泡(用的多)
*/

14.3 阻止冒泡

function fn(e) {
        // 阻止冒泡
        console.log('我是老二')
        // e.stopPropagation();
      }
      two.addEventListener('click', fn)

15.事件委派

事件委派: 是依衬于事件冒泡的 如果没有冒泡 就不能用事件委派,也叫事件委托 事件代理

15.1事件委派场景一:

// 每一个子元素都在做相同的事情
//案例:移入li 让li变色
    window.onload = function () {
      // 给这些子元素最近的那个父元素添加事件
      var ulNode = document.querySelector('ul')
      ulNode.onmouseover = function (e) {
        // 事件流(事件传播)是客观存在的 和事件监听一毛钱关系都没有
        // 移入li的时候 会冒泡到ul身上 触发ul的事件监听   
        // 需要找到具体那一个子元素 让他变色
        // e.target  代表的是触发事件的最里面(内部)的那个元素
        // e.target有可能拿到的是ul  也有可能拿到的是li
        if (e.target.nodeName === 'LI') {
          e.target.style.backgroundColor = 'aqua'
        }
      }
      ulNode.onmouseout = function (e) {
        if (e.target.nodeName === 'LI') {
          e.target.style.backgroundColor = 'green'
        }
      }
    }

15.2事件委派场景二:

//之前老的做的事情一样  然后会增加新的  想让新的也具备老的的功能    
window.onload=function(){
      // 事件委派的玩法
      var btn = document.querySelector('button')
      var ulNode = document.querySelector('ul')
      btn.onclick=function(){
        var liNode = document.createElement('li')
        liNode.innerHTML='我是新的li'
        ulNode.appendChild(liNode)
      }
      // 事件委派玩法 必须用over out
      ulNode.onmouseover=function(e){
        // console.log(e.target)
        if(e.target.nodeName ==='LI'){
          e.target.style.backgroundColor = 'pink'
        }else if(e.target.nodeName ==='SPAN'){
          e.target.parentNode.style.backgroundColor = 'pink'
        }
      }
      ulNode.onmouseout=function(e){
        if(e.target.nodeName ==='LI'){
          e.target.style.backgroundColor = ''
        }
      }

    }

16.相亲墙案例

    window.onload = function () {
      var inps = document.querySelectorAll('input')
      var btn = document.querySelector('button')
      var tbody = document.querySelector('tbody') //dom上是由tbody标签的
      // 添加
      btn.onclick = function () {
        // 获取用户输入的姓名和特点
        var name = inps[0].value
        var trait = inps[1].value
        // 判断用户输入的内容是不是正确的
        if (name.trim() && trait.trim()) {
          // 创建tr结构
          var trNode = document.createElement('tr')
          // 创建姓名的td
          var tdNode = document.createElement('td')
          tdNode.innerHTML = name
          trNode.appendChild(tdNode)

          // 创建特点的td
          var tdNode = document.createElement('td')
          tdNode.innerHTML = trait
          trNode.appendChild(tdNode)

          // 创建删除的td
          var tdNode = document.createElement('td')
          tdNode.innerHTML = '<a href="javascript:;">删除</a>'
          trNode.appendChild(tdNode)

          // 把咱们自己创建的tr放到tbody中 (树上)
          tbody.appendChild(trNode)

        } else {
          alert('请输入你的姓名和特点')
        }
        inps[0].value = ''
        inps[1].value = ''
      }
      // 删除
      tbody.onclick = function (e) {
        e = e || window.event
        if (e.target.nodeName === 'A') {
          // 找到这个a对应的那个tr  删除tr
          console.log(e.target.parentNode.parentNode)
          tbody.removeChild(e.target.parentNode.parentNode)
        }
      }
    }

17.三种鼠标位置

clientX  clientY 参照视口 来获取你鼠标的位置 常用
offsetX  offsetY 参照自身元素的左上角 来获取你鼠标的位置
pageX  pageY  参照页面的左上角 来获取你的鼠标位置

17.1鼠标跟随案例

 window.onload = function () {
      /*鼠标移动  给谁绑定?
      body?html?一般不给这2个特殊的绑定事件,那么基于事件冒泡特性 我们可以给document绑定
      */
      var img = document.querySelector('img') //获取img  
      document.onmousemove = function (e) { //事件绑定
        // console.log(e.clientX)
        //让图片始终跟着图片
        e = e || window.event //兼容性处理
        img.style.left = e.clientX + 'px'  //设置水平方向定位
        img.style.top = e.clientY + 'px'    //设置垂直方向定位
      }
    }

18.定时器

18.1延时定时器

1.语法:setTimeout(回调函数,毫秒值)
分析:其实就是一个函数调用的表达式,不过参数是回调函数整体和毫秒,内置封装了,那么表达式的值就是定时器的id。从2开始往后排

执行过程:等到某个时间 去执行回调函数当中的代码块 一次!!!

返回值:每个定时器都会返回一个东西  这个东西就是定时器的id

#因为咱们之后可能在任意位置去清除定时器  所以一定要把定时器返回的id做为全局变量保存 
 // 或者在全局有个var id=null;然后定时器里的函数 id 不带var   ,那么返回的id就操作了全局变量的id了

2.清除定时器
 clearTimeout(id)

18.2循环定时器

语法:
      setInterval(回调函数,毫秒值)
执行过程:
      每隔一段时间(你赋予的) 都会去执行一次回调函数当中的代码块  无法确定执行多少次
      类似死循环  但是和死循环不一样
      定时器每个都会返回一个唯一的id  按照顺序依次排列
// 清除循环定时器 
      clearInterval(id)

18.3定时器有时候不准的问题(了解)

    因为想要执行异步代码 必须执行完所有的同步代码 
    但是同步代码当中有可能执行了5s 那定时器的1s早就过了

18.4万年历练习

      //万年历案例练习
      /* 分析题意:实时年月日 时间(可见最小变换就是秒)   循环定时器 每间隔1s 就获取一次时间
      */
      var h1Node = document.querySelector('h1') //获取元素节点
      //封装一个实时时间函数
      function getNowDateAndTime() {
        var date = new Date()
        var year = date.getFullYear()
        var month = date.getMonth() + 1//月份默认0-11的
        var day = date.getDate()
        // var time = date.toLocaleDateString()2022/4/26
        var time = date.toLocaleTimeString()
        return '现在是' + year + '年' + month + '月' + day + '号 ' + time
      }
      //添加定时器
      setInterval(function () {
        var time = getNowDateAndTime()// 每间隔1s 就获取一次时间
        h1Node.innerHTML = time //看着是秒变了 其实每一次都是整体变掉

      }, 1000)

18.5阅读协议案例

      var btn = document.querySelector('button')
      num = 5
      setInterval(function () {
        if (num <= 1) { //不加判断 就一直减下去了
          // btn.style.disabled = false  //这里是改的属性  不要加style的
          btn.disabled = false
          btn.innerHTML = '确认'
        } else {
          num--
          btn.innerHTML = '确认(' + num + ')'
        }
      }, 1000)
    }

19.同步与异步

/js的代码有同步代码和异步代码
异步代码:定时器当中的回调函数   事件回调//不是所有的回调函数都是异步的 像数组方法当中的回调函数就不是异步的

    程序  就是一个文件 静静的躺在你的硬盘(c盘 d盘)当中  静态的
    进程  程序执行叫做进程  进程是动态的
    
    进程  车间
    线程  线程是进程的基本执行单位  工人
    一个进程所有的任务都在线程当中去执行的 进程想要执行任务 必须的有线程

    js是单线程(只有一个工人)
    同步代码一定优先于异步代码执行  异步代码想要执行 必须等我所有的同步执行完才行
    
 /*
    执行过程:进程先执行第一行同步代码,碰到异步代码就放入管理模块(管理模块里也会分定时器管理模块 事件回调管理模块)中不执行,
    继续执行后面的同步代码,直到同步代码执行完,有消息返回后,系统通知进程将异步代码拿出来放入 call queue 相应时间后执行     */  
    
 /*
 执行过程:查资料发现,代码执行会有一个执行栈call stack,所有代码都在里面,从一开始执行,同步就执行,如果是事件回调就放模块管理  里面,模块管理   里面也会细分,如事件回调 定时回调函数等等,(如果是点击事件,点击后才会放入执行队列里面,call queue,定时回掉函数时间到了就放进去,按顺序)线程执行完所有同步代码后,会返回任务队列去执行队列里拿出来放入执行线程后面执行 。
  事件循环(event loop)主线程重复的获得任务 执行任务 再获取任务,再执行                                       
   */
          
  

20.元素的大小与位置

一共是有12个 讲六个 剩下六个是一样的

20.1元素的大小

clientWidth //盒子的宽+盒子的padding
offsetWidth //盒子的宽+盒子的padding+盒子的边框
scrollWidth
           1. 内容(three)小于盒子(two)  拿的是盒子(two)的clientWidth
           2. 内容(three)大于盒子(two)  拿的是内容的offsetWidth+内容一侧的外边距+盒子一侧的内边距

20.2元素的位置

clientLeft //拿的是盒子的左边框
clientTop //拿的是盒子的上边框

offsetLeft //拿的是盒子的偏移量(left) 不是一样要定位才行 参照点(视口 开启相对定位的父元素都可以)
          //只要是定位的移动  就看定位移动的值就行;

scrollLeft //只有scrollLeft 可读可写
          //内容往左侧滚动的距离 而不是滚动条滚动的距离!!!! 设置在有内容的元素上,才能溢出滚动
         //不设置点击滚动的话,他打开会默认计算滚动位置了 一直是0
        // two.scrollLeft = 70  //scrollLeft 设置的时候注意不要加px

21.浏览器的默认行为

//例如点击a标签会刷新页面,这就是默认行为
    window.onload = function () {
      var aNode = document.querySelector('a')
      aNode.onclick = function (e) {
        // 阻止默认行为 需要用到事件对象
        e.preventDefault()
      }
    }

22.案例练习

1.导航跟随案例

      var viewH = document.documentElement.clientHeight//获取视口高度  clientHeight就是获取盒子的高和上下padding
      var nav = document.querySelector('.nav')  //获取div dom元素
      //获取 滚动距离 是body页面的滚动距离
      //绑定页面滚动事件
      window.onscroll = function () {
        // 有些浏览器认为页面滚动的距离是html的 有些认为是body的
        // 兼容去拿滚动距离
        var disT = document.documentElement.scrollTop || document.body.scrollTop
        if (disT > viewH) {
          nav.style.position = 'fixed'      //写的样式是字符串  不要忘记加引号
          //nav.style.position = 'absolute'//绝对定位默认参照根元素 可这里不行?  固定定位时相对页面整体内容定位的,而绝对定位默认是视口
          //那这里视口已经和页面整体内容不一样了
          nav.style.left = 0
          nav.style.top = 0
        } else nav.style.position = 'static';
      }

2.盒子来回移动

      var box = document.querySelector('#box')//获取元素对象
      var dis = 3
      setInterval(function () {
        var eleX = box.offsetLeft //获取起始位置的偏移量
        var lastX = eleX + dis //获取结束位置的偏移量
        //加入判断 不然盒子一直向右走
        if (lastX > document.documentElement.clientWidth - box.offsetWidth) { //判断盒子结束位置距离是否大于视口宽度减去盒子本身宽度
          lastX = document.documentElement.clientWidth - box.offsetWidth //到还有一个盒子宽的距离停下来
          dis = -3 //往回走
        }
        // else {
        //   dis = 3 // 这样写的话会一直左右一步来回走;因为大于就相等,那么小于等于就往前走,又大了又退 就一直循环了
        // }
        else if (lastX < 0) {//要有个明确的判断,锁定左边临界值
          lastX = 0 //走的距离 可能不能刚好走完这么完美 可能最后一超了,还是当小的时候 就给他lastX初始到0比较好
          dis = 3 //往右走
        }
        box.style.left = lastX + 'px'
      }, 16)

3.系统滚动条的控制

    /* 
     overflow: scroll; overflow: hidden;
    body单独scroll document的 了解
    html单独scroll document的 了解
    body scroll html hidden  开启的滚动条是body自己的 了解
    body hidden  html scroll  开启的滚动条是document的 了解
    body scroll  html  scroll body的滚动条开启了  document的滚动条也开启了  了解 
    */
   # body hidden  html  hidden  关闭了系统滚动条  熟记!!!
   html,body{
       height:100%;//更有说服力,只有内容超出了才会被隐藏或滚动条,若不设置,bodyhtml的高度是由内容撑开的,body内容永远不会溢出了。
       overflow:hidden;
   }

4.拖拽完整版

    // 事件冒泡
    // 事件委派

    // 一
    // 1.拖拽元素很快 鼠标出来了
    //   计算机计算的速度跟不上你移动的速度
    // 2.出来之后移动鼠标 元素不跟鼠标动
    //   你给box添加的移动事件
    // 解决:不给box绑定移动了 给document绑定移动
    // 3.出来之后 抬起鼠标(document身上抬起的) 再移入元素 跟着我又动了
    //   没有触发box的抬起事件 所以没有解绑box的移动事件
    // 解决:不给box绑定抬起了 给document绑定抬起事件
    // 二
    // 点击元素 crtr+a 选中文本 拖拽文本 抬起元素会立马跑过来
    // 这是因为浏览器有默认行为
    window.onload = function () {
      // 元素最终的位置 = 元素的起始位置 + 鼠标前后的距离差
      var box = document.querySelector('div')
      var imgNode = document.querySelector('img')
      // 绑定按下事件
      box.onmousedown = function (e) {
        e = e || window.event
        // 需要box自身偏移的位置
        var eleX = box.offsetLeft
        var eleY = box.offsetTop
        // 需要鼠标按下的这个位置
        var startX = e.clientX
        var startY = e.clientY
        // 移动事件得写在按下时间里面 因为是在按下得时候去移动的
        document.onmousemove = function (e) {
          e = e || window.event
          // 要鼠标移动之后的位置
          var endX = e.clientX
          var endY = e.clientY
          // 求鼠标移动前后的距离差
          var disX = endX - startX
          var disY = endY - startY
          // 元素最终位置
          var lastX = eleX + disX
          var lastY = eleY + disY
          // 边界值判断 在把这个值设置到left top之前
          // 水平距离边界值
          if (lastX > document.documentElement.clientWidth - box.offsetWidth - 50) { //右边界
            lastX = document.documentElement.clientWidth - box.offsetWidth
          } else if (lastX < 50) { //左边界
            lastX = 0
          }
          // 垂直距离的边界值
          if (lastY > document.documentElement.clientHeight - box.offsetHeight - 50) { //下边界
            lastY = document.documentElement.clientHeight - box.offsetHeight
          } else if (lastY < 50) { //上边界
            lastY = 0
          }
         // 元素动起来
          box.style.left = lastX + 'px'
          box.style.top = lastY + 'px'
          // 碰撞效果
          var boxL = lastX + box.offsetWidth
          // getBoundingClientRect() 会返回这个dom对象在视口的位置  值是一个对象
          // console.log(imgNode.getBoundingClientRect(),'imgNode.getBoundingClientRect()')
          var imgL = imgNode.getBoundingClientRect().left

          var boxT = lastY + box.offsetHeight
          var imgT = imgNode.getBoundingClientRect().top

          var boxR = lastX
          var imgR = imgNode.getBoundingClientRect().left + imgNode.offsetWidth

          var boxB = lastY
          var imgB = imgNode.getBoundingClientRect().top + imgNode.offsetHeight

          if (boxL < imgL || boxT < imgT || boxR > imgR || boxB > imgB) { //碰不到
            imgNode.src = './img/1.jpg'
          } else { //碰到
            imgNode.src = './img/2.jpg'
          }
        }
        // 绑定抬起事件
        document.onmouseup = function () {
          // 抬起的时候 不能再让他移动了 解绑移动事件
          // 最好把抬起事件也解绑掉
          document.onmousemove = document.onmouseup = null
        }
        // 清除浏览器的默认行为
        e.preventDefault()
      }
    }
#可以将拖拽设为模板复用

5.滚轮事件

 <div id="box"></div>
  <!-- 滚动滚轮 改变某个元素得高度 -->
  <script>
    window.onload=function(){
      var box =document.querySelector('#box')
      // onmousewheel滚轮事件
      // 学习滚轮事件 要知道究竟是往上滚 还是往下滚
      box.onmousewheel=function(e){
        // console.log('滚轮滚动了')
        // 往上滚还是往下滚看e.wheelDelta大于0还是小于0
        if(e.wheelDelta>0){ //往上滚
          box.style.height = box.offsetHeight -10+'px'
        }else if(e.wheelDelta<0){ //往下滚
          box.style.height = box.offsetHeight +10+'px'
        }
      }
    }

6.自定义滚动条+带内容+滚轮事件

//绝对定位是相对于浏览器窗口而言的或者说内容?,会随着滚动条移动而移动
//固定定位是相对于屏幕内网页窗口而言或者说视口?,滚动条移动他不动
 window.onload = function () {
      // 元素最终的位置 = 元素的起始位置 + 鼠标前后的距离差
      var box = document.querySelector('#scrollin')
      var content = document.querySelector('#content')
      // 模拟内容
      for (var i = 0; i < 400; i++) {
        content.innerHTML += i + '</br>'
      }
      // 自定义滚动条的万能比例:
      // 滑块的高度 / 滑槽的高度 = 滑槽的高度 / 内容的高度 = 滑块的滚动距离 / 内容的滚动距离 
      // 滑块的高度不能写死了 得根据内容发生变化  内容多 滑块小 内容少滑块大
      // 求比例
      var scale = document.documentElement.clientHeight / content.offsetHeight
      var boxHeight = document.documentElement.clientHeight * scale
      box.style.height = boxHeight + 'px'
      // 滚轮逻辑
      // 给document添加滚轮事件
      document.onmousewheel = function (e) {
        e = e || window.event
        if (e.wheelDelta > 0) { //往上滚
          // 滑块动
          // 最新得滑块得位置
          var scrollinDis = box.offsetTop - 10
          // 上边界判断
          if (scrollinDis < 0) {
            scrollinDis = 0
          }
          box.style.top = scrollinDis + 'px'
          // 内容也得动
          // 自定义滚动条的万能比例:
          // 滑块的高度 / 滑槽的高度 = 滑槽的高度 / 内容的高度 = 滑块的滚动距离 / 内容的滚动距离 
          content.style.top = -scrollinDis / scale + 'px'  //注意内容是负值
        } else if (e.wheelDelta < 0) { //往下滚
          // 滑块动
          var scrollinDis = box.offsetTop + 10
          // 下边界判断
          if (scrollinDis > document.documentElement.clientHeight - box.offsetHeight) {
            scrollinDis = document.documentElement.clientHeight - box.offsetHeight
          }
          box.style.top = scrollinDis + 'px'
          // 内容也得动
          content.style.top = -scrollinDis / scale + 'px'  //注意内容是负值
        }
      }
      // 绑定按下事件
      box.onmousedown = function (e) {
        e = e || window.event
        // 需要box自身偏移的位置
        var eleY = box.offsetTop
        // 需要鼠标按下的这个位置
        var startY = e.clientY
        // 移动事件得写在按下时间里面 因为是在按下得时候去移动的
        document.onmousemove = function (e) {
          e = e || window.event
          // 要鼠标移动之后的位置
          var endY = e.clientY
          // 求鼠标移动前后的距离差
          var disY = endY - startY
          // 元素最终位置
          var lastY = eleY + disY
          // 边界值判断 在把这个值设置到left top之前
          if (lastY > document.documentElement.clientHeight - box.offsetHeight) { //下边界
            lastY = document.documentElement.clientHeight - box.offsetHeight
          } else if (lastY < 0) { //上边界
            lastY = 0
          }
          // 自定义滚动条的万能比例:
          // 滑块的高度 / 滑槽的高度 = 滑槽的高度 / 内容的高度 = 滑块的滚动距离 / 内容的滚动距离 
          // 内容滚动距离  别忘给content定位  别忘是负值!!!!
          content.style.top = -lastY / scale + 'px'
          // 元素动起来
          box.style.top = lastY + 'px'
        }
        // 绑定抬起事件
        document.onmouseup = function () {
          // 抬起的时候 不能再让他移动了 解绑移动事件
          // 最好把抬起事件也解绑掉
          document.onmousemove = document.onmouseup = null
        }
        // 清除浏览器的默认行为
        e.preventDefault()
      }
    }

7.轮播图完整版

1.轮播图-点击按钮动起来(封装版)
2.轮播图-无缝轮播
3.轮播图-小圆点变色
4.轮播图-小圆点点击
5.轮播图-定时器叠加
6.轮播图-定时器自动轮播
   // transition
    // 1. 没有办法去跟踪到中间状态的变化
    // 2. 过渡他只能应用在有具体数据的属性上
    window.onload = function(){
      var wrap = document.querySelector('.wrap')
      var boxs = document.querySelectorAll('.wrap>div')
      var list = document.querySelector('.list')
      var liNodes = document.querySelectorAll('.dots li')
      var timeAll = 600  //点一次移动一张图片的时间 总时间
      var stepTime = 20  //每走一步需要的时间
      var id = null
      var isMove = false //这个变量去控制你的轮播图有没有动
      var autoLoopId = null

      // 移入最外面的wrap这个div  让箭头显示出来
      wrap.onmouseover=function(){
        boxs[0].style.opacity = '1'
        boxs[1].style.opacity = '1'
        // 移入 清除轮播图自动轮播得定时器
        clearInterval(autoLoopId)
      }
      // 移出最外面的wrap这个div  让箭头隐藏出来
      wrap.onmouseout=function(){
        boxs[0].style.opacity = '0'
        boxs[1].style.opacity = '0'
        autoLoop()
      }
      // 点击右边按钮的逻辑
      boxs[1].onclick = function(){
        move(true)
      }
      function autoLoop(){
        // 自动轮播
        autoLoopId = setInterval(function(){
          move(true)
        },2000)
      }
      // 页面一打开就得轮播 默认让他执行一次
      autoLoop()
      // 小圆点点击
      for(var i =0;i<liNodes.length;i++){
        liNodes[i].index =i
        liNodes[i].onclick=function(){
          // this.index+1*-600  图片下标等于小圆点下标+1  图片下标*-600 就是要去得那个位置
          // 可以拿到小圆点下标  反推图片下标  推出可以拿到ul最终得位置
          move((this.index+1)*-600)
        }      
      // 点击左边按钮的逻辑
      boxs[0].onclick = function(){
        move(false)
      }
      function move(flag){
        // 第一次ismove是false 不会return
        // 第二次ismove是true  不会往下走了 因为碰到了return
        if(isMove){
          return
        }
        isMove = true
        if(typeof flag =='boolean'){ //说明是点的左右按钮
          if(flag){ //为真证明点的是右边按钮
            var disX = -600  //移动的距离差
          }else{ //为假证明点的是左边按钮
            var disX = 600  //移动的距离差
          }
        }else{ //点的小圆点
          // 求距离差
          var disX = flag - list.offsetLeft //最终距离 - ul得初始距离
        }
        var startX = list.offsetLeft //ul初始位置    
        var lastX = startX + disX //最终距离
        // list.style.left = lastX +'px' 
        // 这样直接设置的话叫做瞬变轮播图
        // 咱们不做这样的 慢慢的让你到下一张 到了下一张停下来
        // 计算每步走的距离
        var stepDis = disX/(timeAll/stepTime) //  路程/总步数   总步数==所有时间/每步所需要的时间
        id = setInterval(function(){
          // ul的起始位置
          var startX = list.offsetLeft
          // 每次都会累加走
          var left = startX+stepDis
          if(left==lastX){ //判断走到位置了 不要往后走了 停下来 清定时器
            clearInterval(id)
            // 
            if(left==-3600){
              left = -600
            }else if(left == 0){
              left=-3000
            }
            isMove = false
          }
          list.style.left = left +'px'
        },stepTime)
        // 小圆点变色的逻辑
        for(var i =0;i<liNodes.length;i++){
          liNodes[i].className = ''
        }
        // 要找对应的小圆点下标
        // 得通过图片得下标  图片下标-1就是小圆点下标
        // 图片下标   ==>   就是你最终移动得那个距离 (-2400 -1800)  c除以-600(每个图片宽度600)
        // index 小圆点下标
        var index =  lastX/-600 -1
        if(index>4){
          index = 0
        }else if(index<0){
          index = 4
        }
        liNodes[index].className = 'check'
      }
    }

\