【前端】BOM & DOM

两天更新完毕,建议关注收藏点赞
友情链接:
HTML&CSS&LESS&Bootstrap&Emmet
Axios & AJAX & Fetch
BOM DOM
待整理 js2


  • 浏览器如何界面渲染的
    在这里插入图片描述
    解析(Parser)HTML,生成DOM树(DOM Tree)
    同时解析(Parser) CSS,生成样式规则 (Style Rules)
    根据DOM树和样式规则,生成渲染树(Render Tree)
    进行布局 Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置,大小)
    进行绘制 Painting(重绘): 根据计算和获取的信息进行整个页面的绘制
    Display: 展示在页面上
  • 回流(重排)
    当 Render Tree 中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过程称为回流。
    会导致回流(重排)的操作:简单理解影响到布局了,就会有回流
     页面的首次刷新
     浏览器的窗口大小发生改变
     元素的大小或位置发生改变
     改变字体的大小
     内容的变化(如:input框的输入,图片的大小)
     激活css伪类 (如::hover)
     脚本操作DOM(添加或者删除可见的DOM元素)
  • 重绘
    由于节点(元素)的样式的改变并不影响它在文档流中的位置和文档布局时(比如:color、background-color、outline等), 称为重绘。
  • 重绘不一定引起回流,而回流一定会引起重绘。
    在这里插入图片描述
  • JS运行机制

    • JS是单线程,不能同时进行;因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对
      某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
    • 同步任务v.s.异步任务
      为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。故出现了JS中所有任务可以分成两种,同步任务(synchronous)异步任务(asynchronous)
      同步任务指的是:在主线程【执行栈】上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
      异步任务指的是:不进入主线程、而进入【任务队列】的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。

    比如你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。

    【异步任务】类别示例/说明
    网络请求fetch(), axios.get(), XMLHttpRequest
    定时器任务setTimeout, setInterval, requestAnimationFrame
    DOM 事件回调click, keyup, onload 等事件回调
    Promise 任务.then, .catch, .finally
    Async/Await封装 Promise 的更简洁语法
    微任务(Microtasks)queueMicrotask, Promise.then
    宏任务(Macrotasks)setTimeout, setInterval, MessageChannel
    Web Worker在子线程中处理数据(比如大计算任务)
    事件循环触发浏览器/Node 的 event loop 会安排任务执行顺序
    • JS执行机制:事件循环
      JavaScript在按代码从前往后顺序执行,依次压入执行栈,每次执行一个方法,都会为它生成独有的执行环境(上下文),当这个方法执行完成后,就会销毁当前的执行环境,并从栈中弹出此方法,然后继续执行下一个方法。执行栈是栈,先进后出,但并不是代码从后往前执行,而是调用一个压栈、它执行完出栈,所以还是从前往后执行。
      在这里插入图片描述
      任务队列分为宏任务、微任务两种队列。详见事件循环专题 点击跳转
    //下面三个片段的输出各是什么
    console.log(1);
    setTimeout(function() {
        console.log(3);
    }, 1000);
    console.log(2);
    //1->2->3
    
    console.log(1);
    setTimeout(function() {
        console.log(3);
    }, 0);
    //将回调函数放入 宏任务队列,虽然 0ms 但仍然要等主线程任务执行完毕后才执行。
    //即使 setTimeout 的时间是 0ms,它依然不会立即执行,而是等 同步任务执行完毕 才执行。
    console.log(2);
    //1->2->3
    
    console.log(1);
    document.onclick = function() {
        console.log('click');
    }
    console.log(2);
    setTimeout(function() {
        console.log(3);
    }, 3000);
    //1 2 3(如果 3 秒不点击) 
    // 1 2 click 3(如果 3 秒内点击)
    

    BOM浏览器对象模型

    BOM (Browser Object Model,简称BOM) 是指浏览器对象模型,它提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。通过BOM可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。其核心/顶级对象是 window。
    ​ BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
    ​ BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。
    BOM>DOM,BOM包含DOM
    在这里插入图片描述

    • window对象
      是js访问浏览器窗口的接口。
      是一个全局对象,定义在全局作用域中的变量、函数都会变成window对象的属性和方法。
      调用时可省略window,例如alert(),prompt()等
      • window.name 是 JavaScript 里一个独特的属性,它和一般的变量不同,具有持久性和跨页面共享的特性。window.name 在页面刷新后仍然保留
    1. 一般来说,JavaScript 变量在页面刷新后会被重置,但 window.name 不会丢失,除非手动修改或关闭浏览器。window.name 可以在同一窗口的不同页面之间共享
    2. 如果用户在当前窗口导航到一个新页面,window.name 的值不会丢失,可以在新页面中访问它!
    3. window.name 允许存储超长字符串
      一般的 localStorage 最大存储约 5MB,而 window.name 可以存储比 localStorage 更多的数据,甚至可以用来存储 JSON 字符串。
    4. 安全性风险:由于 window.name 可以在不同页面之间共享,如果不小心泄露,可能会导致安全风险(如 XSS 攻击)。🚫 不要存储敏感数据(密码、token)
    //解决方案:跨域访问时重置 window.name
    window.onload = function () {
        if (window.location.hostname !== "yourwebsite.com") {
            window.name = "";
        }
    };
    
    • 定时器

    回调函数:回调,就是回头调用的意思。上一件事干完,再回头再调用这个函数。例如:定时器中的调用函数,事件处理函数,也是回调函数。
    以前我们讲的 element.onclick = function(){} 或者 element.addEventListener(“click”, fn); 里面的 函数也是回调函数。

    //炸弹定时器
    window.setTimeout(func,[延迟的毫秒数])
    //这里面的函数称为回调函数callback
    //window可省
    window.clearTimeout(timeoutID)//取消之前调用setTimeout()的定时器
    //递归setTimeout可以实现setInterval
    let clock=document.querySelector('.clock')
    function myInterval(){
    	let d=new Date();
    	clock.innerText=d.toLocaleString();
    	setTimeout(myInterval,1000);
    }
    myInterval();
    
    //闹钟定时器
    let timeID=window.setInterval(callback,[ms])
    //重复调用一个函数,每隔这个时间就调用一次回调函数,第一次执行也是要间隔这ms数
    window.clearInterval(intervalID)
    //注意 即使clear了timeID依然在,所以要timeID=null才行
    
    • this指向问题
      this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象。
      1.全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
      2.方法调用中谁调用this指向谁
      3.构造函数中this指向构造函数的实例
      4.在箭头函数中, this指向外层"函数"作用域this的值

    window对象常见事件

    • iframe相关
      window.parent.getUserInfo() 是在 iframe 场景下使用的一种 跨窗口调用 方法,通常用于 子页面(iframe 内部)调用父页面的方法,从而获取用户信息或执行其他逻辑。

    window.open("http://www.w3school.com.cn","window2","width=600,height=400")打开窗口
    window对象有innerWidth和innerHeight属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。还有一个outerWidth和outerHeight属性,可以获取浏览器窗口的整个宽高。
    兼容性:IE<=8不支持。

    • 页面(窗口)加载事件(2种)

    onload是页面内容全部加载完触发(包括图像、脚本文件、CSS
    文件等)。DOMContentLoaded是仅DOM元素加载完触发,且从IE9开始支持。

    var width = window.innerWidth || document.body.clientWidth;
    

    window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数。唯一,后覆盖前。

    window.onload=function(){}
    window.addEventListener('load',function(){})//没有唯一性限制
    
    document.addEventListener('DOMContentLoaded',function(){})
    //window也可以 不过最好是加在document上
    //事件触发时仅DOM加载完成,不包括样式表、图片、flash等
    //IE9以上才支持!!!
    //如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间, 
    //交互效果就不能实现,必然影响用户的体验,此时用 DOMContentLoaded 事件比较合适。
    
    • 调整窗口大小事件
      经常利用这个事件完成响应式布局。 window.innerWidth 当前屏幕的宽度
    window.onresize=function(){}
    //window.onresize 是调整窗口大小加载事件,  当触发时就调用的处理函数。
    window.addEventListener('resize',function(){})
    
    • 禁止选中
    window.getSelection ? window.getSelection().removeAllRanges() : 				    document.selection.empty();
    
    • window.location.hash 是 JavaScript 中的一个属性,用来获取或设置 URL 中的 “哈希值”部分,也叫 锚点(anchor) 或 片段标识符(fragment identifier)。就是#及之后的内容

    screen对象

    screen对象表示屏幕的信息,常用的属性有:
    screen.width:屏幕宽度,以像素为单位;
    screen.height:屏幕高度,以像素为单位;
    screen.colorDepth:返回颜色位数,如8、16、24。

    location对象

    window对象给我们提供了location属性用于获取或设置窗体URL,也可以解析URL。因为这个属性返回的是一个对象location。

    • location属性
      在这里插入图片描述

    如一个完整的url:
    http://www.example.com:8080/path/index.html?a=1&b=2#TOP
    location.protocol; // ‘http’
    location.host; // ‘www.example.com’
    location.port; // ‘8080’
    location.pathname; // ‘/path/index.html’
    location.search; // ‘?a=1&b=2’
    location.hash; // ‘TOP’

    • location对象的常见方法
      在这里插入图片描述
    			// 记录浏览历史,所以可以实现后退功能
                // location.assign('http://www.itcast.cn');
                // 不记录浏览历史,所以不可以实现后退功能
                // location.replace('http://www.itcast.cn');
    console.log(location.search); // ?uname=andy
    // 1.先去掉?  substr('起始的位置',截取几个字符);
    var params = location.search.substr(1); // uname=andy
    console.log(params);
    // 2. 利用=把字符串分割为数组 split('=');
    var arr = params.split('=');
    console.log(arr); // ["uname", "ANDY"]
    var div = document.querySelector('div');
    // 3.把数据写入div中
    div.innerHTML = arr[1] + '欢迎您';
    
    • URL (uniform resource locator)统一资源定位符
      protocol://host[:port]/path/[?query]#fragment
    • 利用location实现多页面参数传递
      ① 第一个登录页面,里面有提交表单, action 提交到 index.html页面
      ② 第二个页面,可以使用第一个页面的参数,这样实现了一个数据不同页面之间的传递效果
      ③ 第二个页面之所以可以使用第一个页面的数据,是利用了URL 里面的 location.search参数
      ④ 在第二个页面中,需要把这个参数提取。
      ⑤ 第一步去掉? 利用 substr
      ⑥ 第二步 利用=号分割 键 和 值 split(‘=‘)
      ⑦ 第一个数组就是键 第二个数组就是值

    navigator对象

    navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。

    if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
        window.location.href = "";     //手机
     } else {
        window.location.href = "";     //电脑
     }
    
    // 检测 userAgent(浏览器信息)
    !(function () {
    const userAgent = navigator.userAgent
    // 验证是否为Android或iPhone
    const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
    const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
    // 如果是Android或iPhone,则跳转至移动站点
    if (android || iphone) {
    location.href = 'http://m.itcast.cn'
    }
    })()
    

    navigator对象表示浏览器的信息
    navigator.appName:浏览器名称;
    navigator.appVersion:浏览器版本;
    navigator.language:浏览器设置的语言;
    navigator.platform:操作系统类型;
    navigator.userAgent:浏览器设定的User-Agent字符串。
    navigator的信息可以很容易地被用户修改,所以JavaScript读取的值不一定是正确的。

    history对象

    这个对象属于历史遗留对象,对于现代Web页面来说,由于大量使用AJAX和页面交互,简单粗暴地调用history.back()可能会让用户感到非常愤怒。任何情况,你都不应该使用history这个对象了
    window对象给我们提供了一个 history对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。history对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到。
    在这里插入图片描述

    DOM文档对象模型

    核心要点:增删改查,创建,属性操作,事件操作

    文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言的标准编程接口。通过 DOM 提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)。顶级对象是document

    • DOM树
      DOM树 又称为文档树模型,把文档映射成树形结构,通过节点对象对其处理,处理的结果可以加入到当前的页面。
      在这里插入图片描述
      文档:一个页面就是一个文档,DOM中使用document表示
      节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
      标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示
      DOM把这些节点都看作对象。
      节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
      在这里插入图片描述
    • 总结
      创建
    1. document.write
    2. innerHTML
    3. createElement
    4. appendChild
    5. insertBefore
    6. removeChild

      主要修改dom的元素属性,dom元素的内容、属性, 表单的值等
    7. 修改元素属性: src、href、title等
    8. 修改普通元素内容: innerHTML 、innerText
      安全起见,尽量使用innerText
    9. 修改表单元素: value、type、disabled等
    10. 修改元素样式: style、className

      主要获取查询dom的元素
    11. DOM提供的API 方法: getElementById、getElementsByTagName 古老用法 不太推荐
    12. H5提供的新方法: querySelector、querySelectorAll 提倡
    13. 利用节点操作获取元素: 父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling) 提倡
      属性操作
      主要针对于自定义属性。
    14. setAttribute:设置dom的属性值
    15. getAttribute:得到dom的属性值
    16. removeAttribute移除属性
      事件操作
      给元素注册事件, 采取 事件源.事件类型 = 事件处理程序
      在这里插入图片描述

    一般获取元素

    document.getElementById(id)
    参数:id值,区分大小写的字符串
    返回值:元素对象 或 null
    
    <body>
        <div id="time">2019-9-9</div>
        <script>
            // 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面
            var timer = document.getElementById('time');
            console.log(timer);
            console.log(typeof timer);
            // console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
            console.dir(timer);
        </script>
    </body>
    
    document.getElementsByTagName('标签名') 或者
    element.getElementsByTagName('标签名') 
    作用:根据标签名获取元素对象
    参数:标签名
    返回值:元素对象集合(伪数组,数组元素是元素对象)
    注意:getElementsByTagName()获取到是动态集合,即:当页面增加了标签,这个集合中也就增加了元素。
    
    document.getElementsByClassName()是h5新增的方法,有浏览器兼容性问题
    
    <script>
            // 1.获取元素 获取的是 tbody 里面所有的行
    var trs = document.querySelector('tbody').querySelectorAll('tr');
            // 2. 利用循环绑定注册事件
    for (var i = 0; i < trs.length; i++) {
                // 3. 鼠标经过事件 onmouseover
    	trs[i].onmouseover = function() {
                        // console.log(11);
    		this.className = 'bg';
    	}
                    // 4. 鼠标离开事件 onmouseout
       trs[i].onmouseout = function() {
    		this.className = '';
    	}
    }
    </script>
    <script>
            // 1. 全选和取消全选做法:  让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
            // 获取元素
            
            var j_cbAll = document.getElementById('j_cbAll'); 
            var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); 
            // 全选按钮注册事件
            j_cbAll.onclick = function() {
                    // this.checked 当前复选框的选中状态
                    console.log(this.checked);
                    for (var i = 0; i < j_tbs.length; i++) {
                        j_tbs[i].checked = this.checked;
                    }
             }
             // 给所有的子复选框注册单击事件
            for (var i = 0; i < j_tbs.length; i++) {
                j_tbs[i].onclick = function() {
                    // flag 控制全选按钮是否选中
                    var flag = true;
                    // 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
                    for (var i = 0; i < j_tbs.length; i++) {
                        if (!j_tbs[i].checked) {
                            flag = false;
                            break; 
                        }
                    }
                    // 设置全选按钮的状态
                    j_cbAll.checked = flag;
                }
            }
        </script>
    
    • querySelector 和 querySelectorAll 是两种常用的 DOM 查询方法。
    1. querySelector只能选择第一个匹配的节点;
    2. querySelectorAll可以选择多个节点,以","分隔开,返回的是个数组;
    querySelector 返回指定选择器的第一个元素对象  
    里面的选择器需要加符号 .box  #nav
    var tab_list = document.querySelector('.tab_list');
    var lis = tab_list.querySelectorAll('li');
    
    //获取特殊元素
    document.body //body元素对象
    document.documentElement //html元素对象
    
    //tab栏
            // 获取元素
            var tab_list = document.querySelector('.tab_list');
            var lis = tab_list.querySelectorAll('li');
            var items = document.querySelectorAll('.item');
            // for循环,给选项卡绑定点击事件
            for (var i = 0; i < lis.length; i++) {
                // 开始给5个小li 设置索引号 
                lis[i].setAttribute('index', i);
                lis[i].onclick = function() {
                    // 1. 上的模块选项卡,当前这一个底色会是红色,其余不变(排他思想)
                    // 干掉所有人 其余的li清除 class 这个类
                    for (var i = 0; i < lis.length; i++) {
                        lis[i].className = '';
                    }
                    // 留下我自己 
                    this.className = 'current';
                    // 2. 下面的显示内容模块
                    var index = this.getAttribute('index');
                    console.log(index);
                    // 干掉所有人 让其余的item 这些div 隐藏
                    for (var i = 0; i < items.length; i++) {
                        items[i].style.display = 'none';
                    }
                    // 留下我自己 让对应的item 显示出来
                    items[index].style.display = 'block';
                }
            }
    
    

    层级关系获取元素

    在这里插入图片描述
    节点就是前面DOM树中的所有节点,网页的所有内容都是节点。节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个
    基本属性。
     元素节点 nodeType 为 1
     属性节点 nodeType 为 2
     文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)

    //父级节点 返回最近的一个父节点或者null
    node.parentNode
    
    //返回包含指定节点的子节点的集合,该集合为即时更新的集合。
    //包含了所有的子节点,包括元素节点,文本节点等。
    //如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用childNodes
    parentNode.childNodes
    
    //是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回
    //是得到了各个浏览器的支持
    parentNode.children
    
    //第一个或最后一个 找不到则返回null。同样,也是包含所有的节点。
    //子节点 不仅仅是元素节点
    parentNode.firstChild
    parentNode.lastChild
    
    //返回第一个子元素节点,找不到则返回null。
    //注意:这两个方法有兼容性问题,IE9 以上才支持。
    parentNode.firstElementChild
    parentNode.lastElementChild
    
    //综上
    //firstChild 和 lastChild 包含其他节点,操作不方便
    //firstElementChild 和lastElementChild 又有兼容性问题
    //如果要第一个子元素节点,使用 parentNode.chilren[0]
    //如果是最后一个子元素节点,使用 parentNode.chilren[parentNode.chilren.length - 1]
    
    //兄弟节点 返回当前元素的下一个兄弟节点,找不到则返回null。包含所有的节点。
    node.nextSibling
    node.previousSibling
    
    //兄弟元素节点 注意:这两个方法有兼容性问题, IE9 以上才支持。
    node.nextElementSibling
    node.previousElementSibling
    
    //使用自己封装的获取兄弟节点函数 同时解决兼容性
    function getNextElementSibling(element) {
    	 var el = element;
    	 while (el = el.nextSibling) {
    		 if (el.nodeType === 1) {
    		 	return el;
    		 }
    	 }
    	 return null;
    }
    
    //下拉菜单
    var nav = document.querySelector('.nav');
    var lis = nav.children; // 得到4个小li
    for (var i = 0; i < lis.length; i++) {
    	lis[i].onmouseover = function() {
    		this.children[1].style.display = 'block';
    	}
    	lis[i].onmouseout = function() {
    		this.children[1].style.display = 'none';
    	}
    }
    
    node.cloneNode()//返回调用该方法的节点的一个副本。
    //如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
    //如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
    
    parentNode.replaceChild(newChild, oldChild);
    //用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。
    

    事件对象event

    事件是可以被 JavaScript 侦测到的行为。简单理解: 触发— 响应机制

    类型示例事件说明
    鼠标事件click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout用户鼠标操作相关
    键盘事件keydown, keyup, keypress键盘按键相关
    表单事件submit, change, input, focus, blur表单输入与交互
    触摸事件touchstart, touchmove, touchend移动端手势事件
    拖拽事件drag, dragstart, dragend, dragover, drop拖放交互
    页面事件load, resize, scroll, unload页面/窗口行为
    剪贴板事件copy, cut, paste复制剪切粘贴
    其他事件contextmenu, wheel, animationend右键菜单、滚轮、动画结束等
    • mouseover vs mouseenter(进入事件)
    属性mouseovermouseenter
    是否冒泡✅ 会冒泡❌ 不冒泡
    子元素触发✅ 进入子元素也会触发❌ 进入子元素不会再触发
    使用场景一般用于处理整块区域的悬停效果更适合精确处理当前元素悬停
    • mouseout vs mouseleave(离开事件)
    属性mouseoutmouseleave
    是否冒泡✅ 会冒泡❌ 不冒泡
    子元素触发✅ 移动到子元素也会触发❌ 只在真正离开元素才触发
    • 事件三要素
      事件源(谁):触发事件的元素
      事件类型(什么事件): 例如 click 点击事件
      事件处理程序(做啥):事件触发后要执行的代码(函数形式),事件处理函数
    • 执行事件步骤
      获取事件源,注册事件(绑定事件),添加事件处理程序(采用函数赋值形式)
    • 常见的鼠标事件
      在这里插入图片描述
      mouseenter鼠标经过
      mouseleave
      window.addEventListener(‘scroll’, function () { // 执行的操作 })
      window.onresize = function () {} 或window.addEventListener(‘resize’, function () { // 执行的操作 })

    keydown键盘按下
    keyup键盘抬起
    keypress键盘按下并弹起,onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift,ctrl 等。
    键盘事件执行顺序:keydown->keypress->keyup
    onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。
    e.key 替代keyCode返回改建的ascii码

    按键e.key 返回值
    A-Z"a" - "z"(小写,和键盘大小写无关)
    数字 0-9"0" - "9"
    Enter"Enter"
    空格" "(空字符串)
    方向键"ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"
    Shift"Shift"
    Ctrl"Control"
    Alt"Alt"
    Backspace"Backspace"
    Escape"Escape"
     //由事件参数对象的兼容性问题导致 
     1. 谷歌,火狐,IE9+ : 事件参数对象随着事件处理函数的参数传入 
     2. IE8以下: event对象必须作为window对象的一个属性(window.event)
    
    // 按下回车可以生成留言信息
        // 事件侦听的三要素
        // textarea.addEventListener('键盘事件', function() {发布信息})
        textarea.addEventListener('keyup', function (e) {
          // 怎么知道用户按下了回车键呢?
          // console.log(e.keyCode) 已经废弃    只要 e.key === 'Enter'
          // console.log(e.key)
          if (e.key === 'Enter') {
            // alert(11)
            // 发布新闻
            // 自动触发点击按钮
            send.click()
          }
        })
    =
            // 获取输入框
            var search = document.querySelector('input');
    		// 给document注册keyup事件
            document.addEventListener('keyup', function(e) {
                // 判断keyCode的值
                if (e.keyCode === 83) {
                    // 触发输入框的获得焦点事件
                    search.focus();
                }
            })
    
    

    input表单
    输入事件input,
    改变事件change 如checkbox状态改变

    //应用场景:防止复制
    1.禁止鼠标右键菜单
    contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单
    document.addEventListener('contextmenu', function(e) {
    	e.preventDefault();
    })
    2.禁止鼠标选中(selectstart 开始选中)
     document.addEventListener('selectstart', function(e) {
     e.preventDefault();
    })
    
    <body>
        <button id="btn">唐伯虎</button>
        <script>
            // 点击一个按钮,弹出对话框
            // 1. 事件是有三部分组成  事件源  事件类型  事件处理程序   我们也称为事件三要素
            //(1) 事件源 事件被触发的对象   谁  按钮
            var btn = document.getElementById('btn');
            //(2) 事件类型  如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
            //(3) 事件处理程序  通过一个函数赋值的方式 完成
            btn.onclick = function() {
                alert('点秋香');
            }
        </script>
            <script>
            // 1.获取元素 获取的是 tbody 里面所有的行
            var trs = document.querySelector('tbody').querySelectorAll('tr');
            // 2. 利用循环绑定注册事件
            for (var i = 0; i < trs.length; i++) {
                // 3. 鼠标经过事件 onmouseover
                trs[i].onmouseover = function() {
                        // console.log(11);
                        this.className = 'bg';
                    }
                    // 4. 鼠标离开事件 onmouseout
                trs[i].onmouseout = function() {
                    this.className = '';
                }
            }
        </script>
    </body>
    
    • 两种注册事件的区别:
    1. 传统:on注册(L0)
      同一个对象,后面注册的事件会覆盖前面注册(同一个事件)【唯一性】
      直接使用null覆盖就可以实现事件的解绑
      都是冒泡阶段执行的
    2. 事件监听注册(L2)addEventListener
      后面注册的事件不会覆盖前面注册的事件(同一个事件)【多个监听器按注册顺序依次执行】
      可以通过第三个参数去确定是在冒泡或者捕获阶段执行
      必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
      匿名函数无法被解绑
      ie9以下版本IE不支持,使用attachEvent()代替
    //事件监听版本
    事件源.on事件 = function() { }
    //若是用 L0 事件监听 即上述,则只有冒泡阶段,没有捕获
    
    元素.addEventListener('事件',执行的函数)//IE9以后才支持addEventListener
    //低版本的IE可以使用attacheEvent代替addEventListener
    target.addEventListener(type, listener, options);
    document.addEventListener("click", function () {
        console.log("点击事件触发");
    }, { once: true }); // 事件触发一次后自动移除
    options 配置对象的属性:
    属性名	类型	说明
    capture	Boolean	是否在捕获阶段执行监听器(默认为 false,即冒泡阶段执行)。
    once	Boolean	事件触发后监听器是否自动移除(默认为 false)。
    passive	Boolean	是否不会调用 event.preventDefault()(默认为 false)。
    
    eventTarget.attachEvent(eventNameWithOn, callback)
    eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on
    callback: 事件处理函数,当目标触发事件时回调函数被调用
    //注意:IE8 及早期版本支持
    
    function addEventListener(element, eventName, fn) {
     // 判断当前浏览器是否支持 addEventListener 方法
     if (element.addEventListener) {
     element.addEventListener(eventName, fn); // 第三个参数 默认是false
     } else if (element.attachEvent) {
     element.attachEvent('on' + eventName, fn);
     } else {
     // 相当于 element.onclick = fn;
     element['on' + eventName] = fn;
    }
    
    document.onclick = function(evt){  var e=window.event||event;  };
    //低版本的IE,通过 window.event获取事件对象。
    e = e || window.event;
    
    //删除事件
    eventTarget.onclick = null;
    eventTarget.removeEventListener(type, listener[, useCapture]);
    eventTarget.detachEvent(eventNameWithOn, callback);
    function removeEventListener(element, eventName, fn) {
     // 判断当前浏览器是否支持 removeEventListener 方法
     if (element.removeEventListener) {
     element.removeEventListener(eventName, fn); // 第三个参数 默认是false
     } else if (element.detachEvent) {
     element.detachEvent('on' + eventName, fn);
     } else {
     element['on' + eventName] = null;
    }
    
    • e.target 和 this 的区别:
      this 是事件绑定的元素, 这个函数的调用者(绑定这个事件的元素)
      e.target 是事件触发的元素。

    正常情况下target 和 this是一致的,但有一种情况不同,那就是在事件冒泡时单击子元素,父元素的事件处理函数也会被触发执行
    这时候this指向的是父元素,因为它是绑定事件的元素对象,
    而target指向的是子元素,因为他是触发事件的那个具体元素对象。

        <ul>
            <li>abc</li>
            <li>abc</li>
            <li>abc</li>
        </ul>
        <script>
            var ul = document.querySelector('ul');
            ul.addEventListener('click', function(e) {
                  // 我们给ul 绑定了事件  那么this 就指向ul  
                  console.log(this); // ul
    
                  // e.target 触发了事件的对象 我们点击的是li e.target 指向的就是li
                  console.log(e.target); // li
            });
        </script>
    
    • 事件流:浏览器处理事件的顺序

    如果addEventListener 第三个参数是 false 或者 省略,为冒泡阶段 ,son -> father ->body -> html -> document;
    如果addEventListener() 第三个参数是 true 那么在捕获阶段触发,document -> html -> body -> father -> son

    在这里插入图片描述
    在 JavaScript 中,事件流有 3 个阶段:
    捕获阶段(Capturing Phase):事件从 window 开始,沿 DOM 树向目标元素传播。
    目标阶段(Target Phase):事件到达目标元素,并执行其绑定的事件处理函数。
    冒泡阶段(Bubbling Phase):事件从目标元素开始,沿 DOM 树向 window 反向传播。当一个元素触发事件后,会依次向上调用所有父级元素的同名事件,事件冒泡是默认存在的。
    JS 代码只能执行捕获或者冒泡其中的一个阶段。默认事件监听器在冒泡阶段执行,但可以通过 addEventListener() 的 capture 选项改变。
    onclick 和 attachEvent 只能得到冒泡阶段。
    实际很少使用事件捕获,我们更关注事件冒泡。有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave(对应over 、out)。

    • 事件对象event/e
      在事件绑定的回调函数的第一个参数就是事件对象。事件触发时就会产生事件对象,事件对象有方法和属性,保存了事件相关的信息,还可以阻止事件冒泡和默认行为等。
      常用属性
      1.type获取当前的事件类型
      2.clientX/clientY获取光标相对于浏览器可见窗口左上角的位置
      3.offsetX/offsetY获取光标相对于当前DOM元素左上角的位置
      4.key用户按下的键盘键的值,现在不提倡使用keyCode已废弃(可读性差)
      5.pageX/pageY 鼠标相当于文档页面的X坐标/Y坐标 IE9+支持
      6.screenX/screenY 鼠标相对于电脑屏幕的X坐标/Y坐标
      在这里插入图片描述
    var pic = document.querySelector('img');
    document.addEventListener('mousemove', function(e) {
    var x = e.pageX;
    var y = e.pageY;
    pic.style.top = y - 40 + 'px';
    pic.style.left = x - 50 + 'px';
    })
    
    • 阻止事件流动
      因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
      若想把事件就限制在当前元素内,就需要阻止事件流动
      阻止事件流动需要拿到事件对象
      e.stopPropagation()此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
      非标准写法:IE 6-8 利用事件对象 cancelBubble 属性e.cancelBubble = true;
    if(e && e.stopPropagation){
     e.stopPropagation();
    }else{
     window.event.cancelBubble = true;
    }
    
    • 阻止默认行为,比如链接点击不跳转,表单域的跳转
      e.preventDefault()
      // 低版本浏览器 ie678 returnValue 属性 e.returnValue = false;
    • 鼠标经过事件:mouseenter 和mouseover的区别
       mouseover 和 mouseout 会有冒泡效果
       mouseenter 和 mouseleave 没有冒泡效果(推荐)
    • mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发
      之所以这样,就是因为mouseenter不会冒泡,跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡
    • 事件委托(代理、委派,在 jQuery 里面称为事件委派)是利用事件流的特征解决一些开发需求的知识技巧
       优点:给父级元素加事件(可以提高性能)
       原理:事件委托其实是利用事件冒泡的特点
       实现:事件对象.target 可以获得真正触发事件的元素

    例子:点击每个 li 都会弹出对话框,以前需要给每个 li 注册事件,是非常辛苦的,而且访问 DOM 的次数越多,这就会延长整个页面的交互就绪时间

    事件委托的原理
    不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
    以上案例:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上,
    ul 有注册事件,就会触发事件监听器。
    事件委托的作用:我们只操作了一次 DOM ,提高了程序的性能。

        <ul>
            <li>知否知否,点我应有弹框在手!</li>
            <li>知否知否,点我应有弹框在手!</li>
            <li>知否知否,点我应有弹框在手!</li>
            <li>知否知否,点我应有弹框在手!</li>
            <li>知否知否,点我应有弹框在手!</li>
        </ul>
        <script>
            // 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
            var ul = document.querySelector('ul');
            ul.addEventListener('click', function(e) {
                // e.target 这个可以得到我们点击的对象
                e.target.style.backgroundColor = 'pink';
            })
        </script>
    

    JS 特效三大系列【offset】【scroll】【client】

    offset

    1. 获得元素和其父元素的距离
    2. 获得元素自身的大小(宽度高度)
    3. 注意:返回的数值都不带单位
      在这里插入图片描述
    • offset 与 style 区别
      与 style . ( left / top / width / height ) 的区别:
    1. offset系列的是只读属性,而通过style的方式可以读写
    2. offset系列返回的数值类型(结果四舍五入),style返回的是字符串
    3. style 可以返回没有定位的元素的left值和top值,而 offsetLeft 和 offsetTop 不可以
      • offset 【想要获取元素大小位置,用offset】
        offset 可以得到任意样式表中的样式值(行内行外都可以)
        offset 系列获得的数值是没有单位的
        offsetWidth 包含padding+border+width
        offsetWidth 等属性是只读属性,只能获取不能赋值
        可视宽高,如果盒子隐藏,则获取结果为0
        在这里插入图片描述
      • style【想要给元素更改值,用style改变】
        style 只能得到行内样式表中的样式值
        style.width 获得的是带有单位的字符串
        style.width 获得不包含padding和border 的值
        style.width 是可读写属性,可以获取也可以赋值
    • 案例:获取鼠标在盒子内的坐标
    1. 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。
    2. 首先得到鼠标在页面中的坐标(e.pageX, e.pageY)
    3. 其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop)
    4. 用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标
    5. 如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动
    var box = document.querySelector('.box');
    box.addEventListener('mousemove', function(e) {
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
    this.innerHTML = 'x坐标是' + x + ' y坐标是' + y;
    })
    
    //offset
    offsetLeft : 用于获取元素到最近的定位父盒子的左侧距离 
    * 计算方式: 当前元素的左边框的左侧到定位父盒子的左边框右侧 
    * 如果父级盒子没有定位, 那么会接着往上找有定位的盒子 
    * 如果上级元素都没有定位,那么最后距离是与body的left值 
    
    offsetTop : 用于获取元素到最近定位父盒子的顶部距离
     * 计算方式:当前元素的上边框的上侧到定位父盒子的上边框下侧 
     * 如果父级盒子没有定位,那么会接着往上找有定位的盒子 
     * 如果上级元素都没有定位,那么最后距离是与body的top值 
     
    offsetWidth :用于获取元素的真实宽度(除了margin以外的宽度) 
     
    offsetHeight : 用于获取元素的真实高度(除了margin以外的高度) 
     
    offsetParent :用于获取该元素中有定位的最近父级元素 
    * 如果当前元素的父级元素都没有进行定位,那么offsetParent为body
    

    client

    在这里插入图片描述
    在这里插入图片描述

    //client 网页可视区域
    1. clientWidth : 获取网页可视区域的宽度 
    2. clientHeight: 获取网页可视区域的高度 
    3. clientX : 获取鼠标事件发生时的应用客户端区域的水平坐标 
    4. clientY : 获取鼠标事件发生时的应用客户端区域的垂直坐标
    
    //兼容问题
     //由浏览器对象不同导致 
     * 未声明 DTD: 谷歌,火狐,IE9+支持 document.body.clientWidth/clientHeight 
     * 已经声明DTD:IE8以下支持 document.documentElement.clientWidth/clientHeight 
     * 火狐/谷歌/ie9+以上支持的 window.innerWidth/innerHeight
    //兼容代码
    function client() {
      if (window.innerWidth) {
        return {
          "width": window.innerWidth,
          "height": window.innerHeight
        };
      } else if (document.compatMode === "CSS1Compat") {
        return {
          "width": document.documentElement.clientWidth,
          //屏幕宽度
          "height": document.documentElement.clientHeight
        };
      } else {
        return {
          "width": document.body.clientWidth,
          "height": document.body.clientHeight
        };
      }
    }
    
    • 淘宝 flexible.js 源码分析
      立即执行函数 (function(){})() 或者 (function(){}())
      主要作用: 创建一个独立的作用域。 避免了命名冲突问题
      下面三种情况都会刷新页面都会触发 load 事件。
      1.a标签的超链接
      2.F5或者刷新按钮(强制刷新)
      3.前进后退按钮
      但是 火狐中,有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。
      所以此时后退按钮不能刷新页面。
      此时可以使用 pageshow事件来触发。这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件`注意这个事件给window添加。
    window.addEventListener('pageshow', function(event) {
        if (event.persisted) {
            console.log('页面来自缓存');
        } else {
            console.log('页面是重新加载的');
        }
    });
    
    (function flexible(window, document) {
        // 获取的html 的根元素
        var docEl = document.documentElement
            // dpr 物理像素比
        var dpr = window.devicePixelRatio || 1
    
        // adjust body font size  设置我们body 的字体大小
        function setBodyFontSize() {
            // 如果页面中有body 这个元素 就设置body的字体大小
            if (document.body) {
                document.body.style.fontSize = (12 * dpr) + 'px'
            } else {
                // 如果页面中没有body 这个元素,则等着 我们页面主要的DOM元素加载完毕再去设置body
                // 的字体大小
                document.addEventListener('DOMContentLoaded', setBodyFontSize)
            }
        }
        setBodyFontSize();
    
        // set 1rem = viewWidth / 10    设置我们html 元素的文字大小
        function setRemUnit() {
            var rem = docEl.clientWidth / 10
            docEl.style.fontSize = rem + 'px'
        }
    
        setRemUnit()
    
        // reset rem unit on page resize  当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小
        window.addEventListener('resize', setRemUnit)
            // pageshow 是我们重新加载页面触发的事件
        window.addEventListener('pageshow', function(e) {
            // e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小
            if (e.persisted) {
                setRemUnit()
            }
        })
    
        // detect 0.5px supports  有些移动端的浏览器不支持0.5像素的写法
        if (dpr >= 2) {
            var fakeBody = document.createElement('body')
            var testElement = document.createElement('div')
            testElement.style.border = '.5px solid transparent'
            fakeBody.appendChild(testElement)
            docEl.appendChild(fakeBody)
            if (testElement.offsetHeight === 1) {
                docEl.classList.add('hairlines')
            }
            docEl.removeChild(fakeBody)
        }
    }(window, document))
    

    scroll

    如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll事件。
    window.scroll(x,y)滚动窗口至文档中的特定位置。注意,里面的x和y 不跟单位,直接写数字
    scrollLeft和scrollTop这两个属性是可以修改的
    检测页面滚动的头部距离(被卷去的头部)用document.documentElement.scrollTop,document.documentElement返回HTML元素对象
    在这里插入图片描述

    1. 页面被卷去的头部:可以通过window.pageYOffset 获得 如果是被卷去的左侧window.pageXOffset
    2. 注意,元素被卷去的头部是element.scrollTop , 如果是页面被卷去的头部 则是 window.pageYOffset
    //兼容问题
    * 未声明 DTD: 谷歌,火狐,IE9+支持 document.body.scrollTop/scrollLeft 
    * 已经声明DTD:IE8以下支持 document.documentElement.scrollTop/scrollLeft 
    * 火狐/谷歌/ie9+以上支持的 window.pageYOffest/pageXOffest
    //兼容代码
    function getScroll() {
      return {
        left: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft || 0,
        top: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop || 0
      };
    }
    getScroll().left//使用
    
    • 总结
    网页可见区域宽: document.body.clientWidth; 
    网页可见区域高: document.body.clientHeight; 
    
    网页真实元素宽: document.body.offsetWidth (包括边线的宽); 
    网页真实元素高: document.body.offsetHeight (包括边线的宽); 
    
    网页正文全文宽: document.body.scrollWidth; 
    网页正文全文高: document.body.scrollHeight; 
    
    网页被卷去的高: document.body.scrollTop; 
    网页被卷去的左: document.body.scrollLeft; 
    
    浏览器窗口的顶部边缘与屏幕的顶部边缘之间的距离: window.screenTop; 
    浏览器窗口的左边缘与屏幕的左边缘之间的距离: window.screenLeft; 
    
    屏幕分辨率的高: window.screen.height; 
    屏幕分辨率的宽: window.screen.width; 
    
    屏幕可用工作区高度: window.screen.availHeight; 
    屏幕可用工作区宽度: window.screen.availWidth;
    

    在这里插入图片描述
    他们主要用法:
    1.offset系列 经常用于获得元素位置 offsetLeft offsetTop
    2.client经常用于获取元素大小 clientWidth clientHeight
    3.scroll 经常用于获取滚动距离 scrollTop scrollLeft
    4.注意页面滚动的距离通过 window.pageXOffset 获得

    操作元素

    element.innerText 不解析html标签,去除空格、换行
    element.innerHTML 不去除
    两者的区别在于读取属性时,innerText不返回隐藏元素的文本,而textContent返回所有文本。
    IE<9不支持textContent。
    HTML 里隐藏文本框有多种方式
    1、type="hidden"     隐藏且不占位置。
    2、style="display:none;"    隐藏任何元素,文本框也不例外
    3、style="visibility:hidden;"     隐藏后还占位置
    
    //常见属性
    src,href
    id,alt,title
    
    //表单元素
    type,value,checked,selected,disabled
    btn.disabled = false; // 启用按钮
    input.select();//选中
    
    //样式属性
    element.style//是一个对象,里面存储行内样式键值对
    //当js修改样式时为行内样式,优先级高
    //获取不到类定义的样式
    //样式名字带-的改成驼峰命名 因为在js中会被解析成减法
    document.body.style.backgroundImage = 'url(' + this.src + ')';
    element.className//覆盖原先类名 class是关键字不能用
    element.classList.add("class1", "class2");
    document.querySelector('.tab .active').classList.remove('active')
    .remove(class1,class2,..);
    .toggle(class)//切换(添加或移除)指定的 CSS 类。如果该类已存在,则移除它;如果不存在,则添加它。
    
    box.style.display = 'none';
    
    //自定义属性
    element.属性//获取内置属性
    element.getAttribute('attr_name')//获取自定义属性
    element.setAttribute('attr_name','value')
    element.removeAttribute('属性');
    
    element.属性='value'
    
    //H5自定义属性目的:为了保存并使用数据。有些数据保存到页面中而不用保存到数据库中。
    //H5规定自定义属性data-开头做为属性名并且赋值。
    //但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。
    <div data-index="1"></div>
    element.setAttribute('data-index',2)
    H5新增 element.dataset.index 或者 element.dataset['index'] 
    //ie11才开始支持
    
     		var div = document.querySelector('div');
            // console.log(div.getTime);
            console.log(div.getAttribute('getTime'));
            div.setAttribute('data-time', 20);
            console.log(div.getAttribute('data-index'));
            console.log(div.getAttribute('data-list-name'));
            // h5新增的获取自定义属性的方法 它只能获取data-开头的
            //ie11才开始支持
            // dataset 是一个集合里面存放了所有以data开头的自定义属性
            console.log(div.dataset);
            console.log(div.dataset.index);
            console.log(div.dataset['index']);
            // 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
            console.log(div.dataset.listName);
            console.log(div.dataset['listName']);
    
    //定时器-间歇函数 使用定时器函数重复执行代码
    let timerId = setInterval(回调函数, 间隔时间ms)
    clearInterval(timerId)
    //定时器需要等待,所以后面的代码先执行
    //每一次调用定时器都会产生一个新定时器
    setInterval(repeat,1000)
    //实战:轮播图
    let i=0;
            fetch("./data.txt")
            .then(res => res.text())
            .then(data=>{
                console.log(data)
                try{
                    const jsondata=JSON.parse(data);
                    console.log(jsondata);
                    let timeID=setInterval(() => {
                        //不能在这个()里放入参数 因为此时的作用域是setInterval里面 
                        // setInterval 不会自动传递外部参数 会重新定义一个变量 值为undefined
                        i=(i+1)%9;
                        console.log(jsondata)
                        let imgsrc=jsondata[i].imgSrc
                        let text=jsondata[i].title
                        console.log(imgsrc,text)
                        let img=document.querySelector('.pic')
                        let title=document.querySelector('.text')
                        img.src=imgsrc
                        title.textContent=text
                    }, 1000);
                }
                catch(err){
                    console.log('解析失败',err)
                }
            })
            .catch(err=>console.log('读取文件失败',err))
    
    //实战:禁用按钮倒计时
        btn=document.querySelector('.btn');
        let s=10;
        let timeID=setInterval(update,1000);
        function update(){
            if(s==0){
            btn.disabled=false;
            btn.textContent=`我已经阅读用户协议`
            clearInterval(timeID);
            return
            }
            s-=1;
            btn.textContent=`我已经阅读用户协议(${s}s)`
        }
    
    //循环雪碧图 精灵图
    var lis = document.querySelectorAll('li');
    for (var i = 0; i < lis.length; i++) {
    // 让索引号 乘以 44 就是每个li 的背景y坐标  index就是我们的y坐标
    	var index = i * 44;
    	lis[i].style.backgroundPosition = '0 -' + index + 'px';
    }
    
    //仿新浪注册页面
            // 首先判断的事件是表单失去焦点 onblur
            // 如果输入正确则提示正确的信息颜色为绿色小图标变化
            // 如果输入不是6到16位,则提示错误信息颜色为红色 小图标变化
            // 因为里面变化样式较多,我们采取className修改样式
            // 1.获取元素
            var ipt = document.querySelector('.ipt');
            var message = document.querySelector('.message');
            //2. 注册事件 失去焦点
            ipt.onblur = function() {
                // 根据表单里面值的长度 ipt.value.length
                if (this.value.length < 6 || this.value.length > 16) {
                    // console.log('错误');
                    message.className = 'message wrong';
                    message.innerHTML = '您输入的位数不对要求6~16位';
                } else {
                    message.className = 'message right';
                    message.innerHTML = '您输入的正确';
                }
            }
    
    <!DOCTYPE html>
    <html lang="zh">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>dataset 示例</title>
    </head>
    <body>
        <button id="myButton" data-index="1">点击我</button>
    
        <script>
            // 获取按钮元素
            var button = document.getElementById("myButton");
    
            // 读取 dataset
            console.log("初始索引:", button.dataset.index); // 输出: "1"
    
            // 修改 dataset
            button.dataset.index = "2";
            console.log("修改后的索引:", button.dataset.index); // 输出: "2"
    
            // 监听点击事件
            button.addEventListener("click", function() {
                // 自增 index
                this.dataset.index = parseInt(this.dataset.index) + 1;
                alert("当前索引: " + this.dataset.index);
            });
        </script>
    </body>
    </html>
    

    因为我们插入的js节点已经存在于当前的文档树,因此这个节点首先会从原先的位置删除,再插入到新的位置。
    创建了一个< style >节点,然后把它添加到< head >节点的末尾,这样就动态地给文档添加了新的CSS定义

    //创建节点
    document.createElement('name')
    //创建由tagName指定的HTML元素。因为这些元素原先不存在,称为动态创建元素节点。
    
    element.remove();  // 删除该节点
    
    //将一个节点添加到指定父节点的子节点列表末尾。
    //类似于 CSS 里面的after 伪元素。
    parentnode.appendChild(child)
    
    //将一个节点添加到父节点的指定子节点前面。
    //类似于 CSS 里面的 before 伪元素。
    parentnode.insertBefore(NEWchild, 指定元素)
    
    //从 DOM 中删除一个子节点,返回删除的节点。
    parentnode.removeChild(child)
    //如不存在父子关系则删除不成功
    //注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置。
    //当你遍历一个父节点的子节点并进行删除操作时,要注意,children属性是一个只读属性,并且它在子节点变化时会实时更新。
    parent.removeChild(parent.children[0]);
    parent.removeChild(parent.children[1]); // <-- 浏览器报错
    //因为一共有0,1两号子节点,删掉0号后1号自动变0号
    
    //返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点
    node.cloneNode()
    //括号参数为空或者为false则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
    //如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
    
    
    • 三种动态创建元素的区别
       document.write()
       element.innerHTML
       document.createElement()
      区别
    1. document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
    2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
    3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂;innerHTML 复制节点的时候,不会复制原先节点的事件,会存在内存泄露问题
    4. createElement() 创建多个元素效率稍低一点点,但是结构更清晰,如果页面创建元素较少,建议使用 createElement()
      总结:不同浏览器下,innerHTML 效率要比 creatElement 高

    特效实例

    节流阀

    防止轮播图按钮连续点击造成播放过快。
    节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
    核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
    开始设置一个变量 var flag = true;
    If(flag) {flag = false; do something} 关闭水龙头
    利用回调函数 动画执行完毕, flag = true 打开水龙头

    移动端网页特效

    • 触屏事件概述
      在移动端,touch事件比鼠标事件执行效率高
      移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android 和 IOS 都有。
      touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。
      在这里插入图片描述
      如果移动距离小于 某个像素 就回弹原来位置
      如果移动距离大于某个像素就上一张下一张滑动。
      滑动也分为左滑动和右滑动 判断的标准是 移动距离正负 如果是负值就是左滑 反之右滑
    • 触摸事件对象(TouchEvent)
      TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多
      个触点,使开发者可以检测触点的移动,触点的增加和减少,等等
      touchstart、touchmove、touchend 三个事件都会各自有事件对象。
      触摸事件对象重点我们看三个常见对象列表:
      targetTouches属性和touches在手指按下和移动中都可以获取手指列表
      在这里插入图片描述
      因为平时我们都是给元素注册触摸事件,所以重点记住 targetTouches
    • 移动端拖动元素
    1. touchstart、touchmove、touchend 可以实现拖动元素
    2. 但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageX 和 pageY
    3. 移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离
    4. 手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置
      拖动元素三步曲:
      (1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
      (2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
      (3) 离开手指 touchend:
      注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();
    • 移动端常用开发插件
      比如移动端常见插件:iScroll、Swiper、SuperSlider 。
      https://www.swiper.com.cn/
      superslide: http://www.superslide2.com/
      iscroll: https://github.com/cubiq/iscrol
      移动端视频插件 zy.media.js
      H5 给我们提供了 video 标签,但是浏览器的支持情况不同。在移动端我们可以使用插件方式来制作。
    • click 延时解决方案
      移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。
      fastclick 插件解决 300ms 延迟。 使用延时
      GitHub官网地址: https://github.com/ftlabs/fastclick
    //解决方案
    //1 禁用缩放,浏览器禁用默认的双击缩放行为并且去掉 300ms 的点击延迟。
    <meta name="viewport" content="user-scalable=no">
    //2 使用插件。 fastclick 插件解决 300ms 延迟。
    if ('addEventListener' in document) {
     document.addEventListener('DOMContentLoaded', function() {
     FastClick.attach(document.body);/*等页面文档加载完成 不需要等所有的资源*/
     }, false);
    }
    //3 利用touch事件自己封装这个事件解决 300ms 延迟。
    //如果离开屏幕到触摸屏幕时间差值小于150ms,并且没有滑动过屏幕,那么我们就定义为点击
    //封装tap,解决click 300ms 延时
    function tap (obj, callback) {
     var isMove = false;
     var startTime = 0; // 记录触摸时候的时间变量
     obj.addEventListener('touchstart', function (e) {
     startTime = Date.now(); // 记录触摸时间
     });
     obj.addEventListener('touchmove', function (e) {
     isMove = true; // 看看是否有滑动,有滑动算拖拽,不算点击
     });
     obj.addEventListener('touchend', function (e) {
     if (!isMove && (Date.now() - startTime) < 150) { // 如果手指触摸和离开时间小于150ms 算点击
     callback && callback(); // 执行回调函数
     }
     isMove = false; // 取反 重置
     startTime = 0;
     });
    }
    //调用 
     tap(div, function(){ // 执行代码 });
    
    • 移动端常用开发框架
      前端常用的框架有 Bootstrap、Vue、Angular 等。
      MUI 原生UI前端框架
      MUI 是一个专门用于做手机 APP 的前端框架。
      MUI 的 UI 设计理念是:以 IOS 为基础,补充 Android 平台特有的控件。因此 MUI 封装的控件,UI 上更符合app 的体验。
      MUI 中文官网地址:http://dev.dcloud.net.cn/mui/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七灵微

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值