DOM事件流

问题:整个页面 由元素嵌套元素,document是最外面的最大的元素,当单击一个div时,同时你也单击了div的父元素,甚至整个页面。哪么是先执行父元素的单击事件,还是先执行div的单击事件???

1551166555833

  • 事件流: 描述的是从页面中接收事件的顺序。事件被触发时会在元素节点之间按照特定的顺序传播,这个过程就是事件流

    • 例如:首先给div注册单击事件,当你单击了div时,也就是单击body、单击了html,单击了document。
  • 事件捕获:网景最早提出:document对象首先接收到click事件,然后事件沿着DOM树逐级向下传播,直到传播到事件的实际目标。(由外向内)

  • 事件冒泡:IE最早提出:事件由具体的元素(文档中嵌套层次最深的哪个节点)接收,然后逐级向上传播到DOM最顶层节点的过程 。(由内向外)

  • W3C统一:制定统一的标准——先捕获再冒泡。IE、Firefox、Chrome、Opera和Safari即支持冒泡又支持捕获。目前低版本的IE浏览器只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流)建议使用冒泡流。

1551169007768

  • 总结:比如我们向水里面扔一块石头,首先石头下降的过程,可以理解为事件捕获,石头下降至水底之后产生气泡,气泡上升的过程,可以理解为事件冒泡。

  • 注意: js代码中只能执行捕获或者冒泡的其中一个阶段

    • onclick和attachEvent 只能得到冒泡阶段
    • addEventListener(type、listener、useCapture) 第三个参数如果是true ,表示在事件捕获阶段调用事件处理程序,如果是false(不写默认是false) 则表示的是事件冒泡阶段调用事件处理程序。
    • 实际开发中我们很少使用事件捕获,我们更关注事件冒泡
    • 有些事件是没有事件冒泡的,比如blur、focus、mouseenter、mouseleave
    • 如何阻止事件冒泡

事件流的三个阶段

  1. 捕获阶段

        <div class="father">
            <div class="son">son盒子</div>
        </div>
        <script>
            // 如果addEventListener() 第三个参数是 true 那么在捕获阶段触发
            // document -> html -> body -> father -> son
             var son = document.querySelector('.son');
    		// 给son注册单击事件,第3个参数为true
             son.addEventListener('click', function() {
                 alert('son');
             }, true);
             var father = document.querySelector('.father');
    		// 给father注册单击事件,第3个参数为true
             father.addEventListener('click', function() {
                 alert('father');
             }, true);
    		// 给document注册单击事件,第3个参数为true
            document.addEventListener('click', function() {
                alert('document');
            }, true)
        </script>
    复制代码
  2. 目标阶段

  3. 冒泡阶段

        <div class="father">
            <div class="son">son盒子</div>
        </div>
        <script>
            // onclick 和 attachEvent(ie) 在冒泡阶段触发
            // 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 
            // son -> father ->body -> html -> document
            var son = document.querySelector('.son');
    		// 给son注册单击事件
            son.addEventListener('click', function() {
                alert('son');
            }, false);
    		// 给father注册单击事件
            var father = document.querySelector('.father');
            father.addEventListener('click', function() {
                alert('father');
            }, false);
    		// 给document注册单击事件,省略第3个参数 效果跟冒泡阶段一样
            document.addEventListener('click', function() {
                alert('document');
            })
        </script>
    复制代码

阻止默认行为

  • 阻止默认行为 例如 表单的提交会跳转页面,a标签被单击后,默认会进行页面跳转

  • addEventListener() 注册事件 ,只有一种阻止默认行为的方式 ,e.preventDefault()

  • 传统的注册方式 onclick ,有三种注册方式(preventDefault(),returnValue,return false;)

        <a href="http://www.baidu.com">百度</a>
        <script>
            // 2. 阻止默认行为 让链接不跳转 只有一种阻止默认行为的方式
            var a = document.querySelector('a');
            a.addEventListener('click', function(e) {
                 e.preventDefault(); //  dom 标准写法
            });
            // 3. 传统的注册方式
            a.onclick = function(e) {
                // 普通浏览器 e.preventDefault();  方法
                e.preventDefault();
                // 低版本浏览器 ie678  returnValue  属性
                e.returnValue = false;
                // 我们可以利用return false 也能阻止默认行为 没有兼容性问题
                return false;  //特点是return后面的代码不会执行
            }
        </script>
    复制代码

阻止事件冒泡

事件冒泡本身的特性,会带来坏处,与好处 要怎么利用冒泡这一特性

  • e.stopPropagation( ) //有兼容性,只支持高版本的浏览器

  • e. cancelBubble=true; // 只支持 IE6-8, 低版本浏览器

        <div class="father">
            <div class="son">son儿子</div>
        </div>
        <script>
            var son = document.querySelector('.son');
    		// 给son注册单击事件
            son.addEventListener('click', function(e) {
                alert('son');
                e.stopPropagation(); // stop 停止  Propagation 传播
                window.event.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
            }, false);
    
            var father = document.querySelector('.father');
    		// 给father注册单击事件
            father.addEventListener('click', function() {
                alert('father');
            }, false);
    		// 给document注册单击事件
            document.addEventListener('click', function() {
                alert('document');
            })
        </script>
    复制代码

冒泡的应用—事件委托

  • 作用:减少操作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的动画是通过定时器(setInterval())不断移动盒子实现的

注意:js动画中元素需要 有定位才能动

  • 简单动画函数的封装
    • 函数需要传递两个参数 移动距离动画对象

如何给不同的动画元素指定不同的定时器?

  • 核心原理:利用js是一门动态语言,可以很方便的给当前对象添加属性。
<script>
        // var obj = {};
        // obj.name = 'andy';
        // 简单动画函数封装obj目标对象 target 目标位置
        // 给不同的元素指定了不同的定时器
        function animate(obj, target) {
            // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
            // 解决方案就是 让我们元素只有一个定时器执行
            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                if (obj.offsetLeft >= target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                }
                obj.style.left = obj.offsetLeft + 1 + 'px';

            }, 30);
        }

        var div = document.querySelector('div');
        var span = document.querySelector('span');
        var btn = document.querySelector('button');
        // 调用函数
        animate(div, 300);
        btn.addEventListener('click', function() {
            animate(span, 200);
        })
    </script>
复制代码

缓动效果原理

缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来

思路:

  1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
  2. 核心算法: (目标值 – 现在的位置)   /  10    做为每次移动的距离步长
  3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器 
  4. 注意步长值需要取整  

动画函数多个目标值之间移动

可以让动画函数从 800 移动到 500。

当我们点击按钮时候,判断步长是正值还是负值

​ 1.如果是正值,则步长往大了取整

​ 2.如果是负值,则步长 向小了取整

动画函数添加回调函数

回调函数原理:将函数作为实参传递给另一个函数的形参中使用 。当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。

回调函数写的位置:定时器结束的位置。

动画完整版代码

function animate(obj, target, callback) {
    // console.log(callback);  callback = function() {}  调用的时候 callback()

    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        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();
            // }
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';

    }, 15);
}
复制代码

节流阀

防止轮播图按钮连续点击造成播放过快。

节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。

核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。

  开始设置一个变量var flag= true;

 If(flag){flag = false; do something}       关闭水龙头

 利用回调函数动画执行完毕, flag = true     打开水龙头

移动端触屏事件概述

移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android和 IOS 都有。

touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。

常见的触屏事件如下:

图片1

click延时解决方案

  • 移动端click事件会有300ms的延时,原因是移动端屏幕双击会缩放页面

    • 解决方案:

      1. 禁用缩放。浏览器禁用默认的双击缩放行为并且去掉300ms的点击延迟

          <meta name="viewport" content="user-scalable=no"> 
        复制代码
      2. 使用插件。fastclick 插件解决300ms 延迟。

        • GitHub官网地址: https://github.com/ftlabs/fast…

        • 引入js插件文件

        • 设置配置文件

          if ('addEventListener' in document) {
                      document.addEventListener('DOMContentLoaded', function() {
                                 FastClick.attach(document.body);
                      }, false);
          }
          复制代码

          1551797533(1)

本地存储

本地存储特性

  1. 数据存储在用户的浏览器中
  2. 设置、获取、清除方便、甚至页面刷新不丢失数据
  3. 存储容量大,sessionStorage约5M、localStorage约20M
  4. 只能存储字符串、可以通过JSON.stringify()将对象编码成字符串后存储
  5. 使用本地存储的数据,需要将本地存储的对象格式的字符串转化成对象使用 JSON.parse

window.sessionStorage

  • 生命周期是 关闭浏览器窗口

  • 以键值对的形式存储

  • 可在同一个窗口(页面)下数据可共享

  • 存储容量约 5M

    1. 存储数据

      //以键值对的形式存储
      sessionStorage.setItem(key,value)
      复制代码
    2. 获取数据

      //获取key对应的值 
      sessionStorage.getItem(key)
      复制代码
    3. 删除数据

      //删除key对应的值
      sessionStorage.removeItem(key)
      复制代码
    4. 清除数据:(所有都清除掉)

      //删除所有数据
      sessionStorage.clear()
      复制代码

window.localStorage

  • 生命周期为永久性,除非手动清除
  • 以键值对的形式存储
  • 在同一个浏览器 下的所有窗口(页面)都可共享数据
  • 存储量 约20M

存储数据:

//以键值对的形式存储
localStorage.setItem(key, value)
复制代码

获取数据:

//获取key对应的值
localStorage.getItem(key)
复制代码

删除数据:

//删除key对应的值 
localStorage.removeItem(key)
复制代码

清空数据:(所有都清除掉)

//清除所有的数据 
localStorage.clear()
复制代码

本地存储的小结

sessionStorage localStorage
生命周期 关闭浏览器 永久性,除非手动清除
作用域 在同一页面下共享数据 在同一浏览器下共享数据
存储量 5M 20M

文件读取

1.  首先需要一个上传控件用来获取上传的文件
2.  点击上传按钮的时候通过onchange
3.  通过files获取上传文件    
4.  创建读取器  new FileReader();
5.  读取器中的方法,属性及事件

方法:  
    readAsText(file)            读取成文本形式
    readAsDataURL(file)         读取成文件路径形式
属性:  
	reader.result                获取读取结果

事件:  onload                       当读取操作完成后
备注:获取读取结果的时候,一定要在读取操作结束后执行(在onload事件中完成)
复制代码
<body>
	<img src="" alt="">
	<input type="file">

	<script type="text/javascript">
		var img = document.querySelector("img");
		var input = document.querySelector("input");
			input.onchange = function(e) {

				//获取读取文件
				var file = this.files[0];

				//创建读取器
				var reader = new FileReader();

				//读取文件
				reader.readAsDataURL(file);

				//当文件读取成功后
				reader.onload = function() {
					
					//判断文件后缀名是否合法
				 var file_name = file.name;
                   var f_name = file_name.substring(file_name.lastIndexOf('.')) ;
					
					var f_ary = ['.jpg', '.png', '.gif', '.ico'];

					var flag = true;
					
					for(var i = 0; i < f_ary.length; i++) {

						if (f_name == f_ary[i]) {
							img.src = this.result;
							flag = true;
							break;
						}else {
							flag = false;
							continue;
						}
					}
					if (flag) {
						alert('读取成功!');
					}else {
						alert('文件格式错误!');
					}
				}
			}
	</script>
</body>
复制代码

原生js 之 (DOM操作) >-<

「点赞、收藏和评论」

❤️关注+点赞+收藏+评论+转发❤️,鼓励笔者创作更好的文章,谢谢?大家。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享