JavaScript进阶

一、DOM编程

DOM(Document Object Module) 不属于原生js的一部分,是浏览器提高给js操作网页的一个API
DOM文档对象模型,就是浏览器把html的每个元素生成了一个js对象。通过js可以去操作DOM对象,就是DOM编程。

一、window对象

在浏览器里面window对象是js的根(最顶层)对象,所有的标准库都是放在window对象里面,一般都是省略window对象。

二、document对象

浏览器会把整个网页解析为一个document对象,网页的所有的元素搜索该对象的一个属性。
document对象下面的每一个节点都是一个Node节点类,每一个标签或者文本就是一个节点,节点不是一个确定的html标签。
每一个Node节点会有一个element 类,一般是一个具体的html标记(element类是继承自Node,一个element有Node的属性和自身属性)
htmlElement 继承自 element 继承自 Node

// 1. 在浏览器里面window对象是js的根(最顶层)对象,所有的标准库都是放在window对象里面,一般都是省略window对象。
    console.log(window.document);// 一般省略window对象
    // 2. 浏览器把整个网页解析为一个document对象
    console.log(document);

1. document对象常用的属性和方法

// 1.直接可以通过.body,.head取到对应的子元素对象
    console.log(document.body); //输出body对象
    console.log(document.head); //输出head对象
    console.log(Object.getPrototypeOf(document.body)); //获取body对象的原型
   
// 2. baseURI 获取网页的url地址
    console.log(document.baseURI);

// 3. domain 返回网页的域名(或者ip地址)
    console.log(document.domain);

// 4. forms 返回网页中的所有form表单对象
    console.log(document.forms);

// 5. images 返回网页中所有的img图片对象
    console.log(document.images);

// 6. links 返回网页中的所有a标签对象
    console.log(document.links);

// 7. characterSet 网页编码格式
    console.log(document.characterSet); // UTF-8

2. 查找dom的方法

实际开发中一般需要精准的查找到对应的DOM,需要用查询dom的方法去查询。

// 1. getElementById("id名称") 根据元素的id返回元素对象(不是集合)
     console.log(document.getElementById("a1"));// 查询id为a1的元素dom对象

// 2. querySelector("css选择器") 根据css选择器条件查询返回查询到的第一个dom对象
     console.log(document.querySelector(".f1"));// 查询第一个对象class为f1的dom

// 3. querySelectorAll("css选择器") 选择符合css选择条件的所有dom对象集合
     var dom = document.querySelectorAll(".f2");// 返回class=f2的所有元素的集合
     dom.forEach(function(item){ // 原型上有forEach方法 可以遍历每个元素
         console.log(item)
     })

// 4. getElementByClassName("类名") 根据标签的类名查询dom,返回一个集合
     console.log(document.getElementsByClassName("f1"));// 查询class=f1的所有元素

// 5. getElementByTagName("标签名") 根据标签名查询标签,返回的是一个标签对象集合
     console.log(document.getElementsByTagName("div"));// 查询网页中的所有div元素

// 6. getElementByName("name属性名称") 根据标签的name属性名称查询dom,返回的是一个集合
     console.log(document.getElementsByName("pwd"));// 查询name属性为pwd的dom元素

三、DOM对象

1. DOM对象的属性

    var div = document.querySelector("#box2")
    // 1. 获取node的id属性,如果赋值就是修改属性
        console.log(div.id);// 获取id属性
        div.id = "divb"// 修改id属性值

    // 2. className 获取class属性值
        console.log(div.className);// 获取class属性
        div.className = "divq" // 修改class值

    // 3. innerHTML 返回元素内部的所有内容(包含的html标签,只读取一层)返回的是字符串    
        console.log(div.innerHTML);
        // 修改元素内容,会渲染html标签,一般用该方式修改dom的显示元素,效率比较高
        div.innerHTML = "vbka"

    // 4. innerText 获取元素的文本内容,过滤html标签
        console.log(div.innerText);
        // 修改元素的文本内容,有html标签,当做字符串显示在网页
        div.innerText = "vbka"

    // 5. tagName 获取元素的标签名称(大写字母)
        console.log(div.tagName);

    // 6. attributes 返回元素的可见属性的map(id,class...)
        console.log(div.attributes);

    // 7. classList 返回元素class名的集合(类似数组对象)
        console.log(div.classList,div.className);
        //classList操作class,不会影响其他class
        div.classList.add("warp","a")// 给div元素添加c3的class值
        div.classList.remove("a")// 删除div上class为a的属性
        div.classList.replace("warp","box")// 替换warp值为box
        div.classList.toggle("box1")// 在一个样式之间切换(有该样式则是删除,没有则是添加)

    // 8. style 返回元素的css样式结合(css的-线链接的样式名称在js里用小驼峰命名)
        console.log(div.style.fontSize);
        console.log(div.style.color);
        // 给样式赋值就是修改样式
        div.style.color = "gray"

    // 9. nodeType 返回元素节点类型(返回的是一个数字,用不同的数字代表不同的节点类型)
        console.log(div.nodeType);
        
	// 10. dataset 获取元素自定义数据(data-后面的值)的对象结构
        //
aaa
var box = document.querySelector("#box") console.log(box.dataset); //{text: 'text', index: 'index'} // 11. parentNode 获取当前元素的父元素 console.log(span.parentNode);
1. classList 类名操作
   var btn = document.querySelector('.btn')
// 1. 添加类 是在后面追加类名 不会覆盖前面的类名
        btn.classList.add('submit')
// 2. 删除类 
        btn.classList.remove('submit')
// 3. 切换类 当前有这个类名就取消,没有就添加
        btn.classList.toggle('submit')

2. DOM对象的方法(函数)

方法就是一个函数,一般也叫事件。用户对DOM操作的一个响应。

    var box1 = document.querySelector(".box1")
    // 1. onclick 点击事件,用户点击dom以后触发的函数
        box1.onclick = function(){ // 用户点击box1触发该函数
            box1.style.backgroundColor = "yellowgreen"
            // dom的事件里面的this一般代表当前dom对象
            this.style.color = "gray"
        }

    // 2. onmouseenter() 鼠标移入元素触发的事件。不受子元素影响,只有移入的时候触发一次
        box1.onmouseenter = function(e){
            console.log("enter");
            console.log(e); // e表示鼠标事件对象
        }

    // 3. onmouseover() (较少用)鼠标移入滑过元素会触发,元素内部有其他子元素,离开子元素又会触发over
        box1.onmouseover = function(){
            console.log("over");
        }

    // 4. onmouseout() 鼠标离开元素触发,受子元素影响,移入离开子元素也会触发out
        box1.onmouseout = function(){
            console.log("out");
        }

    // 5. onmousemove() 鼠标在元素内部只要移动就触发的事件
        box1.onmousemove = function(){
            console.log("move");
        }

	// 6. onkeydown() 键盘按下触发的事件
        inputName.onkeydown = function(e){
            console.log("keydown");
            // 默认参数e是键盘事件对象, e.key按键符号,e.keyCode按键符号的码点
            console.log(e);
            // console.log(this);
        }

// 一般用在form表单的输入控件里面:
    // 7. onchange 元素内容发生改变触发的事件,一般用在form表单的输入控件里面
        // 失去焦点的时候才对比内容,有改变则执行该函数。
        var inputName = document.querySelector("input[name='name']")
        inputName.onchange = function(){
            // 获取input的value值,赋值则是修改value值
            console.log(this.value,"onchange");
        }
    
    // 8. onblur 元素失去焦点触发的事件(一般输入控件有该事件)
        inputName.onblur = function(){
            console.log("onblur");
        }

    // 9. onfocus 元素获取焦点触发
        inputName.onfocus = function(){
            console.log("onfocus");
        }
    
        
   /* 10. dom对象可以通过getElementById(),getElementsByTagName(),getElementsByClassName(),getElementsByName()
   querySelector(),querySelectorAll()
   查询dom对象的后代元素
  */
        var span = document.querySelector("span")

    // 11. document.createElement(tagName) 创建element对象(一般不用此方法,用innerHTML)
        var div2 = document.createElement("div") // 创建一个div 元素
        div2.style.color = "red"
        div2.innerText = "div2"
        console.log(div2);
        // box.innerHTML = "HELLO"

        var box1 = document.querySelector(".box1")
        
    // 12.  在某个dom对象里面添加dom对象
        box1.appendChild(div2)// 把div2的dom对象添加到box1里面,box1里面会渲染该元素
        // 移出dom里面的某个dom元素
         box1.removeChild(span) // 删除box1元素内部的span元素

    // 13. setAttribute("属性名","属性值"),给元素添加属性
        box1.setAttribute("id","c1")
        box1.setAttribute("onclick","console.log(1)")
    

3. 节点操作

// 1. 父节点 parentNode
    son.parentNode // 得到离元素最近的父级元素 找不到返回null

// 2. 子节点 childNodes和children
    ul.childNodes // 包含 元素节点、文本节点等等
    ul.children // 只获得元素节点 实际开发常用

    ul.firstChild // 获取第一个子节点 包含 元素节点、文本节点等等
    ul.firstElementChild // 返回第一个子元素节点
    // 实际开发常用
    ul.children[0] // 第一个
    ul.children[ul.children.length - 1] // 最后一个

// 3. 兄弟节点  
    div.nextSibling // 上一个兄弟节点 包含 元素节点、文本节点等等
    div.nextElementSibling // 返回上一个子元素节点

    div.previousSibling // 下一个兄弟节点 包含 元素节点、文本节点等等
    div.previousElementSibling // 返回下一个子元素节点

// 4. 创建、添加 节点
    var li = document.createElement('li') // 创建一个li标签
    ul.appendChild(li) // 给ul后面追加li节点
    ul.insertBefore(li,ul.children[0]) // 给ul前面添加li节点 两个参数:1.添加的元素 2.在哪个节点前添加

// 5. 删除节点
    ul.removeChild(ul.children[0]) // 把ul里第一个li元素删除
    ul.removeChild(ul.children[ul.children.length - 1]) // 把ul里最后一个li元素删除
// 6. 复制节点 
    /**
     * 1. node.childNode() 括号内为空或者为false --浅拷贝 只复制标签不复制里面的内容
     * 2. node.childNode(true) 括号内为true --深拷贝 复制标签和里面的内容
    */
    var li = ul.children[0].cloneNode(true)
    ul.appendChild(li)
1. 案例:动态生成表格
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态生成表格title>
    <style>
        .box6 table {
            margin: 100px auto;
            width: 500px;
            text-align: center;
        }
    style>
head>
<body>
    <div class="box6">
        <table cellspacing="0" border="1">
            
            <thead>
                <tr>
                    <td>姓名td>
                    <td>科目td>
                    <td>成绩td>
                    <td>操作td>
                tr>
            thead>
            
            <tbody>
     
            tbody>
        table>
    div>

    <script>
// 动态生成表格
        // 准备学生的数据,用数组来存放数据
        var dates = [{
                name: '张三',
                subject: 'javascript',
                score: 100
            }, 
            {
                name: 'Anna',
                subject: 'javascript',
                score: 78
            }, 
            {
                name: '约翰',
                subject: 'javascript',
                score: 89
            }, 
            {
                name: '杨幂',
                subject: 'javascript',
                score: 90
            }]
        // 往tbody里面创建行   有几个人就建几行
        var tbody = document.querySelector('.box6 tbody')
        for (var i = 0; i < dates.length; i++){
            // 1. 创建行 tr
            var tr = document.createElement('tr')
            tbody.appendChild(tr)
            // 2. 创建单元格td 单元格数量取决每个对象里面的属性个数 for循环遍历对象
            for(var k in dates[i]){
                // 创建单元格td
                var td = document.createElement('td')
                // 把对象里面的属性值给单元格td
                // console.log(dates[i][k]);
                td.innerHTML = dates[i][k]
                tr.appendChild(td)
            }
            // 3. 创建删除单元格
            var td = document.createElement('td')
            td.innerHTML = '删除'
            // 添加 删除 操作
            tr.appendChild(td) 
        }
        // 4. 点击可以删除单元格
        var dea = document.querySelectorAll('.box6 a')
        for(let i = 0 ; i < dea.length;i++){
            dea[i].onclick = function(){
                // this.parentNode仅表示单元格td this.parentNode.parentNode才表示它所在的整个tr
                tbody.removeChild(this.parentNode.parentNode)
            }
        }
    script>
body>
html>

4. innerHTML 和 createElement() 效率比较

innerHTML 和 createElement() 创建多个元素的效率 哪个更高?

innerHTML采用数组(8ms左右)形式 而不采用拼接字符串格式(3s左右)效率更高,但结构稍微复杂
createElement()创建多个元素稍微低一点点(20ms左右),但结构更清晰

四、事件监听 addEventListener

它可以触发多个操作函数 不会像 原始写法后面操作会覆盖前面的操作

1.一些常用操作执行方法

1. addEventListener(“click”,function(){})
// 1. 传统的事件属性只能执行一个函数,不能同时执行多个
        var btn = document.querySelector("button")
        btn.onclick = function(){
            alert("aaa")
        }
        btn.onclick=function(){
            alert(11) // 会覆盖前面的函数,不能同时执行两个函数
        }

// 2. 事件监听,通过js的事件监听系统,监听dom对象的事件,只要监听的事件触发就好执行对应的函数,可以监听多个事件。
        /* element.addEventListener(eventName,callback),可以监听多个事件
            eventName 事件名称,js事件属性去掉on
            callback 监听事件执行的函数 */
        // 给btn按钮添加点击事件的监听,两个函数都可执行
        btn.addEventListener("click",function(){
            alert(222)
        })
        var btn1 = function(){
            alert("XXX");
        }
        btn.addEventListener("click",btn1)
        
// 3. 移出事件监听
        /* element.removeListener(eventName,callback)
            eventName 事件名称,js事件属性去掉on
            callback 移出事件执行的函数(要和监听的函数是同一个内存地址) */
        btn.removeEventListener("click",btn1) // 移出监听点击事件执行f1函数的监听
2. addEventListener(“change”,function(){})

鼠标移入移出,内容发生变化才会触发

//
   const input = document.querySelector('input')
   // 只有文本框的内容有变化才会触发
   input.addEventListener('change',function(){
       console.log('触发');
   })
3. addEventListener(“blur”,function(){})

鼠标每移入移出就会触发一次

//
   const input = document.querySelector('input')
   // 文本框的内容有无变化都会触发
   input.addEventListener('blur',function(){
       console.log('触发');
   })

五、事件冒泡

多个重叠dom的事件触发顺序是 从内往外 执行,像水泡一样,所以叫事件冒泡(默认行为)

e是当前事件对象:
e.stopPropagation(); 阻止事件冒泡(停止事件传递)
e.preventDefault();阻止默认事件,(阻止a标签的默认跳转链接事件)

1. e.preventDefault() 阻止默认事件

//  阻止dom的默认事件(a标签有默认的跳转事件)
   var a = document.querySelector("a")
   a.addEventListener("click",function(e){
       e.preventDefault() // 阻止默认事件,阻止a标签的默认跳转链接事件
   })
a. 禁止选中文字和右键菜单
// selectstart 禁止选中文字
	box7.addEventListener('selectstart',function(e){
	    e.preventDefault()
	})

// contextmenu 禁用右键菜单
	box7.addEventListener("contextmenu",function(e){
	    e.preventDefault()
	})

2. e.stopPropagation() 阻止事件冒泡

/*HTML代码:
		
*/
var div = document.querySelectorAll("div") // 默认会有冒泡关系 div[0].addEventListener("click",function(){ alert("box1") }) div[1].addEventListener("click",function(e){// e是当前事件对象 e.stopPropagation()// 阻止事件冒泡(停止事件传递) alert("box2") }) div[2].addEventListener("click",function(e){ alert("box3") })

六、事件委托

事件委托原理: 不是给每个子元素添加监听器,通过给元素的父元素添加事件,利用事件冒泡原理,设置到每个子元素上
通过获取点击元素的标签名(tagName),触发的事件却是父元素的事件。
如: 需要给li标签添加点击事件,获取点击li标签的所有值。

/*	
  • 1
  • 2
  • 3
*/
var ul = document.querySelector("ul") ul.addEventListener('click',function(e){ // e是当前点击事件对象,事件对象的target属性会记录点击的那个dom元素 if(e.target.tagName === "LI"){// 判断是否点击的li标签 var index = +e.target.dataset.indext// 获取自定义数据index并转为number类型 console.log(index); } })

1. e.target 和 this 的区别

this 指向绑定事件
e.target 指向我们点击的那个对象

var ul = document.querySelector('ul')
	 ul.addEventListener("click",function(e){
	      // this 指向绑定事件 --> ul
	      console.log(this);
	      // e.target 指向我们点击的那个对象 --> li
	      console.log(e.target);
	  })

七、事件捕获

多个重叠的dom元素,事件执行顺序是 从外向内 执行
addEventListener(event,callback,bool)
bool为true则是事件捕获,为false则是事件冒泡,默认是false(一般省略)

三个阶段:
1.捕获阶段(从祖先元素document向目标元素进行事件的捕获) --从外向内
2.目标阶段(触发事件的对象)
3.冒泡阶段(再由目标元素向祖先元素进行事件的冒泡) --从内向外
默认在捕获阶段 事件不会触发,将addEventListener的第三个参数设置为true

	  div[0].addEventListener("click",function(){
          alert("box1")
      },true)
      div[1].addEventListener("click",function(e){// e是当前事件对象
          // e.stopPropagation()// 阻止事件冒泡(停止事件传递)
          alert("box2")
      },true)
      div[2].addEventListener("click",function(e){
          alert("box3")
      },true)

八、函数节流与防抖

1. 函数节流

一定时间内函数只指向一次(每隔一段时间可以执行函数)

	var box1 = document.querySelector(".box1")
    var canMove = true // 创建变量控制函数内部逻辑代码是否执行
    box1.onmousemove = function (){
        if (canMove){
            canMove = false //执行了代码修改变量值为false
            console.log(11);
            setTimeout(function(){// 控制函数内部逻辑指向的时间间隔
                canMove = true
            },500)
        }   
    }

2. 函数防抖

函数频繁触发的情况下,只有足够的空闲时间才执行函数代码(某个操作结束后才执行该函数)
如:window窗口的滚动事件,需要滚动停止后再执行函数代码。

var setIndex = ""
    window.onscroll=function(){
     // 记录定时器的索引值,每次滚动都清除上一个定时器
     clearTimeout(setIndex)
     setIndex = setTimeout(function(){
      console.log("scroll")
     },500)
    }

二、BOM对象

JS执行机制:同步和异步

JS执行分:主线程执行栈(同步任务)和任务队列(异步任务)
1. 先执行 主线程执行栈的同步任务
2. 异步任务(回调函数)放入任务队列中
3. 执行栈的同步任务执行完后,系统会按次序读取任务队列的异步任务,被读取的异步任务结束等待状态,进入执行栈,开始执行。异步任务处理完毕清空

一、window 对象

window 是浏览器窗口对象,是BOM的核心对象
在浏览器中window也是一个全局对象,是一个顶层对象,BOM自带的对象都是window对象的属性。 如: alert,propt,document等,一般省略了window —>(window.document)

var 关键词创建的全局变量都是做为window对象的属性

1. 鼠标事件的坐标

document.addEventListener("click",function(e){
// 1. client 鼠标在当前可视区的x和y坐标
    console.log(e.clientX);
    console.log(e.clientY);

// 2. page 鼠标在页面文档的x和y坐标(包括右边拉伸条)
    console.log(e.pageX);
    console.log(e.pageY);

// 3. screen 鼠标在电脑屏幕的x和y坐标(包括缩小浏览器窗口)
    console.log(e.screenX);
    console.log(e.screenY);
})
        
//1.获取浏览器窗口的宽度和高度
	console.log(window.innerWidth,window.innerHeight)
//2.open(url,name) 打开一个窗口并显示url的网页内容
//name是窗口的名称(可省略),如果打开多个窗口,窗口的name一致,则只会打开一个窗口,后面覆盖前面的网页窗口。
	window.open("https://www.baidu.com","a")
  	window.open("https://www.taobao.com","a")
1. mouseenter 和 mouseover 区别

mouseover:经过自身盒子会触发一次,经过子盒子再触发一次(有冒泡)
mouseenter:仅经过自身触发一次(没有冒泡)
mouseenter 和 mouseleave搭配 都不会冒泡

2. 键盘事件 - 按下和弹起

// 1. keyup键盘弹起 触发事件
    document.addEventListener("keyup",function(e){
        // 事件对象的 keyCode 可以得到相应的ASCII码值
        console.log('upup');
    })

// 2. keydown键盘按下 触发事件(比 keypress先触发)
    document.addEventListener('keydown',function(e){
        console.log(e.keyCode); // 不区分大小写 a和A得到的都是65
    })

// 3. keypress 键盘按下 触发事件
    document.addEventListener('keypress',function(e){
        console.log('keypress');
        console.log(e.keyCode); // 区分大小写 a:65 A:97
    })

3.窗口滚动条滚动触发的事件

// 一、 window.onscroll 窗口滚动条滚动触发的事件
        // 1. dom.scrollTop 滚动条离顶部的距离,如果用等号赋值就是修改滚动条的位置
        // 2. dom.scrllLeft 滚动条离左边的距离
        var bodyHeight = document.body.clientHeight// 获取body的可见高度
        var wHeight = window.innerHeight // 获取windown窗口的高度
        var btn1 = document.querySelector(".btn")
        window.onscroll = function(){
        /* 
        获取网页滚动条滚动距离用 document.documentElement.scrollTop 或者
        document.body.scrollTop (不同的浏览器写法不一样,一般需要两者都用)
        */
            var top = document.documentElement.scrollTop || document.body.scrollTop
            // 例: 当网页滚动条滚动到底部就出现返回顶部的按钮
        // 网页滚动到底部的scrollTop = body的高度 - 窗口的高度 - 滚动条高度(一般50px)
            var bottom = bodyHeight - wHeight - 80
            if (top > bottom){
                console.log("bottom!");
                btn1.style.display = "block" // 到底部显示
            } else {
                btn1.style.display = "none"// 没到底部隐藏
            }
        }
        // 点击返回顶部按钮,滚动条返回顶部
            btn1.onclick = function(){
                // 修改scrollTop滚动条返回顶部
                document.documentElement.scrollTop = 0
                document.body.scrollTop = 0
            }

    // 二. dom对象也有scroll系列属性
            var div1 = document.querySelector(".box")
            div1.onscroll = function(){
                console.log(this.scrollTop,this.scrollLeft);
            }
            setTimeout(function(){// 设置定时器让它5秒内还原
                div1.scrollLeft = 0
                div1.scrollTop = 0
            },5000);

    // 三. window.onresize 窗口尺寸发生变化触发的事件
            window.onresize = function(){
                console.log(window.innerWidth); // 获取窗口宽度
                console.log(window.innerHeight);// 获取窗口高度
                
            }
    
    // 四. dom.offset 相对于非static父元素的偏移距离(如果没有非static定位,则是相对于body)
            var warp = document.querySelector(".warp")
            console.log(warp.offsetLeft);// 离最近非static父辈元素的左边的距离
            console.log(warp.offsetTop);// 离最近非static父辈元素的顶部的距离

二、navigator 对象

1. userAgent属性–判断用户的操作平台

可以获取浏览器相关的信息,常用userAgent属性判断用户的操作平台
如果包含了 Android 关键词则是安卓手机
如果包含了 iPhone 关键词则是iphone设备
如果都没有包含,则是pc平台

// 判断浏览器是在 移动端平台 还是在 pc平台
     function isMobile(){
         const userAgent = navigator.userAgent
         return userAgent.match("Android") || userAgent.match('iPhone') || false
     }
     document.write(isMobile()?"移动端平台":"PC端平台")

三、location 对象

操作url网址相关的对象

1. location.href 获取完整的url地址

http://127.0.0.1:5500/demo3.html?id=11&name=123#444

location.href="https://www.baidu.com" // 赋值则是跳转到该url地址

// location的 href地址跳转案例
	//支付将在5秒后自动跳转页面
     const a = document.querySelector('a')
     let num = 5
     let timer = setInterval(function(){
         num--
         a.innerHTML = `支付将在${num}秒后自动跳转页面`
         if (num === 0){ //0秒后清除定时器并跳转页面
             clearInterval(timer)
             location.href = "http://www.baidu.com"
         }
     },1000)

2. location.hash 获取url地址的(#及其后面的值)hash值

#444

3. location.search 获取url的(?后面的参数)查询参数

?id=11&name=123

获取到某个查询参数的具体值

一般需要获取到某个查询参数的值,要封装一个函数来查询

/**
  * @params key 查询参数名称
*/
     function getParam(key){
         var search = location.search//获取查询参数字符串
         // 1.替换 ? 为空字符串
         search = search.replace("?","")
         // 2.按 & 符合分隔数组
         search = search.split("&")
         // 3.按 = 号分隔数组
         for (let i = 0; i < search.length;i++){// 先遍历分隔的数组
             var items = search[i].split("=") // 再= 号分隔
             if(key === items[0]){ // 如果传入的参数等于数组的参数
                 // url中的中文字符串会被编码,需要用解码函数解码
                 return decodeURI(items[1]) // 就返回它对应的属性值
             }
         }
         return "" // 都没有找到返回空值
     }
     console.log(getParam("id"));// 11
     console.log(getParam("name"));// 123
编码/解码 中文字符串:
console.log(encodeURI('中国')); // 编码
console.log(decodeURI('%E4%B8%AD%E5%9B%BD')); //解码

4. location.host 获取主机(域名)有端口号会获取到

127.0.0.1:5500

5. location.hostname 获取主机(域名),不带端口号

127.0.0.1

6. location.pathname 获取主机后面的地址(查询参数和hash值都不会获取)

/demo3.html

7. location.protocol 获取传输协议

:http

8. location.reload() 刷新当前页面

location.reload() == F5 (普通刷新)
location.reload(true) == CTRL + F5 (强制刷新)

// 用定时器每3秒自动刷新一次
	setTimeout(function() {
    	location.reload()
    }, 3000);

9. replace(url) 替换当前网站

用一个url网址替换当前网站,不会有浏览器历史记录

location.replace("https://taobao.com")

四、history 操作浏览器历史记录对象

1. 历史记录前进(forward/go)后退(back/go)

/*	
    
    百度
*/
	 const btn = document.querySelectorAll('button')
     btn[0].onclick = function (){ // back 按钮
         // 打开上一个浏览历史记录
         history.back()
         history.go(-1)
     }
     btn[1].onclick = function (){// next 按钮
         // 打开下一个浏览历史记
         history.forward()
         history.go(1)
     }

2. length 查看浏览器历史数目

console.log(history.length);

五、offset 元素偏移量 、page、scroll

必须给元素加定位 才能控制它的left等值

// offsetLeft 元素距窗口左边的距离
    console.log(div.offsetLeft);
// offsetLeft 元素距窗口顶部的距离
    console.log(div.offsetLeft);

// offsetWidth 元素本身的宽度 包括:padding + border + width
    console.log(div.offsetWidth);
//offsetHeight 元素本身的高度 包括:padding + border + width
    console.log(div.offsetHeight);

1.offset 和 style 的用法区别

/offset 和 style 的区别
1. offset可以得到任意样式表值,style只能得到行内样式表值
2. offset获得的数值没有单位,style获得是带单位的字符串
3. offsetWidth包含padding + border + width,style.width不包含padding和border值
4. offsetWidth等属性是只读属性 不能赋值,style.width是可读属性,可获取可赋值
-
综上:获取元素大小位置用offset,给元素更改值用style

2. page

element.pageX:元素到页面的X坐标长度
element.pageY:元素到页面的Y坐标长度

3. scroll

针对盒子元素:
element.scrollTop:元素被卷走的头部
element.scrollHeight:元素总的高度
针对整个页面:
window.pageYOffset:页面被卷走的头部

JavaScript进阶_第1张图片

六、js的动画

动画原理:
1. 获得盒子当前位置
2. 让盒子在当前位置加一个移动的距离
3. 利用定时器不断重复这个操作
4. 加一个结束定时器的条件
5. 注意元素一定要加定位,才能使用 element.style.left

1. 匀速动画

盒子当前的位置 + 固定值

// 封装函数:函数传递两个参数,一个操作对象,一个结束条件
	function animation(obj,target){
     // 当我们不断点击按钮,这个元素会越来越快 因为每点击一次,元素就会开启一个定时器
     // 解决方案:让我们元素只有一个定时器
     //      先清除以前的定时器,只保留当前的一个定时器执行函数
     clearInterval(obj.timer)
     obj.timer = setInterval(function(){ // obj.timer性能优化更好
         if (obj.offsetLeft == target){
             clearInterval(obj.timer)
         } else {
             obj.style.left = obj.offsetLeft + 1 + 'px'
         }
     },30)
 }
           
// 调用函数
	  var box = document.querySelector('.bar')
      var banner = document.querySelector('.banner')
      animation(box,300)
      
      var btn = document.querySelector('button')
      btn.addEventListener('click',function(){
          animation(banner,200)
      })

1. 缓动动画

也可以实现多个目标值之间的移动
盒子当前的位置 + 变化值(目标值 - 当前的距离值 / 值)

思路:

  1. 让盒子每次移动的距离慢慢变小 速度就会慢慢落下来
  2. 核心算法:(目标值 - 现在的位置) / 10 作为每次移动的距离步长
  3. 停止条件:让当前的盒子位置等于目标位置就停止定时器
// 封装函数:函数传递两个参数,一个操作对象,一个结束条件
     function animation(obj,target){
         // 当我们不断点击按钮,这个元素会越来越快 因为每点击一次,元素就会开启一个定时器
         // 先清除以前的定时器,只保留当前的一个定时器执行函数
         clearInterval(obj.timer)
         obj.timer = setInterval(function(){ // obj.timer性能优化更好
             var step = (target - obj.offsetLeft) / 10
           // 把步长改为整数 不要出现小数问题
             tep = step > 0 ? Math.ceil(step) : Math.floor(step)
             if (obj.offsetLeft == target){
                 clearInterval(obj.timer)
             } else {
            // 每次加的步长值都是一个慢慢变小的值
                 obj.style.left = obj.offsetLeft + step + 'px'
             }
         },15)
     }
1. 加回调函数
// 封装函数:函数传递两个参数,一个操作对象,一个结束条件 ,回调函数
 function animation(obj,target,callback){
     // 当我们不断点击按钮,这个元素会越来越快 因为每点击一次,元素就会开启一个定时器
     // 先清除以前的定时器,只保留当前的一个定时器执行函数
     clearInterval(obj.timer)
     obj.timer = setInterval(function(){ // obj.timer性能优化更好
         var step = (target - obj.offsetLeft) / 10
         // 把步长改为整数 不要出现小数问题
         step = step > 0 ? Math.ceil(step) : Math.floor(step)
         if (obj.offsetLeft == target){
             clearInterval(obj.timer)
             // 结束调用 回调函数
             if (callback){// 有回调函数就调用 没有就不调用
                 callback()
             }
         } else {
             // 每次加的步长值都是一个慢慢变小的值
             obj.style.left = obj.offsetLeft + step + 'px'
         }
     },15)
 }

// 调用函数
	var banner = document.querySelector('.banner')    
    var btn2 = document.querySelector('.btn2')

    btn2.addEventListener('click',function(){
        animation(banner,800,function(){
            banner.style.backgroundColor = 'red'
        })
    })

六、js异常处理

js程序如果报错,会终止代码运行并显示错误。有时不希望出错就停止执行代码,需要把可能出错的代码写入到异常处理代码块里面。

不要乱用try…catch,一般js代码需要包装逻辑的完整性,正确性,容易出错的地方用if判断解决。

1. try…catch()…语句

	 try {// 把可能出错的底代码放入try语句中,如果代码出错,错误会抛到catch语句中,没有错误直接执行语句,不会执行catch语句
      	 console.log(text1); // 错误语句
     }catch(err){// catch语句接受try抛出的异常,err就是错误的描述对象
         console.log(err.message); //text1 is not defined
     }finally{// 不管执行了try还是catch语句,都会执行finally语句(可省略)
         console.log("都会执行的语句");
     }

2. 自定义抛出错误

//1. 可以用Error构造函数收到抛出一个错误
     function err1(){
         // throw 语句抛出错误
         throw new Error("err1 是一个错误语句");// 实例化一个错误对象,参数是错误显示内容
     }
//2. 测试
     try{
         err1() // 调用错误函数
     }catch(err){
         console.log(err.message); //err1 是一个错误语句
     }

六、cookie存储

cookie 是一种小型的文本文件,可以让程序员在计算机中存储少量的数据(4kb),用来记录用户的一些信息。比如记录用户的身份,喜好,浏览器网页的习惯等等。cookie最初设计主要是为php,asp等服务器端语言设计,前端也可以通过js操作cookie。cookie可以设置存储的过期时间。

cookie的数据是保留在客户端的硬盘上,cookie是绑定在域名上的,不同域名的cookie不能互相访问,cookie创建数量有限制,大部分浏览器现在在20-50个之间;一个cookie存储数据大小是4kb左右。前端开发一般不用cookie

cookie 的操作

// 1. 创建cookie,cookie的值是一个字符串,字符串用key=value方式存储内容
/* 存储的cookie 的 value数据中不能包含分号,逗号或者cookie,
 建议存储数据使用encodeURI对内容进行编码 */
    // 读取的时候用decodeURI解码。默认数据是浏览器关闭,cookie数据就消失
        document.cookie = `name=${encodeURI("张珊")}`

// 2. 如果要延长cookie数据存储时间,用max-age指定cookie数据存储时长(单位s),默认为-1,浏览器关闭,数据消失
        document.cookie = `test=${encodeURI("安娜")};max-age=${60*60*24*30}`// test数据30天过期

// 3. expires属性指定cookie过期日期(日期字符串格式)
        document.cookie = `sex=11;expires=Fri, 17 Jul 2024 21:15:42 GMT`

// 4. 修改cookie的值,创建一个相同的cookie覆盖
        document.cookie = "name=Amy"

// 5. 删除cookie,把cookie的有效期设置为一过期时间则是删除
        document,cookie = "sex='';expires=Fri, 17 Jul 2020 21:15:42 GMT"

// 6. 读取cookie的值
    // 封装读取cookie指定值的函数
        function getCookie(key){
            const cookie = document.cookie.split("; ")
            for (let i = 0; i < cookie.length; i++){
                const items = cookie[i].split("=")
                if(items[0] === key){
                    return decodeURI(items[1])
                }
            }
            return ""
        }

七、本地存储localStorage 和sessionStorage

localStorage 永久存储数据,不主动删除,数据不会消失。
sessionStorage用法和localStorage一致,浏览器关闭,数据消失。storage 存储数据大小是5M

1. localStorage 永久存储数据(简单存储)

// 1. 存储数据 localStorage.setItem('key','value') ,只能存字符串(非字符串数据会转字符串)      
        localStorage.setItem('uame','anna') // 本地存储空间,数据永久存储
        localStorage.setItem('age',18)// 数字会转成字符串
// 2. 读取key值 localStorage.getItem('key') 读取key值对应的value       
        console.log(localStorage.getItem('age'));
// 3. 删除key localStorage.removeItem(key) 
        localStorage.removeItem('age')
// 4. 删除storage里面所有数据 localStorage.clear()---也可用上面的禁用符删除  
        localStorage.clear()

2. sessionStorage 浏览器关闭,数据消失

用法同 localStorage 一样,除生命周期不同,页面关闭即消失。

// 添加数据
    setnum.addEventListener('click',function(){
        var val = ipt.value
        sessionStorage.setItem('age',val)
        sessionStorage.setItem('uname',val)
    })
// 获取数据的值
    getnum.addEventListener('click',function(){
        console.log(sessionStorage.getItem('age'));
    })
// 删除一条数据
    remove.addEventListener('click',function(){
        sessionStorage.removeItem('uname')
    })
// 清除所有数据 不推荐使用
    detele.addEventListener('click',function(){
        sessionStorage.clear()
    })

3. localStorage (复杂存储)

本地只能存储字符串,不能存储复杂的数据类型,需要将复杂数据类型转换成JSON字符串存储到本地

// 1.存储 JSON.stringify() 把对象转成JSON字符串
     localStorage.setItem('user',JSON.stringify(user)) // {"name":"Anne","age":29,"gender":"女"}

// 2.取值 JSON.parse() 把JSON字符串转成对象
     console.log(JSON.parse(localStorage.getItem('user'))); //{name: 'Anne', age: 29, gender: '女'}
1. 记住用户名 案例
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>记住用户名title>
    <style>
    style>
head>
<body>   
    <input type="text" placeholder="请输入用户名" class="username">
    <input type="checkbox" name="" class="checkbox">
    
    <script>
        var username = document.querySelector('.username')
        var checkbox = document.querySelector('.checkbox')
        // 先判断有没有存储的 username
        if (localStorage.getItem('username')){
            username.value = localStorage.getItem('username')
            checkbox.checked = true
        }

        // 如果复选框勾选就存储用户名 如果取消就移出用户名
        checkbox.addEventListener('change',function(){
            if (this.checked){
                localStorage.setItem('username',username.value)
            } else {
                localStorage.removeItem('username')
            }
        })
    script>
body>
html>

八、正则表达式

正则表达式: 是一种匹配字符的逻辑公式,主要是匹配字符串中的字符。

1. 常用匹配符号 和 修饰符

匹配符:

// a. ^ 表示以某个字符开头的(开头不能有其他任何的字符)
	const reg1 = /^hello/; // 匹配字符以hello开头的hello字符串
// b. $ 匹配以某个字符结尾(结尾不能有其他任何的字符)
    const reg2 = /css$/; // 匹配以css结尾的css字符串
// c. \b 匹配单词是否到边界(单词后面没有其他任意字符,可以有空格)
    const regc3 = /java\b/g

修饰符:

  1. g 全局匹配,查询字符串中的所有。
    2. i 不区分大小字母。大小写字母都会匹配
    3. m 多行匹配,匹配多行中的字符
    4. u 字符按照unicode编码匹配

2. 正则表达式的模式

// a. [] 范围查询,查询[]里面包含的单个字符
   const reg3 = /[ak]/g // 查询a或者l字符
   console.log(str.match(reg3))
// b. -  表示一段连续的内容
   const reg = /[0-9a-z]/ig // 查询所0-9的所有数字,A-z查询所有的字母,不区分大小写
   // const reg = /[0-9A-z]/g  // 从大A - 小z
   // const reg = /[0-9a-zA-Z]/g
// c. [^a] 查询除了中括号里面的所有内容
   const reg5 = /[^a-z]/ig // 查询字符串里面a-z(不区分大小写)的以外的所有字符
// d. | 分隔正则逻辑(或的关系)
   const reg6 = /(hello)|java/g // 查询hello或者java字符串,()表示一个整体
// e. \ 转义符号,正则表达式的内置关键字符如果要作为字符串匹配,需要添加\转义符
   const reg7 = /\[java\]/ // 匹配[java],而不是作为正则表达式符号
   const reg8 = /\//g // 匹配 /符号

3. 元字符(匹配逻辑简写)

// 1. \d  匹配所有的数字 等价:/[0-9]/g
    const rega = /\d/g
// 2. \D 匹配所有的非数字 等价:/[^0-9]/g
    const regb = /\D/g
// 3. \w 匹配数字,字母,下划线 等价:/[0-9A-z_]/g
    const regc = /\w/g
// 4. \W 匹配非数字\字母\下划线的字符 等价:/[^0-9A-z_]/g
    const regd = /\w/g
// 5. \s 匹配所有的空白字符串(空格)
    const rege = /\s/g
// 6. .表示任意字符
    const regf = /./g // 匹配所有的字符
// 7. u修饰符匹配中文字符(内部其实是去匹配中文字符对应的unicode编码)
    const regg = /\p{Unified_Ideograph}/gu // 匹配所有的中文字符

4. 量词(匹配字符串出现指定的次数)

// 1. + 表示匹配字符 >=1 个以上
    var rega1 = /go+d/g // 匹配gd中间带有o的字符串
// 2. * 表示匹配字符0个以上(最少是0个)
    var rega2 = /go*d/g // 匹配gd中间可以有o也可以没有o的字符串
// 3. ? 表示0个或者1个
    var rega3 = /go?d/g // gd中间有1个o或者没有o的字符串
// 4. {min,max} 自定义匹配数量,min是最小数量,max是最大数量
    var regb1 = /go{1,3}d/g // 匹配gd之间有1-3个o的字符串
    var regb2 = /go{3}d/g // 匹配gd之间只有3个o的字符串
    var regb3 = /go{3,}d/g // 匹配gd之间最少有3个o的字符串

5. 正则表达式对象

字符串的test(),match(),search(),replace()可以使用正则表达式对象

1. test() 检查是否满足正则对象规则

正则表达式的test(str) 检查str字符串是否满足正则对象规则,满足返回true,不满足返回false

// 例: 校验手机号码规则
    const reg = /^1[3-9]\d{9}$/   
    function ipone(num){
        if (reg.test(num)){
            console.log("手机号码正确");
        } else {
            console.log("手机号码格式错误");
        }
    }     
    console.log(ipone(13555555547));

// 例: 用户名注册用户名的规则:
    // 以字母开头,第二位开始可以是数字,字母下划线,-,.的组合。用户名长度是4-8位
    const userReg = /^[a-z][\w-\.]{3,7}$/i
    const u1 = "w888"
    console.log(userReg.test(u1));
2. match() 返回满足正则的数组
const str = "124 GoodJGsiwncKK 883 *4*"
    // 查询数字和所有的字母 
const reg = /[0-9a-z]/ig
console.log(str.match(reg));
3. replace() 替换字符串
str = str.replace(rege,"") //替换字符串里面所有的空格为""

6. 断言(环视)

预判某个位置是某个字符。也叫零宽断言,预判位置的字符不会出现在查询结果

// a1 char(?=exp) 0宽正预测(先行预测)。判断char字符后面必需符合给定的正则表达式
  var str6 = "1hello-world 2hello-js 3hello-html";
  var regd1 = /2hello(?=-js)/g; // 匹配hello字符串后面是-js的hello字符
  console.log(str6.match(regd1))
  
// a2 char(?!exp) 0宽负预测先行断言(char字符后面不满足exp正则表达式)
  var regd2 = /\dhello(?!-js)/g; // 匹配hello后面没有接-js的hello字符串
  console.log(str6.match(regd2))

// a3 (?<=exp)char 0宽正预测回顾断言(char字符左边是否符合某个正则表达式)
  var regd3 = /(?<=2hello-)\w+/g; // 查询左是2hello-字符串的字符
  console.log(str6.match(regd3))

// a4 (?
  var regd4 = /(?/g; // 查询左边不是2hello-字符串的字符
  console.log(str6.match(regd4))

7. 常用正则表达式的规则

// 1.验证用户名 6-10位
const reg = /^\w{6,10}$/

// 2. 手机号验证 11位手机号码
const reg = /^1[3-9]\d{9}$/

// 3. 验证码 6位数字
const reg = /^\d{6}$/

// 4. 密码 6~20位数字字母符号
const reg = /^[\w-]{6,20}$/

// 企业邮箱
var emailReg = /^(?!.*\.\-)^(?!.*\-\.)\w+[-\.\w]*@([0-9A-z]+\.[0-9A-z]+)+$/

// 身份证
   const reg2 = /^\d{17}[\dX]$/gi

// 复杂密码校验
    // 至少包含小写字母/大写字母/数字,字母下划线的组合的4-8位密码
    const pwdReg = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])\w{4,8}$/
    const pwd = "123Z2a2"

三、移动端

一、touch 触摸事件对象

// 1. touchstart手指触摸DOM元素事件
    div.addEventListener('touchstart',function(e){
        console.log(e);
        /**
         * 1. touches 正在触摸屏幕的手指列表
         * 2. targetTouches 正在触摸当前DOM元素的手指列表(常用)
         * 3. changedTouches 手指状态发生了改变的列表 从无到有 或 从有到无
        */
       // 可以拿到第一个手指触摸的相关信息 坐标等等
       console.log(e.targetTouches[0]);
    })
// 2. touchmove手指在DOM元素身上移动事件
    div.addEventListener('touchmove',function(e){
        console.log('move~~');
    })
// 3. touchend 手指离开DOM元素
    div.addEventListener('touchend',function(e){
        console.log(e);
     //当手指离开屏幕的时候,就没有 touches 和 targetTouches 列表
     // 但会存在 changedTouches
    })

1. 在移动端移动DOM元素

	 var div = document.querySelector('div')
   // 获取手指的初始坐标 (声明全局变量方便后面使用)
     var startX = 0 
     var startY = 0 
     // 获取盒子原来的位置
     var x = 0
     var y = 0
     div.addEventListener('touchstart',function(e){
         // 第一个手指的坐标
         startX = e.targetTouches[0].pageX
         startY = e.targetTouches[0].pageY
         // 盒子的初始值
         x = this.offsetLeft
         y = this.offsetTop
     }) 
     div.addEventListener('touchmove',function(e){
         // 手指移动的距离 = 手指移动的坐标 - 开始手指的坐标
         var moveX = e.targetTouches[0].pageX - startX
         var moveY = e.targetTouches[0].pageY - startY
         
         // 盒子移动的距离 = 盒子原来的位置 + 手指移动的距离
         this.style.left = x + moveX + 'px'
         this.style.top = y + moveY + 'px'
         e.preventDefault() // 阻止屏幕滚动的默认行为
     })

你可能感兴趣的:(javascript,开发语言,前端)