day20-21让你和页面对话

学习时间:1小时复习,2小时学新的。

让你和页面对话


事件知识

W3School 事件

  • onload 和 onunload 事件(进入和离开页面)
  • onchange 事件(与输入字段验证结合使用)
  • onmouseover 和 onmouseout 事件(当用户将鼠标移至 HTML 元素上或移出时触发某个函数)
  • onmousedown, onmouseup 以及 onclick 事件(点击)
    以上几个事件的详细解释见 这里

MDN 事件

《day17-18进一步学习JS第二部分》已看。略。

HTML DOM Text 对象

https://www.w3school.com.cn/jsref/dom_obj_text.asp

HTML DOM Button 对象

https://www.w3school.com.cn/jsref/dom_obj_pushbutton.asp


用 JavaScript 操作 DOM 样式

JavaScript HTML DOM - 改变 CSS

语法:

document.getElementById(id).style.property = new style

实例:

document.getElementById("p2").style.color = "blue";

HTML DOM Style 对象

在这里查找属性

javascript 动态修改css样式方法汇总(四种方法)

前置代码:<input id="btnB" type="button" name="btnLogin" value="登录" class="style1" />
一、obj.style.backgroundColor是内联修改。内联修改的优先级高于外联修改,可能会造成不想要的结果。比如说,外联css里有个hover改变背景颜色,新建的内联背景样色让hover失效。

function changeStyle1() {
  var obj = document.getElementById("btnB");
  obj.style.backgroundColor= "black";
 
}

二、obj.style.cssText也是内联修改,效果和“一”一样。缺陷一样,只是写的代码不同。

function changeStyle2() {
   var obj = document.getElementById("btnB");
   obj.style.cssText = " display:block;color:White";
 
}

三、.setAttribute()这个是修改外联内容,不会造成上述缺陷。有两个变量,要把第一个变量改成第二个变量。

function changeStyle3() {
  var obj = document.getElementById("btnB");
  //obj.className = "style2";
  obj.setAttribute("class", "style2");
}
  • 修改类名还有另一种方法:obj.className = "style2";

四、setAttribute()的另一种用用法。即直接把href的引用改了,实现换肤。(注意,这里obj变量的具体引用和“三”不同,)

<link href="css1.css" rel="stylesheet" type="text/css" id="css"/>
function changeStyle4() {
   var obj = document.getElementById("css");
   obj.setAttribute("href","css2.css");
 }

综上

用这个不容易出问题:.setAttribute("class", "style2");

cssText的用法以及特点

  • 即上面的“二”
  • cssText 的本质就是设置 HTML 元素的 style 属性值。

cssText的使用优势:
对比“一”,减少浏览器开销。一行完成“一”中需要写多行的任务。
(“一”这种通过JS来覆写对象的样式是比较典型的一种销毁原样式并重建的过程,这种销毁和重建,都会增加浏览器的开销。)
例:

var element= document.getElementById(“id”);
element.style.width=20px”;
element.style.height=20px”;
element.style.border=”solid 1px red”;
element.style.cssText=”width:20px;height:20px;border:solid 1px red;;

但是,这样会有一个问题,会把原有的cssText清掉,比如原来的style中有’display:none;’,那么执行完上面的JS后,display就被删掉了。
为了解决这个问题,可以采用cssText累加的方法:

Element.style.cssText += ‘width:100px;height:100px;top:100px;left:100px;
  • 上面cssText累加的方法在IE中是无效的。(在某些浏览器中(比如 Chrome),你给他赋什么值,它就返回什么值。在 IE 中则比较痛苦,它会格式化输出、会把属性大写、会改变属性顺序、会去掉最后一个分号。)

可以在前面添加一个分号来解决这个问题(这样在IE中就有效啦!):

Element.style.cssText +=;width:100px;height:100px;top:100px;left:100px;
  • 如果前面有样式表文件写着 div { text-decoration:underline; },这个会被覆盖吗?不会!因为它不是直接作用于 HTML 元素的 style 属性。(这个问题在上面的四种方法中的“一”和“二”提到啦!)

综上

用这个方便:Element.style.cssText += ‘width:100px;height:100px;top:100px;left:100px;’

JavaScript之ClassName属性学习

  • 在前面的style属性学习中,知道了通过style属性可以控制元素的样式,从而实现了行为层通过DOM的style属性去干预变现层显示的目地,但是这种就是不好的,而且为了实现通过DOM脚本设置的样式,你就不得不花时间去研究JavaScript函数,去寻找对应修改和设置样式的有关语句。而且每添加或修改js脚本的代码量远大于我们修改css样式的代码量。所以与其使用DOM直接改变莫个元素的样式,不如通过JavaScript代码去更新这个元素的class属性。(对照上面的方法“四”)

下面通过代码来比较这两种方式的差别:

function setStyleHeaderSiblings() {
    if (!checkCompatibility()) return; //check compatibility
    var heads = document.getElementsByTagName("h1");
    var ele;  //defines a element for receive;
    for (var i = 0; i < heads.length; i++) {
        ele = getNextElement(heads[i].nextSibling);
        ele.style.fontWeight = "bold";
        ele.style.fontSize = "1.2em";
    }
}
function setStyleHeaderSiblings() {
    if (!checkCompatibility()) return; //check compatibility
    var heads = document.getElementsByTagName("h1");
    var ele;  //defines a element for receive;
    for (var i = 0; i < heads.length; i++) {
        ele = getNextElement(heads[i].nextSibling);
        ele.className="change";
    }
css样式表
.change{
font-weight:bold;
font-size:1.2em;
}
  • 假设我们这个需要给这个效果加上一个background-color:blue;上面这种做法需要在js里面添加代码:ele.style.backgroundColor="blue";
  • 而第二种做法则只要在样式表里加一个样式就可以实现,而且实现了样式与行为的分离,还减少了我们的代码量,所以在后期需求不确定的情况下,建议第二种方法来动态的修改元素的样式。
  • 但是通过className设置元素的样式也有一个缺陷,那就是通过className设置元素的class属性时将替换(而不是追加)该元素原有的class属性,在实际开发中往往很多时候我们需要追加class;

所以根据这个需要我们可以自定义一个方法来实现追加className的效果(即为元素追加类名的效果)代码如下:

function addClass(element,value) {
    if (!element.className) //检查className属性值是否为null{
        element.className = value;
    } else {
        newClassName = element.className;//这个变量不用声明吗?我觉得应该声明。
        newClassName += " "; //这句代码追加的类名分开
        newClassName += value;
        element.className = newClassName;
        //即一个元素有多个类名。
    }
}
  • !表示逻辑非
  • 类名要加引号哦。

再次理解 JavaScript 的事件机制,并了解事件代理

day17-18进一步学习JS第二部分中提到:JS中的事件机制不同于一些其他语言。在此进一步介绍。

初步理解JS的事件机制
一、事件流(捕获,冒泡)

为了兼容更多的浏览器,非特殊情况一般我们都是把事件添加到在事件冒泡阶段。

二、事件处理程序

DOM0级事件处理程序

1 var btn5 = document.getElementById('btn5');
2 btn5.onclick=function(){
3    console.log(this.id);//btn5   
4 };

基于DOM0的事件,对于同一个dom节点而言,只能注册一个,后边注册的同种事件会覆盖之前注册的。利用这个原理我们可以解除事件,btn5.onclick=null;
其中this就是绑定事件的那个元素;
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理;

DOM2级事件处理程序

DOM2支持同一dom元素注册多个同种事件,事件发生的顺序按照添加的顺序依次触发(IE是相反的)。
DOM2事件通过addEventListener和removeEventListener管理
两个方法都一样接收三个参数,第一个是要处理的事件名,第二个是事件处理程序,第三个值为false时表示在事件冒泡阶段调用事件处理程序,一般建议在冒泡阶段使用,特殊情况才在捕获阶段;
注意:通过addEventListener()添加的事件处理程序只能用removeEventListener()来移除,并且移除时传入的参数必须与添加时传入的参数一样。
例:

1 var btn2 = document.getElementById('btn2');
2 var handlers = function () {
3    console.log(this.id);
4 };
5 
6 btn2.addEventListener('click',handlers,false);
7 
8 btn2.removeEventListener('click',handlers.false);

IE事件处理程序
IE用了attachEvent(),detachEvent(),接收两个参数,事件名称和事件处理程序,通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段,所以平时为了兼容更多的浏览器最好将事件添加到事件冒泡阶段,IE8及以前只支持事件冒泡;

1 var btn3 = document.getElementById('btn3');
2 var handlers2=function(){
3    console.log(this===window);//true,注意attachEvent()添加的事件处理程序运行在全局作用域中;
4 };
5 btn3.attachEvent('onclick',handlers2);

跨浏览器事件处理程序

我没看懂:https://www.cnblogs.com/lazychen/p/5664788.html

  • 在同一个对象上注册事件,并不一定按照注册顺序执行,冒泡或捕获模式会影响其被触发的顺序;
三、事件对象

我想先不管IE。

四、事件委托
  • 因为把事件绑定到了父节点上,因此省了绑定事件。就算后面新增的子节点也有了相关事件,删除部分子节点不用去销毁对应节点上绑定的事件
  • 父节点是通过event.target来找对应的子节点的。(事件处理程序中的this值始终等于currentTarget的值,指向的是绑定到的那个元素)
js中的事件委托或是事件代理详解(https://www.cnblogs.com/liugang-vip/p/5616484.html)

为什么要用事件委托和原理:https://www.cnblogs.com/liugang-vip/p/5616484.html
简而言之:利用冒泡原理优化性能。

实现方法:利用事件对象(target属性始终是事件刚刚发生的元素的引用。)

  • Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,

我简化的版本:

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    if(ev.target.nodeName.toLowerCase() == 'li'){
         alert(123);
         alert(ev.target.innerHTML);
    }
  }
}

原文中版本(考虑了IE浏览器):

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
         alert(123);
         alert(target.innerHTML);
    }
  }
}

上面的例子是说li操作的是同样的效果,要是每个li被点击的效果都不一样,那么用事件委托还有用吗?

再写一层if就好了!

<div id="box">
        <input type="button" id="add" value="添加" />
        <input type="button" id="remove" value="删除" />
        <input type="button" id="move" value="移动" />
        <input type="button" id="select" value="选择" />
    </div>
window.onload = function(){
            var oBox = document.getElementById("box");
            oBox.onclick = function (ev) {
                if(ev.target.nodeName.toLocaleLowerCase() == 'input'){
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'remove' :
                            alert('删除');
                            break;
                        case 'move' :
                            alert('移动');
                            break;
                        case 'select' :
                            alert('选择');
                            break;
                    }
                }
            }
        }

现在讲的都是document加载完成的现有dom节点下的操作,那么如果是新增的节点,新增的节点会有事件吗?也就是说,一个新员工来了,他能收到快递吗?

插入一条tip:

  • 事件对象属性e.target和this的区别:
  • this返回的是绑定事件的对象(元素)
  • e.target返回的是触发事件的对象(元素)
    事件对象可以利用冒泡原理优化性能,而this不行。

这是一般的做法,但是你会发现,新增的li是没有事件的,说明添加子节点的时候,事件没有一起添加进去,这不是我们想要的结果,那怎么做呢?一般的解决方案会是这样,将for循环用一个函数包起来,命名为mHover,如下:

window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            
            function mHover () {
                //鼠标移入变红,移出变白
                for(var i=0; i<aLi.length;i++){
                    aLi[i].onmouseover = function(){
                        this.style.background = 'red';
                    };
                    aLi[i].onmouseout = function(){
                        this.style.background = '#fff';
                    }
                }
            }
            mHover ();
            //添加新节点
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
                mHover ();
            };
        }

虽然功能实现了,看着还挺好,但实际上无疑是又增加了一个dom操作,在优化性能方面是不可取的,那么用事件委托的方式,能做到优化吗?

window.onload = function(){
            var oBtn = document.getElementById("btn");
            var oUl = document.getElementById("ul1");
            var aLi = oUl.getElementsByTagName('li');
            var num = 4;
            //事件委托,添加的子元素也有事件
            oUl.onmouseover = function(ev){
                if(ev.target.nodeName.toLowerCase() == 'li'){
                    ev.target.style.background = "red";
                }
            };
            oUl.onmouseout = function(ev){
                if(ev.target.nodeName.toLowerCase() == 'li'){
                    ev.target.style.background = "#fff";
                }
            };
            //添加新节点
            oBtn.onclick = function(){
                num++;
                var oLi = document.createElement('li');
                oLi.innerHTML = 111*num;
                oUl.appendChild(oLi);
            };
        }

看,上面是用事件委托的方式,新添加的子元素是带有事件效果的,我们可以发现,当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。

特例:
现在给一个场景 ul > li > div > p,div占满li,p占满div,还是给ul绑定事件,需要判断点击的是不是li(假设li里面的结构是不固定的),那么e.target就可能是p,也有可能是div,这种情况你会怎么处理呢?
加一个while即可。

  var oUl = document.getElementById('test');
    oUl.addEventListener('click',function(ev){
        var target = ev.target;
        while(target !== oUl ){
            if(target.tagName.toLowerCase() == 'li'){
                console.log('li click~');
                break;
            }
            target = target.parentNode;
        }
    })
  • 那什么样的事件可以用事件委托,什么样的事件不可以用呢?

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

关于JS事件冒泡与JS事件代理(事件委托)

一、阻止冒泡(冒泡导致本不想触发的父元素的点击事件被触发)

    document.getElementById('parent').οnclick=function () {
        console.log(this.getAttribute('data-id'));
    };
    document.getElementById('child').οnclick=function (ev) {
        var e = ev||window.event;//<span style="color:#FF0000;">IE中event可以通过window.event随时取到,而其他浏览器需要通过参数传递</span>
        console.log(this.getAttribute('data-id'));
        stopPropagation(e);
    };
    function stopPropagation(e) {
        if (e.stopPropagation) {
            e.stopPropagation();
        } else {
            e.cancelBubble = true;
        }

二、事件委托


var ul = document.getElementById('parentUl');
    ul.οnclick=function (event) {
      var e = event||window.event,
              source = e.target || e.srcElement;//target表示在事件冒泡中触发事件的源元素,
              //在IE中是srcElement
        if(source.nodeName.toLowerCase() == "li"){   //判断只有li触发的才会输出内容
            alert(source.innerHTML);
        }
        stopPropagation(e);                           //阻止继续冒泡
    };
    function addElement() {
        var li = document.createElement('li');
        li.innerHTML="我是新孩子";
        ul.appendChild(li);
}

完成事件委托练习,tip

  • id属性不在style里,直接.id即可。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值