JavaScript - 第三周

59 阅读11分钟

day11

一、BOM的常用对象

1.1 history对象

保存了当前窗口的历史纪录(过去的url)
  • 前进:history.go(1);
  • 后退:history.go(-1);
  • 刷新:history.go(0);
<script>
    // 前进
    history.go(1)
    // 后退
    history.go(-1)
    // 刷新
    history.go(0)
</script>

1.2 location对象

保存了当前窗口的正在打开的url(现在的url)
  • 一个url的组成部分:
    1. 协议:*https(加密)/*http(未加密)/ftp(传输文件)/ws(直播)... 前两个都属于叫做请求-响应模型
    2. 主机号|IP地址|域名:域名是需要花钱购买的,主机号|IP地址是免费,127.0.0.1才真的是叫做主机号,只能自己访问自己
    3. 端口号:https默认端口为443,http默认端口为80,只有默认端口可以省略不写
    4. 文件的相对路径|路由:百度加密了
    5. 查询字符串|请求消息:前端传输到后端的东西
  • 获取url的5个部分的内容,可以输入location对象查看:
    1. 协议:location.protocal;
    2. 域名:location.hostname;
    3. 端口:location.port;
    4. 路由:location.pathname;
    5. 请求消息:location.search;
<script> 
    console.log(location);//href:"http://127.0.0.1:5500/%E4%BD%9C%E4%B8%9A/%E6%9D%A8%E7%8E%89%E5%8D%8E%20-%20%E7%AC%AC%E4%B8%89%E5%91%A8%E4%BD%9C%E4%B8%9A/day11/02-location%E5%AF%B9%E8%B1%A1.html"
    // 协议
    console.log(location.protocol);//http:
    // 域名
    console.log(location.hostname);//127.0.0.1
    // 端口号
    console.log(location.port);//5500
    // 路由 console.log(location.pathname);///%E4%BD%9C%E4%B8%9A/%E6%9D%A8%E7%8E%89%E5%8D%8E%20-%20%E7%AC%AC%E4%B8%89%E5%91%A8%E4%BD%9C%E4%B8%9A/day11/02-location%E5%AF%B9%E8%B1%A1.html
    // 请求消息
    console.log(location.search);// 
</script>
  • 跳转:location="新url" - 替换当前窗口,可以后退
  • 跳转后,禁止后退:location.replace("新url") - 替换当前窗口,禁止后退
  • 刷新:location.reload();

二、DOM

2.1 查找元素

var elem=document.querySelector("任意css选择器的");
  • 缺陷:只能找到单个元素,如果匹配到了多个,也只会返回第一个,没找到null
var elems=document.querySelectorAll("任意css选择器的");
  • 优点
    1. 找到了是一个集合,没找到是一个空集合
    2. 复杂查找时,非常简单
    3. 返回的是一个静态集合NodeList
<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>
<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>
<script>
    var li1 = document.querySelector('li')
    console.log(li1)//找到的是第一个ul里面的第一个li
    // 要想找到第二个ul里面的所有li
    var lis = document.querySelectorAll('ul')[1].querySelectorAll('li')
    console.log(lis);
</script>

2.2 document.getXXX 和 document.queryXXX的区别

  1. 后者更适合复杂查找
  2. 前者返回的是一个动态集合:每一次DOM发生变化,他都会悄悄的再次查找,让页面和数据保持一致,但是效率也就低下了 - 不支持forEach
  3. 后者返回的是一个静态集合:每一次DOM发生变化,他不会悄悄的再次查找,让页面和数据没有保持一致,但是效率也就高了 - 支持使用forEach

2.3 操作样式表

  1. 获取你想要操作的样式表
var sheet=document.styleSheets[i];
  1. 获取此样式表种所有的样式规则
var rules=sheet.cssRules;
  1. 数出你想要操作的那个规则的
var rule=rules[i];
  1. 操作
console.log(rule.style.css属性名)
rule.style.css属性名="css属性值";

2.4 DOM分类:

  • 核心DOM:即可以操作HTML又可以操作XML,但是语法相对比较繁琐
  • HTML DOM:只可以操作HTML
  • XML DOM:只可以操作XML

2.5 操作属性

  • 获取属性值
    1. 核心DOM:elem.getAttribute("属性名");
    2. HTML DOM:elem.属性名;
  • 设置属性值:
    1. 核心DOM:elem.setAttribute("属性名","属性值");
    2. HTML DOM:elem.属性名="属性值";
  • 删除属性:设置属性值为空字符串,确实某些属性可以算是删除,但是只是删除了属性值,属性名还在,而有的属性哪怕只有一个属性名,也会具有作用(比如:href、disabled、readonly)
    1. 核心DOM:elem.removeAttribute("属性名");
    2. HTML DOM:elem.属性名=""; - 属性节点删不干净
  • 判断有没有:只能判断有没有,不能判断是什么,推荐用elem.getAttribute("属性名");去获取到值,自己再写比较运算
    1. 核心DOM:elem.hasAttribute("属性名");
    2. HTML DOM:elem.属性名!="";

2.6 元素上树的三种方式:

  1. 父元素.appendChild(elem);//在父元素末尾处追加一个子元素elem
  2. 父元素.insertBefore(elem,已有子元素);//在父元素追加一个子元素elem,但是放在已有子元素的前面
  3. 父元素.replaceChild(elem,已有子元素);//在父元素追加一个子元素elem,但是会替换掉已有子元素

2.7 删除元素:

elem.remove();

三、let

let 变量名=值;

作用

  1. 解决了声明提前
  2. 带来了块级作用域,一个{}就是一个块,此变量只能在那个{}里面使用
  3. 如果用let去当作下标绑定事件,那么他会记录着你当前元素的下标,不再需要自定义下标了 - 其实forEach的那个形参i就是let创建的

四、类数组转为普通数组:接住=Array.from(类数组对象);

五、案例

<div id="menu"></div>
<span>>></span>
<div id="content"></div>
<script>
    // 开关门效果
    var span = document.querySelector('span')
    span.onclick = function(){
        span.innerText = span.innerText == '>>'?'<<':'>>'
        content.style.display = span.innerText == '>>'?'block':'none'
    }
</script>
<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>
<section>
    <div class="active"></div>
    <div></div>
    <div></div>
</section>
<script>
    // 选项卡
    var lis = document.querySelectorAll('li')
    var divs = document.querySelectorAll('div')
    lis.forEach((li,i)=>{
        li.onclick = function(){
            divs.forEach(div=>div.className = '')
            divs[i].className = 'active'
        }
    })
</script>
<ul id="ul">
    <li>name1</li>
    <li>name2</li>
    <li>name3</li>
    <li>name4</li>
    <li>name5</li>
    <li>name6</li>
    <li>name7</li>
    <li>name8</li>
    <li>name9</li>
    <li>name10</li>
</ul>
<script>
    // 摇号功能
    // 拿到所有的li
    var lis = document.querySelectorAll('li')
    // 创建一个新数组用来装排序的数
    var num = []
    // 死循环 - 当num的长度和lis的长度一致时退出循环
    while(num.length < lis.length) {
        // 创建随机数
        var r = parseInt(Math.random()*(lis.length))
        // 如果num中没有这个数则向num中加入这个数
        if(num.indexOf(r) == -1) {
            num.push(r)
        }
    }
    // 给li设置自定义属性
    lis.forEach((li,i)=>li.setAttribute('nn',num[i]))
    // 把类数组变为数组
    lis = Array.from(lis)
    // 对数组进行排序
    lis.sort((a,b)=>a.getAttribute('nn') - b.getAttribute('nn'))
    // 上树
    lis.forEach(li=>ul.appendChild(li))
</script>
<select id="lsel" size="5" multiple>
            <option>Argentina</option>
            <option>Brazil</option>
            <option>Canada</option>
            <option>Chile</option>
            <option>China</option>
            <option>Cuba</option>
            <option>Denmark</option>
            <option>Egypt</option>
            <option>France</option>
            <option>Greece</option>
            <option>Spain</option>
    </select>
    <div>
            <button id="btn1">>></button>
            <button id="btn2">></button>
            <button id="btn3"><</button>
            <button id="btn4"><<</button>
    </div>
    <select id="rsel" size="5" multiple>
    </select>
<script>
    // 国家的移动
    // 设置左右空数组
    var larr = [];
    var rarr = [];
    // 拿到每一个国家并且放在左数组中
    larr = lsel.innerHTML.trim().slice(8,-9).split('</option>\n\t\t<option>');
    // 拿到所有的button
    var btns = document.querySelectorAll('button');
    // 遍历btns
    btns.forEach(btn=>{
        // 点击事件
        btn.onclick = function(){
            // 判断按钮
            switch(this.innerText){
                case '>>':
                    // 把左数组的内容全部拼接到右数组
                    rarr = rarr.concat(larr);
                    // 左数组设置为空
                    larr = [];
                    break;
                case '<<':
                    larr = larr.concat(rarr);
                    rarr = [];
                    break;
                case '>':
                    // 拿到sel1里面的option
                    var op1 = lsel.children;
                    // 倒着循环
                    for(var i = op1.length - 1; i >= 0; i--) {
                        // 判断option是否被选中
                        if(op1[i].selected) {
                            // 如果这个被选中就把这个option给删除了并且拼接到右数组
                            rarr = rarr.concat(larr.splice(i,1));
                        }
                    }
                    break
                default :
                    var op2 = rsel.children;
                    for(var i = op2.length - 1; i >= 0; i--) {
                        if(op2[i].selected) {
                            larr = larr.concat(rarr.splice(i,1));
                        }
                    }
            }
            // 数组按照字符串pk排序
            larr.sort();
            rarr.sort();
            // 判断左数组的长度是否为0
            if(larr.length == 0) {
                // 为0则设置左边select内容为空
                lsel.innerHTML = '';
            } else {
                lsel.innerHTML = '<option>' + larr.join('</option><option>') + '</option>';
            }
            // 右
            if(rarr.length == 0) {
                rsel.innerHTML = '';
            } else {
                rsel.innerHTML = '<option>' + rarr.join('</option><option>') + '</option>';
            }
        }
    })
</script>

day12

一、递归

简单来说就是再函数之中再一次调用了函数自己,迟早有一天会停下来
  • 何时:专门用于遍历层级不明确的情况 - DOM树和数据(children只能找到儿子层,找不到孙子层)
  • 如何使用:
function 函数名(root){
    第一层要做什么直接做
    判断有没有下一层,如果有下一层则再次调用此方法,只不过传入的实参是自己的下一层	
}
函数名(实际的根)
  • 算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会跳到兄弟节点
  • 缺陷:不要过多使用,性能相对较差,同时开启大量的函数调用,浪费内存
  • 递归 vs 纯循环
    1. 递归:优点:简单易用
         缺点:性能低
    2. 纯循环:优点:几乎不占用性能
          缺点:难

二、绑定事件:3种方式

  • 在HTML上书写事件属性
<elem on事件名="函数名(实参)"></elem>

缺点

  1. 不符合内容与样式与行为的分离原则
  2. 无法动态绑定,一次只能绑定一个元素
  3. 不支持绑定多个函数对象
  • 在js中使用事件处理函数属性
elem.on事件名=function(){操作}

优点

  1. 符合内容与样式与行为的分离原则
  2. 动态绑定,一次能绑定多个元素
    缺点
  3. 不支持绑定多个函数对象
  • 在js中使用事件API:如果不用考虑老IE,他也不错
    1. 主流:elem.addEventListener("事件名",callback);
    2. 老IE:elem.attachEvent("on事件名",callback);
    3. 兼容
if(elem.addEventListener){
        elem.addEventListener("事件名",callback);
}else{
        elem.attachEvent("on事件名",callback);
}
<script>
    // 主流浏览器
    btn.addEventListener('click',function(){
        console.log(1);
    })
    // 老IE(8-)
    btn.attachEvent('onclick',function(){
        console.log(1);
    })
    // 兼容写法
    if (btn.addEventListener) {
        btn.addEventListener('click',function(){
            console.log(1);
        })
    } else {
        btn.attachEvent('onclick',function(){
            console.log(1);
        })
    }
</script>

优点

  1. 符合内容与样式与行为的分离原则
  2. 动态绑定
  3. 支持绑定多个函数对象
    缺点:有兼容性问题

三、select&option只有他们可以简化创建元素&上树

select.add(new Option("innerHTML","value"));

四、练习

// 递归-DOM 给#ul的孩子设置边框
function getArea(root) {
    for(var i = 0; i < root.length; i++) {
        root[i].style.border = "1px solid #fc1"
        if(root[i].children.length > 0) {
            getArea(root[i].children)
        }
    }
}
getArea(ul.children)
<script>
    // 递归 - 数据
    var json=[{"name":"杨俊杰","children":[{"name":"杨俊"},{"name":"杨杰"}]},
              {"name":"冯小龙"},
              {"name":"胡畔","children":[{"name":"胡田","children":[{"name":"胡半"}]}]},
              {"name":"袍哥","children":[{"name":"钟哥","children":[{"name":"小钟","children":[{"name":"钟种"}]}]}]}
             ];
    // 拿到所有人的姓名
    function getName(root) {
        root.forEach(obj=>{
            console.log(obj.name);
            if(obj.children) {//没有就是undefined
                getName(obj.children)
            }
        })
    }
    getName(json)
</script>
<script>
    // 纯循环 - 设置#ul和所有它孩子的边框
    function getArea(root) {
        // 
        var org = ul
        while(1) {
            root.style.border = "1px solid #fc1"
            if(root.firstElementChild) {
                root = root.firstElementChild
            } else if (root.nextElementSibling) {
                root = root.nextElementSibling
            } else {
                while(!root.parentNode.nextElementSibling&&root!=org.lastElementChild) {
                    root = root.parentNode
                }
                root = root.parentNode.nextElementSibling
                if(root == org.lastElementChild) {
                    root.style.border = "1px solid #fc1"
                    break
                }
            }
        }
    }
    getArea(ul)
</script>
<select name="provs">
        <option>—请选择—</option>
        <option>北京市</option>
        <option>天津市</option>
        <option>河北省</option>
        <option>重庆省</option>
</select>

<select name="cities" class="hide">

</select>

<script>
// 二级联动
        var cities=[//JSON:javascript object notation数据
                [],		/*0号下标没有元素*/
                [{"name":'东城区',"value":101},
                 {"name":'西城区',"value":102},
                 {"name":'海淀区',"value":103},
                 {"name":'朝阳区',"value":104},],
                [{"name":'河东区',"value":201},
                 {"name":'河西区',"value":202},
                 {"name":'南开区',"value":203}],
                [{"name":'石家庄市',"value":301},
                 {"name":'廊坊市',"value":302},
                 {"name":'保定市',"value":303},
                 {"name":'唐山市',"value":304},
                 {"name":'秦皇岛市',"value":305}],
                [{"name":'渝中区',"value":401},
                 {"name":'九龙坡区',"value":402},
                 {"name":'沙坪坝区',"value":403},
                 {"name":'渝北区',"value":404},
                 {"name":'江北区',"value":405}]
        ];
// 拿到两个select
var sels = document.querySelectorAll('select')
// 第一个select选中项改变时候
sels[0].onchange = function(){
    // 拿到当前项的下标
    var i = this.selectedIndex
    // 判断是否为为请选择项  如果是
    if(i > 0){
        // 清除第二个li里面所有的内容
        sels[1].innerHTML = ''
        // 让第二个select显示
        sels[1].className = ''
        // 在数组中对应下标的内容做遍历
        cities[i].forEach(obj=>{
            /* // 创建标签
            var opt = document.createElement('option')
            // 设置属性和内容
            opt.innerHTML = obj.name
            opt.value = obj.value
            // 上树
            sels[1].appendChild(opt) */
                                // 简写以上内容
                                sels[1].add(new Option(obj.name,obj.value))
        })
    } else {
        // 清除第二个li里面所有的内容
        sels[1].innerHTML = ''
        // 让第二个select隐藏
        sels[1].className = 'hide'
    }
}
</script>
<!--根据数据渲染多级联动-->
    <div id="category">

    </div>
    <script type="text/javascript">
            var categories=[
                    {"id":10,"name":'男装',"children":[
                            {"id":101,"name":'正装'},
                            {"id":102,"name":'T恤'},
                            {"id":103,"name":'裤衩'}
                    ]},
                    {"id":20,"name":'女装',"children":[
                            {"id":201,"name":'短裙'},
                            {"id":202,"name":'连衣裙'},
                            {"id":203,"name":'裤子',"children":[
                                    {"id":2031,"name":'长裤'},
                                    {"id":2031,"name":'九分裤'},
                                    {"id":2031,"name":'七分裤'}
                            ]},
                    ]},
                    {"id":30,"name":'童装',"children":[
                            {"id":301,"name":'帽子'},
                            {"id":302,"name":'套装',"children":[
                                    {"id":3021,"name":"0-3岁"},
                                    {"id":3021,"name":"3-6岁","children":[
                                            {"id":2031,"name":'长裤'},
                                            {"id":2031,"name":'九分裤'},
                                            {"id":2031,"name":'七分裤',"children":[
                                                    {"id":101,"name":'正装'},
                                                    {"id":102,"name":'T恤'},
                                                    {"id":103,"name":'裤衩'}
                                            ]}
                                    ]},
                                    {"id":3021,"name":"6-9岁"},
                                    {"id":3021,"name":"9-12岁"}
                            ]},
                            {"id":303,"name":'手套'}
                    ]}
            ];
    // 创建函数
    function getSel(arr) {
        // 创建一个select标签
        var sel = document.createElement('select')
        // 向select里面添加一个请选择option标签
        sel.add(new Option('-请选择-',-1))
        // 遍历数组并且设置option
        arr.forEach(obj=>{sel.add(new Option(obj.name,obj.id))})
        // 当select里面的内容发生改变得到时候
        sel.onchange = function(){
            // 拿到当前下标
            var i = this.selectedIndex
            // 循环判断当前select有没有下一个兄弟 有就删除
            while(sel.nextElementSibling) {
                sel.nextElementSibling.remove()
            }
            // 判断是否为请选择项
            if (i > 0) {
                // 判断是否有children
                if (arr[i-1].children) {
                    // 如果有再次调用函数
                    getSel(arr[i-1].children)
                }
            }
        }
        // 上树
        category.appendChild(sel)
    }
    getSel(categories)
</script>
<script type="text/javascript">
    var json = [
        { "name": "杨俊杰", "children": [{ "name": "杨俊" }, { "name": "杨杰" }] },
        { "name": "冯小龙" },
        { "name": "胡畔", "children": [{ "name": "胡田", "children": [{ "name": "胡半" }] }] },
        { "name": "袍哥", "children": [{ "name": "钟哥", "children": [{ "name": "小钟", "children": [{ "name": "钟种" }] }] }] }
    ];
    // 创建函数
    function getUl(arr,parent) {
        // 创建ul
        var ul = document.createElement('ul')
        // 遍历数组
        arr.forEach(obj=>{
            // 创建li
            var li = document.createElement('li')
            // 设置li的内容
            li.innerHTML = obj.name
            // 上树
            ul.appendChild(li)
            // 判断对象是否有children
            if (obj.children) {
                // 再次调用函数
                getUl(obj.children,li)
            }
        })
        // 上树
        parent.appendChild(ul)
    }
    getUl(json,bd)
</script>
<div id="data1"></div>
<script type="text/javascript">
    var data = [        { "id": 1001, "name": '可口可乐', "price": 2.5, "count": 3000 },        { "id": 1003, "name": '百事可乐', "price": 2.5, "count": 5000 },        { "id": 1011, "name": '非常可乐', "price": 2.3, "count": 1000 },        { "id": 1012, "name": '天府可乐', "price": 2.3, "count": 1000 },        { "id": 1013, "name": '地府可乐', "price": 2.3, "count": 1000 },        { "id": 1014, "name": '阳间可乐', "price": 2.3, "count": 1000 },        { "id": 1015, "name": '阴间可乐', "price": 2.3, "count": 1000 },    ];
            //根据数据生成一个表格
    // 创建一个table
    var table = document.createElement('table')
    // 生成表头
    // 创建数组
    var arr = ['序号','名称','价格','数量','操作']
    // 创建行
    var tr = document.createElement('tr')
    // 遍历数组
    arr.forEach(val=>{
        // 创建列
        var td = document.createElement('td')
        // 设置每一列的内容
        td.innerHTML = val
        // 上树
        tr.appendChild(td)
    })
    // 上树
    table.appendChild(tr)

    // 表体
    // 遍历
    data.forEach(obj=>{
        // 创建行
        var tr = document.createElement('tr')
        // 遍历对象
        for(var i in obj) {
            // 创建列
            var td = document.createElement('td')
            // 设置列的内容
            td.innerHTML = obj[i]
            // 上树
            tr.appendChild(td)
        }

        // 设置操作列
        // 创建列
        var td = document.createElement('td')
        // 设置列的内容
        td.innerHTML = '<button onclick = "del(this)">x</button>'
        // 上树
        tr.appendChild(td)
        // 上树
        table.appendChild(tr)
    })
    // 上树
    data1.appendChild(table)
    // 删除函数
    function del(btn) { 
        var bool = confirm('你确定要删除' + btn.parentNode.parentNode.firstElementChild.nextElementSibling.innerHTML + '吗?')
        if (bool) {
            btn.parentNode.parentNode.remove()
        }
    }
</script>

day13

一、事件周期

从事件发生到所有事件处理函数执行完毕的全过程
  • 三个阶段
    1. 捕获阶段:由外向内,记录要发生的事件有哪些
    2. 目标优先触发:目标元素就是当前点击的实际发生事件的元素
    3. 冒泡触发:由内向外,依次执行之前记录的要发生的事件
<div id="max">
    <div id="middle">
        <div id="min"></div>
    </div>
</div>
<script>
    var divs = document.querySelectorAll('div')
    divs.forEach(div=>{
        div.onclick = function(){
            alert(div.id)
        }
    })
</script>

二、获取事件对象event

  • 主流:会自动作为事件处理函数的第一个形参传入
  • 老IE(8-):event
  • 兼容:event
<button id="btn">按钮</button>
<script>
    btn.onclick = function(e){
        console.log(e);//主流浏览器
        console.log(event);//老IE
        // 兼容写法
        e = e||event
        console.log(e);
        console.log(event);
    }
</script>

三、得到event可以做的

3.1 获取鼠标的坐标:

  • 鼠标相对于屏幕的坐标:e.screenX/Y
  • 鼠标相对于窗口/客户端/文档显示区域的坐标:e.clientX/Y
  • 鼠标相对于网页的坐标:e.pageX/Y
<script>
    // 获取鼠标相对于屏幕的坐标
    /* window.onmousemove = function(e){
        console.log(e.screenX);
        console.log(e.screenY);
    } */
    // 获取鼠标相对于窗口/客户端/文档显示区域的坐标
    /* window.onmousemove = function(e){
        console.log(e.clientX);
        console.log(e.clientY);
    } */
    // 获取鼠标相对于网页的坐标
    window.onmousemove = function(e){
        console.log(e.pageX);
        console.log(e.pageY);
    }
</script>

3.2 阻止事件冒泡:

  • 主流:e.stopPropagation()
  • 老IE:e.cancelBubble=true
  • 兼容:e.cancelBubble=true
<script>
    // 阻止事件冒泡 主流浏览器
    var divs = document.querySelectorAll('div')
    /* divs.forEach(div=>{
        div.onclick = function (e) {
            alert(this.className)
            e.stopPropagation()
        }
    }) */
    // 阻止事件冒泡 老IE
    for(var i = 0; i < divs.length; i++) {
        divs[i].onclick = function (e) {
            alert(this.className)
            var e = event
            e.cancelBubble = true
        }
    }
</script>

3.3 利用冒泡排序/事件委托

1. 开发中常用,提升网页性能,有了它我们的事件函数也可以换为箭头函数了
2. 优化:如果多个子元素定义了相同或者相似的事件操作,最好只给父元素定义一次,因为每绑定一个事件函数,其实都是创建了一个事件对象,创建的事件对象越多,网站性能越差。
3. 目标元素target:你点击的是哪一个,他永远就是那一个,不会变化的
  • 主流:e.target
  • 老IE:e.srcElement
  • 兼容:e.srcElement

3.4 阻止浏览器的默认行为:

比如:a标签默认就可以跳转,提交按钮可以提交表单,右键自带一个弹出框,F12自带一个控制台,F11自带全屏功能,F5自带刷新功能
  • 主流:e.preventDefault()
  • 老IE:e.returnValue=false
  • 兼容:e.returnValue=false
<script>
    // 阻止a标签默认的跳转
    a.onclick = (e) => {
        e.preventDefault()
    }
    // 阻止表单提交
    /* form.onsubmit = function(){
        return false
    } */
    submit.onclick = e => e.preventDefault()
    // 阻止鼠标右键事件
    window.oncontextmenu = e => e.preventDefault()
</script>

四、鼠标和键盘事件

  • 右键事件:window.oncontextmenu
  • 键盘事件
    1. window.onkeydown - 按住和按下,任何键盘按键都可以触发
    2. window.onkeypress - 按住和按下,只有字母、数字、回车、空格可以触发,其他按键不行
    3. window.onkeyup - 松开,任何键盘按键都可以触发(比手速的游戏)
  • 获取键盘的键盘码: e.keyCode
<script>
    // 阻止当按住和按下F12的时候的默认操作
    window.onkeydown = e => {
        if (e.keyCode == 123) {
            e.preventDefault()
        }
    }
</script>

五、扩展

  • 图片的加载速度,比js的执行速度慢,所以js执行完,图片可能还没有加载完,解决:img.onload事件 - 图片加载完毕后才会执行的代码
  • 如果事件委托了
    1. 判断目标元素是什么标签:xx.nodeName; - 得到标签名,是全大写组成的
    2. 事件处理函数可以写为箭头函数->this就会失效,所以必须使用目标元素target
  • this的指向
    1. 单个元素绑定事件,this->这个元素
    2. 多个元素绑定事件,this->当前元素
    3. 箭头函数,this->外部对象

day14

一、事件的取消

  • 如果你使用的是elem.onclick=()=>{},取消就用elem.onclick=null
  • 如果你使用elem.addEventListener("事件名",回调函数),取消就用elem.removeEventListener("事件名",回调函数),事件名和回调函数,必须和添加时的一模一样。
<script>
    // 取消绑定事件方法一
    /* // btn1绑定点击事件
    btn1.onclick = function () {
        console.log(1);
    }
    // 点击btn2的时候取消btn1的点击事件
    btn2.onclick = function () {
        btn1.onclick = null
    } */
    // 取消绑定事件方法二
    //给btn1绑定点击事件
    btn1.addEventListener('click',f1)
    btn1.addEventListener('click',f2)
    // 点击btn2的时候取消btn1的点击事件
    btn2.onclick = function () {
        btn1.removeEventListener('click',f1)
    }
    function f1() {
        console.log(11);
    }
    function f2() {
        console.log(22);
    }
</script>

二、this的指向

  • 单个元素绑定事件this->这个元素
  • 多个元素绑定事件this->当前元素
  • 箭头函数中的this->外部对象
  • 函数中的this->当前正在调用函数的这个人
  • 定时器的this->window
<script>
    // this的指向
    function f1(){
        console.log(this);
    }
    f1();//Window

    setInterval(function(){
        console.log(this);//Window
    },1000)

    var th={
        "name":"田浩",
    }
    var ljm={
        "name":"刘金明",
    }
    Object.prototype.zwjs=function(){
        return "我的名字叫"+this.name
    }
    console.log(th.zwjs())
    console.log(ljm.zwjs())
</script>

三、ES5强制改变this指向

  • call/apply:临时的替换了函数的this - 借用。相当于立刻调用函数,立即执行的。
    语法
    1. 函数名.call(借用的对象,实参,...); - 单独传入每个实参
    2. 函数名.apply(借用的对象,arr); - 只能传入一个实参要求是一个数组,apply其实会悄悄的打散数组
  • bind:永久替换了函数中的this - 买
    1. 创建了一个和原函数功能完全相同的新函数
    2. 将新函数的this永久绑定为了指定对象,别人都借不走
    3. 将新函数的部分参数永久固定
  • 语法
    var 新函数=函数名.bind(永久对象,永久实参,...); - 不是立刻执行,需要自己调用 bind绑定的新函数没办法被call/apply再次借走
<script>
    // 改变this的指向
    function calc(base, bonus1, bonus2) {
        console.log(this);
        return this.name + "的总工资为:" + (base + bonus1 + bonus2);
    }
    var th = {
        "name": "张三",
    }
    var ljm = {
        "name": "李四",
    }
    console.log(th.name);//张三
    console.log(calc());//Window
    // call
    console.log(calc.call(th,100,100,100));
    console.log(calc.call(ljm,100,100,200));
    // apply
    console.log(calc.apply(th,[100,100,100]));
    console.log(calc.apply(ljm,[100,400,100]));
    // bind
    var th_calc = calc.bind(th)
    console.log(th_calc(500,55,555));
</script>
  • 数组获取最大值或最小值
Math.max/min.apply(Math,arr)
  • 判断x是不是数组 - 绝对不能使用typeof:只能判断原始/标准类型,不能判断引用类型
Object.prototype.toString.call/apply(x)==="[object Array]";
  • 类数组转为普通数组
    1. 接住=Array.prototype.slice.call/apply(类数组对象)
    2. 接住=Array.from(类数组对象)
<script>
    // 1.数组获取最大值或最小值
    var arr = [1,22,3,44,6,23,4,55]
    console.log(Math.max.apply(Math,arr));//55
    // 2.判断x是不是数组 - 绝对不能使用typeof:只能判断原始/标准类型,不能判断引用类型
    var str = '1111'
    console.log(Object.prototype.toString.call(arr) == '[object Array]');//true
    console.log(Object.prototype.toString.call(str) == '[object Array]');//false
    // 3.类数组转为普通数组
    var lis = document.querySelectorAll('li')
    console.log(Array.prototype.slice.apply(lis));
    lis = Array.from(lis)
    console.log(lis);
</script>

四、ES6

  • 模板字符串:可以直接识别变量,不再需要+运算去拼接了,而且实现了一个简答的js环境,甚至支持再里面书写API
<script>
    var price = 5
    var count = 3
    console.log(`单格为:${price};数量为:${count};总价为:${price*count}`);
</script>
  • 解构赋值:解析结构再进行赋值 - 赋值的新方式,并且得到了增强
    语法
  1. 类似数组的解构赋值: let [a,b,c]=[1,2,3];
let [a,b,c] = [1,2,3]
console.log(a,b,c)//1 2 3
  1. 类似对象的解构赋值: let {a,b=默认值,c}={c:3,a:1,b:2};//形参可以设置默认值,如果自己传入了,就用自己的
let {name,age,gender} = {name:'张三',age:18,gender:'男'}
console.log(name)//张三
console.log(age)//18
console.log(gender)//男
  1. 调用函数时,传递实参的顺序无所谓了
function getSalary({n1,n2,n3}) {
    return `总薪资为:${n1}+${n2}+${n3}`
}
console.log(getSalary({n2:2000,n1:5000,n3:9000}))
  1. 函数的返回的结果,可以有多个
function getArr() {
    var a = 1
    var b = 2
    return [a,b]
}
var [a,b] = getArr()
console.log(a,b)//1 2

五、Set和Map新的数据类型

Set:类似于数组的一种数据格式 - 【去重数组,然后再转回数组】

  1. var s=new Set(arr);
  2. ...s - 三个点扩展运算符,可以脱掉数组的外套
var arr = [1,2,3,4,5,1,2,3,4,5]
var s = new Set(arr)
console.log(s);//{1, 2, 3, 4, 5}
console.log(...s);//1 2 3 4 5
console.log([...s]);//[1, 2, 3, 4, 5]
  • Map:类似于对象的一种数据格式
  1. var m=new Map();
  2. 添加:m.set("键","值");
  3. 获取:m.get("键");
  4. 清空:m.clear();
  5. 删除:m.delete("键");
var m = new Map()
m.set('name','张三')
console.log(m);//{'name' => '张三'}

六、for..of循环

for(var v of arr){
        v;//值
}

只能遍历索引数组,不能遍历hash数组,意味着也不能遍历对象

<script>
    var arr = [1,2,3,4,5,8,8,9,55]
    for (var v of arr) {
        console.log(v)//v拿到的是数组的每一项
    }
</script>

day15

一、正则表达式

定义字符串中字符出现规则的表达式
  • 何时使用:切割 替换 【验证】

  • 语法:/正则表达式/

  • 最简单的正则就是关键字原文 "原文" -> /原文/后缀

  • 后缀

      1. g:找全部<br>
      2. i:忽略大小写
    
  • 备选字符集:/^[备选字符集]$/

    1. 一个中括号,只管一位字符
    2. 正则表达式默认只要满足就不管后续了,解决:前加^,后加,两者同时使用,代表要求从头到尾完全匹配/[备选字符集],两者同时使用,代表要求从头到尾完全匹配/^[备选字符集]/ - 只要做验证必加!
    3. 一位数字:[0-9];
    4. 一位字母:[A-Za-z];
    5. 一位数字、字母、下划线:[0-9A-Za-z_]
    6. 一位汉字:[\u4e00-\u9fa5]
    7. 除了数字之外的:[^0-9]
  • 预定义字符集:一个只管一位

    1. 一位数字:\d ===>[0-9]
    2. 一位数字、字母、下划线:\w ===> [0-9A-Za-z_]
    3. 一位空白字符:\s
    4. 一位除了换行外的任意字符:.
  • 量词:规定一个字符集出现的次数:

  1. 有明确数量:

    • 字符集{n,m}:前边相邻的字符集,至少n个,最多m个
    • 字符集{n,}:前边相邻的字符集,至少n个,多了不限
    • 字符集{n}:前边相邻的字符集,必须n个
  2. 无明确数量:

    • 字符集?:前边相邻的字符集,可有可无,最多1个
    • 字符集*:前边相邻的字符集,可有可无,多了不限
    • 字符集+:前边相邻的字符集,至少一个,多了不限
  • 选择和分组
  1. 选择:在多个规则中选一个
    • 规则1|规则2
  2. 分组:将多个字符集临时组成一组子规则
    • (规则1|规则2)
  • 指定匹配位置
    1. ^:开头
    2. $:结尾
    3. 特殊:两者同时使用,前加^,后加$,表示从头到尾要求完全匹配 - 只要你做【验证】
  • 密码强度验证:2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合:/^[0-9A-Za-z]{2,4}$/
  • 预判公式
    1. (?![0-9]+$) -> 不能全由数字组成,可能有大写、小写、汉字、日文、特殊符号...
    2. (?![a-z]+$) -> 不能全由小写组成,可能有数字、大写、汉字、日文、特殊符号...
    3. (?![0-9a-z]+$) -> 不能全由数字组成、也不能全由小写组成、也不能全由数字和小写的组合组成
    4. /(?![0-9a-z]+)(?![AZaz]+)(?![A-Za-z]+)[0-9A-Za-z]{2,4};//2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合
    5. /(?![0-9a-z]+)(?![AZaz]+)(?![A-Za-z]+)(?![A-Z0-9]+$)[0-9A-Za-z]{2,4}/;//必须三者都有
    6. /(?![0-9A-Za-z]+$)[0-9A-Za-z_]{2,4}/;//至少要有下划线

二、支持正则表达式的字符串API

  • 切割:var arr=str.split("固定切割符"/RegExp);
  • 替换
  1. 基本替换法:
str=str.replace(/正则表达式/后缀,"新内容");
//replace支持支持正则,并且搭配上后缀g就可以找到全部
//缺陷:替换的新内容是一个固定的
  1. 高级替换法:
str=str.replace(/正则表达式/后缀,function(a,b,c){
    console.log(a);//正则匹配到的关键字
    console.log(b);//正则匹配到的关键字的下标
    console.log(c);//原字符串
    return 判断a关键字的长度,而返回不同的星星数量
});
  • 格式化:身份证
var id="500215200002022569";
var reg=/(\d{6})(\d{4})(\d{2})(\d{2})(\d{4})/;
id.replace(reg,function(a,b,c,d,e,f,g,h){
        //再replace的时候,正则出现了分组,我们会得到更多的形参
        //再形参a的后面就会出现n个形参,就看你有多少个分组
        //第1个分组获得的内容会保存在第2个形参之中
        //第2个分组获得的内容会保存在第3个形参之中
        //...
        //倒数第二个一定是下标
        //倒数第一个一定是原文本身
})
  • 格式化:手机号
var phone="15655555555";
var reg=/(\d{4})\d{4}(\d{3})/;		
phone=phone.replace(reg,(a,b,c)=>{
	return b+"####"+c;
})		
console.log(phone);

三、正则对象:

  • 创建
    1. 直接量:var reg=/正则表达式/后缀;
    2. 构造函数:var reg=new RegExp("正则表达式","后缀");
  • API
    1. 验证:var bool=reg.test(用户输入的);

四、扩展

  1. input的聚焦事件:onfocus
  2. input的失焦事件:onblur
  3. form表单的阻止提交:return false
  4. 实时获取input的内容:oninput
  5. 鼠标按下事件:onmousedown
  6. 鼠标松开事件:onmouseup