• 前端开发工程师


    第1章.基础篇(上)

    Abstract:文档树、节点操作、属性操作、样式操作、事件

    DOM (Document Object Model) - 文档对象模型

    以对象的方式来表示对应的html,它有一系列的规范

    i.e. 

    在浏览器中,DOM是通过JS实现的。

    DOM:

    DOM Core:核心结构、API的定义

    DOM HTML: 定义HTML如何转化成对象(HTML对应的对象)-- 操作节点

    DOM Style:样式转换成对象 -- 操作样式

    DOM Event:事件对象的模型 -- 响应用户的操作

     

    文档树

    HTML -> DOM树

    节点遍历

    node.parentNode

      .firstChild

      .lastChild

      .previousSibling

      .nextSibling

      .firstElementChild

      .lastElementChild

      .nextElementSibling

      .previousElementSibling

    i.e.

    p.parentNode是body

    p.firstChild是hello,

    p.firstElementChild是span

    p.lastElementChild是img

    p.lastChild是img

    p.previousSibling没有,则返回null

    p.nextSibling是div

     

    节点类型:

    ELEMENT_NODE:元素节点 (如上body, p, div, span, img)

    TEXT_NODE:文本节点(如上hello,, 微专业, mooc)

    COMMENT_NODE

    DOCUMENT_TYPE_NODE

    课堂交流:如何实现浏览器兼容版的element.child

    element.children能够获取元素的元素子节点,但是低版本的ie不支持,如何在低版本的ie上兼容类似的功能。

    http://www.jianshu.com/p/b7e111015c48 

    节点操作

    Abstract: getElementById, getElementsByClassName, getElementsByTagName, querySelector(All), createElement, innerText, appendChild, insertBefore, removeChild, innerHTML

    浏览器读取HTML渲染出页面结构以后,还可以通过JS改变页面的结构

    获取节点:

    通过节点关系可以获取节点(父子关系、兄弟关系)

    缺点:可维护性差,如果一个节点的位置发生了变化,则关系也可能会被打乱

    所以,一般使用接口来获取节点(获得的是节点对象:

    getElementById:

    element = document.getElementById(id):id在document中是唯一标识

    getElementsByTagName:

    collection = element.getElementsByTagName(tagName):通过元素来调用来获取元素内的节点

    若tagName为"*", 则会获取指定元素element包含的所有的后代元素节点

    注:collection是动态的集合

    getElementsByClassName:

    collection = element.getElementsByClassName(className)

    通过空格分割,可以指定多个类名(无序),获取同时具有多个类名的元素

    但是IE 6/7/8不兼容getElementsByClassName

    function getElementsByClassName(element, classNames) {
        if (element.getElementsByClassName) {
            // 特性侦测,如果兼容则优先使用W3C规范的方式
            return element.getElementsByClassName(classNames);
        } else {
            var elements = element.getElementsByTagName("*"); // 所有后代元素
            var result = [];
            var element,
                classNameStr,
                flag;
            classNames = classNames.split(' ');
            for (var i = 0; element = elements[i]; i++) {
                classNameStr = ' ' + element.className + ' ';
                flag = true;
                for (var j = 0, className; className = classNames[j]; j++) {
                    if (classNameStr.indexOf(' ' + className + ' ') == -1) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    result.push(element);
                }
            }
            return result;
        }
    }

    querySelector:

    element = element.querySelector(selector)

    返回第一个符合的元素

    querySelectorAll:

    list = element.querySelectorAll(selector)

    i.e. 

    <div id="users">
        <h2>....</h2>
        <ul>
            <li class="user">Satoshi</li>
            <li class="user">春来草青</li>
            <li class="user last">Kash</li>
        </ul>
    </div>

    var users = document.querySelector(#users"); // 获取到元素div#users

    users.querySelectorAll(".user"); // 获取到 [ li.user  li.user  li.user.last ]

    document.querySelectorAll("#users .user"); // 获取到同上 [ li.user  li.user  li.user.last ]

    注:list和collection不同之处:list静态的,获取到的结果之后就不会自动同步改变了;而collection是动态的

    IE6/7不兼容,IE 8部分兼容

    若有如下场景:

    想要在<ul> ... </ul>的末尾处增加一个<li>节点,应该怎么做

    <ul>
        <li>...</li>
        ...
        <li class="user">
            <img src="lf.jpg">
            <a href="/user/lf">lifeng</a>
        </li>
    </ul>

    1. 创建li节点;设置li节点的class属性;插入li节点

    2. 创建img节点;设置img节点的src属性;插入img节点

    3. 创建a节点;设置a节点的hrf属性;设置a节点的内容;插入a节点

    var li = document.createElement("li");
    li.className = 'user';
    ul.appendChild(li);
    var img = document.createElement("img");
    img.src = 'xxx.jpg';
    li.appendChild(img);
    var a = document.createElement("a");
    a.href = '/user/xxx';
    a.innerText = "lifeng";
    li.appendChild(a);

    创建节点

    element = document.createElement(tagName);

    i.e. var li = document.createElement("li");

    修改节点

    element.textContent:表示节点及其后代节点的文本内容,但是IE9不支持

    i.e. a.textContent = "lifeng";

    innerText:不规范,但是经常使用(Firefox不支持)(几乎和textContext一模一样)

    i.e. a.innerText = "lifeng";

    如果要让Firefox兼容的话(使用textContent)

    if(!('innerText' in document.body)) {  // 特性侦测
        HTMLElement.prototype._defineGetter_("innerText", function() {
            return this.textContent;
        });
        HTMLElement.prototype._defineSetter_("innerText", function(s) {
            return this.textContent = s;
        });
    }

     

    插入节点

    var achild = element.appendChild(achild); // 在element元素内的末尾追加achild节点

    i.e. ul.appendChild("li");

    var achild = element.insertBefore(achild, referenceChild); // 在referenceChild元素之前插入achild节点

    i.e. users.insertBefore(li, ul.firstChild);

    删除节点

    var child = element.removeChild(child);

    i.e. user.parentNode.removeChild(user);

     

    innerHTML:节点的HTML内容

    刚才的例子中,为了添加一个lifeng的<li>节点需要那么长的代码,可以使用innerHTML来提高效率

    i.e. 设已经添加了一个li节点在ul的末尾;

    li.innerHTML = '<img src="xxx.jpg">

    <a href="/user/xxx">lifeng</a>';

    li.innerHTML = ""; // 起到删除所有子节点的作用

    那可以使用 li.innerHTML += "<li>.......</li>"; 来实现吗?

    可以,但是,相当于重新设置了HTML的内容,之前修改过/添加了的事件/样式/状态就会被清除 (覆盖)

    innerHTML的问题:内存泄露、安全问题

    i.e. 安全问题:利用innerHTML运行代码

    var userName = '</a><a onclick="alert("我是黑客");" href="#">lifeng';
    li.innerHTML = '<img src="xxx.jpg">
            <a href="/user/xxx/">' + userName + '</a>';

    建议仅用于创建新节点 

    属性操作

    实例:登录框的登陆按钮在点击一次后为了避免重复提交会修改按钮的disabled属性让其不可点击

    每个HTML attribute都可以对应一个DOM property,修改DOM property就能实现对HMLT attribute的修改 

    <div>
        <label for="userName">用户名:</label>
        <input id="userName" type="text" class="u-txt">
    </div>

    i.e. 上例中对应的DOM property为:label.htmlFor="userName"; input.id="userName"; input.type="text"; input.className="u-txt"(因为for和class是关键字)

    三种方法进行属性操作:属性访问器、get/setAttribute、自定义属性dataset

    property accessor:

    i.e. input.className;  // "u-txt"

    input["id"];  // "userName"

    input.value = 'www@163.com'; // 给input增加一个value属性并赋值

    转换的类型:

    (Boolean类型的变量除非设置成false,否则不论是空还是0都表示true)

    转换过的为实用对象

    优缺点:通用性差--名字异常;扩展性差--每增加一个html属性就需要对应一个DOM属性;优点:获得的是一个实用对象

    set/getAttribute:

    var attribute = element.getAttribute(attributeName); // 读

    element.setAttribute(attributeName, value);  // 写

    i.e. input.getAttribute("class");  // "u-txt"

    input.setAttribute("value", "www@163.com");

    input.setAttribute("disabled", "");  // 设置disabled属性为true(前面提到了,只要属性出现了而且不是false,那么就是true)

    转换的类型都为String,获得的是属性字符串

        

    优缺点:只能处理字符串,推荐如果是纯字符串操作就使用get/setAttribute()

    dataset:自定义属性

    HTMLElement.dataset

    data-*属性集

    一般用于元素上保存数据(自定义的数据属性)

    <div id="user" data-id="123456" data-account-name="wwq" data-name="smith" data-email="wwq123@163.com" data-mobile="123123">wwq</div>

    属性名:将"data-"去掉,如果有连接符,会以大写开头表示

    i.e. 鼠标悬停在姓名上时会显示对应详细信息的表格

    <body>
        <ul>
            <li data-id="123456" data-account-name="wwq"
                data-name="魏文庆" data-email="wwq123@163.com" 
                data-mobile="13524543878">wwq</li>
            <li data-id="123457" data-account-name="cjf"
                data-name="蔡剑飞" data-email="cjf123@163.com" 
                data-mobile="13968789868">cjf</li>
        </ul>
        <div id="card" style="display:none">
        <!-- 将空卡片隐藏 -->
            <table>
                <caption id="accountName"></caption>
                <tr><th>姓名:</th><td id="name"></td></tr>
                <tr><th>邮箱:</th><td id="email"></td></tr>
                <tr><th>手机:</th><td id="mobile"></td></tr>
            </table>
    
        </div>
        <script>
            function $(id){
                return document.getElementById(id);
            }
            
            var lis = document.getElementsByTagName('li');
            for(var i = 0, li;li = lis[i]; i++){
                li.onmouseenter = function(event){
                    event = event || window.event;
                    var user = event.target|| event.srcElement;
                    var data = user.dataset;
    
                    // 插入相关数据
                    $('accountName').innerText = data.accountName;
                    $('name').innerText = data.name;
                    $('email').innerText = data.email;
                    $('mobile').innerText = data.mobile;
                    // 显示卡片
                    $('card').style.display = 'block';
                };
    
                li.onmouseleave = function(event){
                    $('card').style.display = 'none';
                };
            }
            
        </script>
    </body>

    dataset在低版本浏览器中不兼容,怎么实现?

    function dataset(element) { // my own version(haven't checked yet)
        if (element.dataset) {
            // check whether the original version od dataset is available
            return element.dataset;
        } else {
            var data = [];
            for (var i = 0; i < element.attributes.length; i++) {
                // traverse all the attributes element has
                if (/^data-/.test(element.attributes[i].nodeName) {
                    // attribute_name starts with "data-"
                    data[element.attributes[i].nodeName.replace("data-","")] = element.attributes[i].nodeValue;
                }
            }
            return data;
        }
    }

      

    样式操作

    样例:

    格式不正确

    QQ空间换皮肤等

    -- 可通过JS动态修改样式

    CSS --> DOM

    <head>
        <link rel="stylesheet" href="base.css">
        <style>
            body {margin: 30;}
            p {color: #aaa; line-height: 20px; }
        </style>
    </head>
    <body>
        <p style="color:red;"> paragraph</p>
    </body>

    3中css的引用:

    <link> 对应的为element.sheet

    <style> 对应的为element.sheet

    style="" 对应的为element.style

    整张页面的所有样式对应的为document.styleSheets

    i.e. element.sheet:

    element.sheet.cssRules : 对应body{...} 和 p{...}

    element.sheet.cssRules[1] : 对应p{...}

    element.sheet.cssRules[1].selectorText : 对应选择器p

    element.sheet.cssRules[1].style : 属于类CSSStyleDeclaration的对象。对应p{...}中的css声明color:#aaa; line-height:20px;

    element.sheet.cssRules[1].style.lineHeight : 对应属性值20px

    element.style:属于CSSStyleDeclaration类的对象,遇上相同,对应css声明color:red;

    element.style.color : 对应red

    对样式的增删查改:

    更新样式:

    element.style.borderColor = 'red';

    element.style.color = 'red';

    -- 更新一个属性需要一条语句,而且不是css格式

    更好的方式:cssText

    i.e. element.style.cssText = 'border-color:red; color:red;';

    -- 一条语句可以设置一个元素,符合css格式

    但是:样式与逻辑混合

    更好的方式:更新class -- 开发中使用的方法

    首先,在css样式中增加样式 .invalid { border-color:red; color:red; }

    在JS中输入框对象处:element.className += 'invalid';

    但是如果要一次性修改批量元素的样式呢,比如上述的QQ空间换肤实例:可以使用更换样式表

    原版本 <link rel="stylesheet" href="style.css">

    可以拆分为两个样式表 base.css 和 skin.spring.css 

    要换肤时,比如换成夏天皮肤,则将skin.spring.css 换成 skin.summer.css即可

    <head>
        <title>换肤 - 更新样式</title>
        <link rel="stylesheet" href="base.css">
        <link id="skin" rel="stylesheet" href="skin.spring.css">
    </head>
    <body>
        <div class="m-tw clearfix">
            <div class="u-img">
                <a href="#"><img src="zhm.jpg" alt=""></a>
            </div>
            <div class="txt">
                <h3><a href="#">张惠妹</a></h3>
                <p>亞洲國寶級傳奇天后「 a MEI」我是a MEI,一個你認識很久,卻認識不完的女人。</p>
            </div>
        </div>
        <button id="change">换肤</button>
        
        <script src="../util.js"></script>
        <script>
            Util.addEventListener($('change'), 'click', changeSkin);
    
            function changeSkin(){
                $('skin').href = "skin.summer.css";
            }
    
        </script>
    </body>
    /* skin.spring.css */
    body{background-color: #d6e6c6;}
    .m-tw .u-img{border-color: #6e9d41;}
    .m-tw p{color: #367701;}
    .m-tw h3{background-color: #6e9d41;}
    .m-tw h3 a, .m-tw h3 a:hover{color: #fff;}
    /* skin.summer.css */
    body{background-color: #fefaf7;}
    .m-tw .u-img{border-color: #a84c5b;}
    .m-tw p{color: #6d3e48;}
    .m-tw h3{background-color: #a84c5b;}
    .m-tw h3 a, .m-tw h3 a:hover{color: #fff;}

    相似的,也可以进行删除样式表、添加样式表等操作

    获取样式:

    i.e. 有一<input type="text">

    element.style.color;  // ""

    字体是黑色,为什么获取到的为空呢?因为element.style对应的为内嵌样式表,确实为空

    若是<input type="text" style="color:red"> 则可获取到"red"

    实际上有三种样式设置方式,而且style获取到的不一定是实际样式,所以不采取上述方法获取样式属性

    window.getComputedStyle()

    var style = window.getComputedStyle(element [, pseudoElt]);

    // 返回值的类型也是CSSStyleDeclaration,但是是只读的,包含了所有的属性名和属性值的键值对

    i.e. window.getComputedStyle(input).color;  // "rgb(0,0,0)"

    IE9以下不兼容,可以使用element.currentStyle(不规范)

    http://web.zhydaxq.com/2017/04/14/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E7%89%88%E7%9A%84window-getcomputedstyle/

      

    事件

    什么是DOM事件?当我们点击一个DOM元素时,或键盘按下一个键,或在输入框中输入内容,或页面加载完成时,都是DOM事件

    事件流:

    DOM事件的处理过程 http://www.w3.org/TR/uievents/#dom-event-architecture 

    i.e. 

    当点击了a标签时,就会产生一个DOM的click事件,事件的处理过程:

    capture phase:从DOM树的根节点开始捕获直到事件节点的父元素:window->document->html->body->div->p

    target phase:事件的触发过程 从父节点p到事件发生所在节点a

    bubble phase:冒泡过程:从事件所在节点的父节点开始,冒泡到最顶层的window对象

    (IE低版本没有捕获过程;而且也不是所有事件都有这三个过程,比如页面load事件并没有冒泡过程)

    事件注册与触发

    注册事件

    eventTarget.addEventListener(type, listener [,useCapture])

    type: 事件类型;listener:事件处理函数;useCapture:是否为捕获过程(默认处理的是冒泡过程)

    var element = document.getElementById('div1');
    var clickHandler = function(event) {
        // TO-DO
    }
    element.addEventListener('click', clickHandler, false); // 默认为false

    还有一种方法进行注册:

    element.onclick = clickHandler;

    缺陷:这种注册方式下的事件处理函数只能有一个,但很多情况下需要注册多个事件处理函数

    取消事件注册

    eventTarget.removeEventListener(type, listener [,useCapture])

    i.e. element.removeEventListener('click', clickHandler, false);

    或者通过 element.onclick = null; 来取消注册

    事件触发:使用代码来触发事件(不包括点击之类的触发)

    eventTarget.dispatchEvent(type);  // type:事件类型;就能触发相应类型的DOM事件了

    i.e. elem.dispatchEvent('click');  // 使用代码触发elem元素的单击事件

    低版本浏览器的兼容:IE6/7/8没有采用这些W3C标准

    没有capture捕获阶段,只能处理冒泡阶段

    事件注册与取消:attchEvent/detachEvent

    事件触发:fireEvent(e)

    var addEvent = document.addEventListener ?
        function(elem, type, listener, useCapture) {
            elem.addEventListener(type, listener, useCapture);
        } :
        function(elem, type, listener, useCapture) {
            elem.attachEvent('on' + type, listener);
        };    // 区别:1. 事件类型前缀了'on',2. 没有useCapture参数
    
    var delEvent = document.removeEventListener ?
        function(elem, type, listener, useCapture) {
            elem.removeEventListener(type, listener, useCapture);
        } :
        function(elem, type, listener, useCapture) {
            elem.detachEvent('on' + type, listener);
        };

    事件对象

    当事件触发时,会调用事件处理函数,此时需要事件状态的信息,事件对象就包含了这些信息。引擎在调用处理函数时会传入事件对象

    i.e. 上例elem.addEventListener('click', clickHandler, false);中

    触发click事件时会调用clickHandler函数,执行时会传入event对象,即事件对象(var clickHandler = function(event) {...})

    可能该事件对象包含了鼠标位置等信息

    在IE的低版本里有一些不同,事件的event对象并不是直接通过函数传入的,而是放在window对象中

    兼容版本:

    var elem = document.getElementById('div1');
    var clickHandler = function(event) {
        event = event || window.event;
        // TO-DO
    }

    事件对象的属性和方法:

    不同分类的事件在不同场景下的事件对象都有可能不同

    通用的属性和方法:

    属性:

    type: 事件类型,如click

    target(IE低版本为srcElement): 事件触发的节点,如a元素

    currentTarget: 当前处理事件的节点(处理一个事件时,不一定要把事件注册在target上,可以注册在父节点(由于冒泡))

    只有事件处于目标阶段时,currentTarget和target的值才是肯定相同的

    方法:

    stopPropagation: 阻止传播,使事件的冒泡停止

    preventDefault: 阻止默认行为

    默认行为:比如双击文字时文字会被选中,比如单击连接时连接会被打开

    stopImmediatePropagation: 阻止冒泡

    i.e. 

    event.stopPropagation() (W3C): 阻止冒泡到父节点

    event.cancelBubble=true (IE低版本)

    event.stopImmediatePropagation() (W3C): 两个结果:1. stopPropagation(); 2. 所有该节点的后续事件也不会触发

    event.preventDefault() (W3C)

    event.returnValue=false (IE低版本)

     

    事件分类 http://www.w3.org/TR/uievents/ 

    Abstract: 事件分类及继承关系;鼠标事件类型、鼠标事件对象、鼠标事件举例;键盘、输入、焦点事件类型、事件对象、事件举例

    MouseEvent:

    事件类型:

    click:单击

    dbclick:双击

    mousedown:按下鼠标

    mousemove:鼠标移动

    mouseout:鼠标从某元素上移开

    mouseover:鼠标在某元素上(若鼠标在元素子元素上,也是可行的--可冒泡)

    mouseup:释放鼠标

    mouseenter:鼠标进入元素(只有鼠标在该元素上时,不可冒泡)

    mouseleave:鼠标离开元素(不可冒泡,与mouseout(可冒泡)的区别和mouseenter和mouseover的区别一样)

    属性:

    clientX, clientY:到页面的左上的距离

    screenX, screenY:到屏幕的左上的距离

    ctrlKey, shiftKey, altKey, metaKey:事件触发时若键按下则为true

    button:值为0/1/2,表示按下的是鼠标的左键/中间键/右键

    顺序:

    i.e. 鼠标从元素A外面移动到元素A上面,再划出去的过程中:

    mousemove -> mouseover(A) -> mouseenter(A) -> mousemove -> mouseout(A) -> mouseleave(A)

    (mousemove一直在触发,触发间隔由浏览器决定)

    i.e. 点击元素:

    mousedown -> [mousemove] -> mouseup -> click

    (click事件是在鼠标松开之后才触发的)

    实例. 拖拽div

    <div id="div1"></div> 
    <style type="text/css">
        #div1{ position:absolute; top:0; left:0;
            border:1px solid #000;
            width:100px; height:100px;
        }
    </style> 
    var elem = document.getElementById("div1");
    var clientX, clientY, moving;
    var mouseDownHandler = function (event) {
        // 鼠标按下
        event = event || window.event;
        clientX = event.clientX;
        clientY = event.clientY;
        moving = !0;  // 点下去后设置moving为true
    }
    var mouseMoveHandler = function(event) {
        // 鼠标移动(drag的移动过程)
        if (!moving) return;  // 鼠标还未down
        event = event || window.event;
        var newClientX = event.clientX,
              newClientY = event.clientY;
        var left = parseInt(elem.style.left) || 0,
              top = parseInt(elem.style.top) || 0;
        elem.style.left = left + (newClientX - clientX) +'px';  // 用offset量来确定div的移动
        elem.style.top = top + (newClientY - clientY) +'px';
        clientX = newClientX;  // 更新clientX和clientY,用于下次move触发
        clientY = newClientY;
    }
    var mouseUpHandler = function (event) {
        moving = !1;
    }
    addEvent(elem, 'mousedown', mouseDownHandler);
    addEvent(elem, 'mousemove', mouseMoveHandler);
    addEvent(elem, 'mouseup', mouseUpHandler); 

    WheelEvent:从MouseEvent继承

    事件类型只有一种 wheel

    属性: 

    deltaMode:指定delta值的单位

    deltaX、deltaY、deltaZ:在X/Y/Z方向上的偏移量

    FocusEvent:元素获取或失去焦点的时候触发(比如点击输入框出现光标/点击页面其他地方取消输入框可输入状态)

    事件类型:

    blur:元素失去焦点时

    focus:元素获得焦点时

    focusin:元素即将获得焦点时(获得焦点之前)

    focusout:元素即将失去焦点时(失去焦点之前)

    属性:

    relatedTarget:当一个元素失去焦点时,另一个元素就会获得焦点,blur/focusout中获得焦点的元素就是relatedTarget

     

    当一个元素获得焦点时,另一个失去焦点的元素就是focus/focusin里面的relatedTarget

    InputEvent:处理输入事件

    事件类型:

    beforeinput:输入在页面上还不能看到时

    input:当输入框里的内容已经有输入内容时(继续输入时也会不断触发input事件)

    在IE低版本中没有input事件,使用onpropertychange

    KeyboardEvent:处理键盘事件

    事件类型:

    keydown:按下键

    keyup:松开键

    属性:

    key:字符串,按下的按键

    code:字符串,按键对应码

    ctrlKey/shiftKey/altKey/metaKey:标识是否被按下

    repeat:一个键持续按着

    以上为W3C,以下为常用非标准

    keyCode、charCode、which:用于获取按键对应的ASCII码,实际编程用这些ASCII码来判断

    Event 最基本的事件:

    事件类型:

    load:代表元素 (如window, image, iframe等依赖网络加载的元素) 加载完成

    unload:与load对应,代表元素退出时(被关闭)

    error:加载错误,比如讲img的src路径写错了

    select:比如input/textarea输入框被选择时

    abort:window/image等元素正在加载时,按下了esc

    按对象来解释这些事件:

    window:

    load:页面的所有请求都完成了,所有需要加载的元素都加载了的时候

    unload:当关闭当前页面时

    error:浏览器加载当前页面异常

    abort:退出时

     

    image:

    load:图片按照src地址通过网络连接加载完成时

    error:图片加载异常

    abort:图片加载时,按下了esc等中断图片的加载

    <!-- 通常会这么写 -->
    <image alt="photo" src=".../photo.jpg" onerror="this.src='...default.jpg'"/>

    UIEvent:

    事件类型:

    resize:修改浏览器或iframe窗体大小时,

    scroll:页面发生滚动时触发。若滚动是在一个元素上触发的,则会冒泡;如果是系统(document)滚动条,则不会冒泡

     

    事件代理

    场景:一个ul中有很多li,要对这些li都注册一个click事件,需要一个一个注册吗?

    -- 直接将click事件注册到ul上即可,因为click事件支持冒泡:li上的事件最终肯定会冒泡到ul上,只需在ul上处理所有li的事件即可

    -- 事件代理:将事件注册到元素的父节点上

    优点:1. 不用注册那么多的事件;2. 内存分配少

    缺点:如果把事件全放入父元素,事件管理起来会很复杂(比如将所有事件都注册到window对象上,事件处理函数会很复杂)

    (数据通信、数据存储、动画、音频与视频、canvas、BOM、表单操作、列表操作见基础篇(下))

     

  • 相关阅读:
    Java学习笔记二:发展史之Java的发展
    Java学习笔记一:发展史之Java诞生前
    JPA学习笔记四:理论篇之CRUD
    JPA学习笔记二:实践篇之SpringBoot集成JPA
    JPA学习笔记一:理论篇之JPA介绍
    JPA学习笔记三:实践篇之实现正向工程
    MySQL:大表优化
    如何理解原型链中的prototype和__proto__?
    Node版本的升级和降级
    常用的git提交代码命令
  • 原文地址:https://www.cnblogs.com/FudgeBear/p/7464839.html
Copyright © 2020-2023  润新知