Javascript常用API及事件原理相关笔记(一)

1.定义Date日期对象的四种方式及对日期对象进行操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Date日期对象的定义方式</title>
</head>
<body>
<script>
    //默认当前时间的定义 方式
    var date1 = new Date();
    console.log(date1);

    //指定某个时间的定义 方式
    var date2 = new Date("2019/1/31 15:48:20");//兼容最强
    console.log(date2);

    //直接指定字面量  但是很多浏览器不兼容 因为每个浏览器渲染的Date对象的字面量可能不一样
    var date3 = new Date("Thu Jan 31 2019 15:48:20 GMT+0800 (中国标准时间)");
    console.log(date3);

    //指定年月日的方式 来定义
    var date4 = new Date(2019, 1, 31);
    console.log(date4);
    //操作日期对象
    console.log(date1.getDate());        //     获取日 1-31
    console.log(date1.getDay());         //     获取星期 0-6(0代表周日)
    console.log(date1.getMonth());       //   获取月 0-11(1月从0开始)
    console.log(date1.getFullYear());        //    获取完整年份(浏览器都支持)
    console.log(date1.getHours());       //获取小时 0-23
    console.log(date1.getMinutes());         //获取分钟 0-59
    console.log(date1.getSeconds());         //获取秒  0-59
    console.log(date1.getMilliseconds());        // 获取毫秒 (1s = 1000ms)
    console.log(date1.getTime());        //返回累计毫秒数(从1970/1/1午夜)

</script>
</body>
</html>


2.为什么时间都是从1970/1/1午夜开始的,因为那是一个纪念的日子,有一个实验室,c语言和unix系统都是从那个实验室出来的,unix是收费的,linux是开源的,诺贝尔的获得者有七八个都是这个实验室的,后来他们把这个1970/1/1午夜被制定成了一个计算机时间的基本点,后面的人一直沿用至今。

◆获取当前距离时间基本点的毫秒数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>返回当前距离时间基本点的毫秒数</title>
</head>
<body>
<script>

    //第一种
    var date = Date.now();
    console.log(date);

    //第二种
    var date = +new Date();
    console.log(date);

    //第三种
    var date = new Date().getTime();
    console.log(date);

    //第四种
    var date = new Date().valueOf();
    console.log(date);

</script>
</body>
</html>


3.获取代码执行的时间

◆方式一:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>获取代码执行的毫秒数</title>
</head>
<body>
<script>
    //获取代码开始执行之前的时间的毫秒数
    var start=Date.now();

//    //开始进行1加到100000之间的运算
//    var sum=0;
//    for(var i=1;i<=10000;i++){
//        sum+=i;
//    }
   var sum=fn(10000);//数字过大会栈溢出

    //获取代码结束执行之后的时间的毫秒数
    var end=Date.now();
    console.log(sum);
    console.log(end-start);


    //递归函数
    function fn(num){
        if(num<=1){
            return 1;
        }
        return num+fn(num-1);
    }

</script>
</body>
</html>
方式二:使用console.time和console.timeEnd。
console.time("test");
for(var i=0;i<10000;i++){
     i+1;
     i-1;
     i*1;
     i/1;
     i%1;
}
console.timeEnd("test");


4.简单日历与倒计时

◆简单日历

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>操作Date对象制作简单日历</title>
    <style type="text/css">
        div {
            width: 800px;
            margin: 300px auto;
            font: 600 30px/30px "simsun";
            color: #f00;
            text-align: center;
        }
    </style>
</head>
<body>
<div id="time">
    今天是 2020年1月31日星期?
</div>
<script>
    //需求:动态展示当前的日期 包括 年 月 日 星期
    //思路:获取Date对象的 完整年Fullyear Month  Date Day 改变div的innerHTML属性即可
    //步骤:
    //1.获取Date对象
    //2.获取Date对象的 完整年Fullyear Month  Date Day
    //3.改变div的innerHTML属性

    //1.获取Date对象
    var date=new Date();

    //2.获取Date对象的 完整年Fullyear Month  Date Day
    var year=date.getFullYear();//完整年
    var month=date.getMonth()+1;//月份 0-11
    var day=date.getDate();//日子 1-31
    var week=date.getDay();//星期 0-6 0表示星期天

    //2.1 定义一个中文星期汉字的的数组
    var weeks=["星期天","星期一","星期二","星期三","星期四","星期五","星期六"];

    //3.改变div的innerHTML属性
    var time=document.getElementById("time");
    time.innerHTML="今天是 "+year+"年"+month+"月"+day+"日"+weeks[week];
</script>
</body>
</html>

◆倒计时

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>操作Date对象制作倒计时</title>
    <style type="text/css">
        div {
            width: 1210px;
            margin: 300px auto;
            font: 600 30px/30px "simsun";
            color: #f00;
            text-align: center;
        }
    </style>
</head>
<body>
<div id="time">
    距离2020年2月28日凌晨1点还剩:多少天多少小时多少分多少秒多少毫秒
</div>
<script>
    //需求:页面动态进行距离2020年2月28日凌晨1点的时间
    //思路:定义一个当前时间的对象和一个未来时间的对象
    //      获取两个对象的毫秒值 进行相减  获取剩下的毫秒值
    //      然后计算出
    //      天数 总的毫秒数/ (24 * 60 * 60 * 1000) 获取天数
    //        注意:(由于js中相除可能会带有小数,所以记得向下取整)
    //      小时数 总的毫秒数/ ( 60 * 60 * 1000) % 24 获取不足以一天的小时数
    //      分数 总的毫秒数/ (60 * 1000) % 60 获取不足以一个小时的分钟数
    //      秒数 总的毫秒数/ 1000 % 60 获取不足以一分钟的秒数
    //      毫秒数 总的毫秒数 % 60 获取不足以一秒钟的 毫秒数
    //步骤:
    //暂定


    setInterval(fn, 100);


    function fn() {
        var datenow = +new Date();
        var datenew = +new Date("2020/2/28 01:00:00");

//    console.log(datenow);
//    console.log(datenew);

        //相差的毫秒数
        var result = datenew - datenow;

        //定义一个计算的对象
//        var calc={day:24*60*60*1000,hours:60*60*1000,minutes:60*1000,seconds:1000,milliSeconds:1};


//        //获取距离的天数
//        var day=Math.floor(result/calc.day);
        var day = Math.floor(result / (24 * 60 * 60 * 1000));
//
//        //获取距离的小时数
//        var hours=Math.floor((result-day*calc.day)/calc.hours);
        var hours = Math.floor(result / ( 60 * 60 * 1000) % 24);

//
//        //获取距离的分钟数
//        var minutes=Math.floor((result-day*calc.day-hours*calc.hours)/calc.minutes);
        var minutes = Math.floor(result / (60 * 1000) % 60);
//
//        //获取距离的秒数
//        var seconds=Math.floor((result-day*calc.day-hours*calc.hours-minutes*calc.minutes)/calc.seconds);
        var seconds = Math.floor(result / 1000 % 60);
//
//        //获取距离的毫秒数
//        var milliSeconds=Math.floor((result-day*calc.day-hours*calc.hours-minutes*calc.minutes-seconds*calc.seconds)/calc.milliSeconds);
        var milliSeconds = Math.floor(result % 1000);

//        var content=["距离2018年2月28日凌晨1点还剩:","$天","$小时","$分","$秒","$毫秒"];
//        content[1]=content[1].replace("$",day);
//        content[2]=content[2].replace("$",hours);
//        content[3]=content[3].replace("$",minutes);
//        content[4]=content[4].replace("$",seconds);
//        content[5]=content[5].replace("$",milliSeconds);

        //优化一下, 因为会抖动 天 小时 分钟 秒都要在小于10的时候补0 毫秒要在小于10的时候补双0 小于100补0
        day = day < 10 ? "0" + day : day;
        hours = hours < 10 ? "0" + hours : hours;
        minutes = minutes < 10 ? "0" + minutes : minutes;
        seconds = seconds < 10 ? "0" + seconds : seconds;
        milliSeconds = milliSeconds < 100 ?
                (milliSeconds < 10 ? "00" + milliSeconds : "0" + milliSeconds) : milliSeconds;


        time.innerHTML =
                "距离2020年2月28日凌晨1点还剩:" + day + "天" + hours + "小时" +
                minutes + "分" + seconds + "秒" + milliSeconds + "毫秒";
    }

</script>
</body>
</html>


5.字符串的实质

◆简单数据类型无法绑定属性和方法,字符串之所以能够使用length属性和indexOf方法,完全是因为,当你使用字符串.属性或者方法的时候,底层会进行隐式转换复杂类型,将简单的字符串转换成了String对象了,所以才能够使用length属性和indexOf方法,用完之后会再转换回来。基本包装类型 String/Number/Boolean对应了string/number/boolean,所以当这些简单数据类型调用方法或者属性时,底层都会去转换为复杂数据类型的对象实例来操作,操作完毕之后会再转换回去,相当于底层做了包装一样。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>String对象</title>

</head>
<body>

<script>
    //简单数据类型无法绑定属性和方法
    var str="";
    str.aaa="20";
    str.bbb= function () {
        console.log("123");
    }

    //但是复杂类型可以
    var obj=new Object();
    obj.aaa=20;
    obj.bbb= function () {
        console.log("123");
    }

    //所以按逻辑上来说 简单数据类型应该不可能有属性和方法
    console.log(str.length);
    str.indexOf("");
    //但是综上所述,简单数据类型 字符串居然有属性和方法
    //结论是:底层肯定做了数据转换  将简单的数据类型字符串 转换成了String对象
    //应该是在使用这个属性或者方法的时候进行了隐式的转换,使用完毕后 又再转换回来了。


</script>

</body>
</html>

◆真正的String对象的实例内部其实是这样的

//创建String对象实例
var strobj=new String("abcdefg");
//打印该对象实例
console.log(strobj);//{0: "a",1: "b",2: "c",3: "d",4: "e",5: "f",6: "g", length: 7,[[PrimitiveValue]]: "abcdefg"}
//每一个字符串都有对应的数字作为key,所以能够使用[]的方式来访问字符串中的每个字符。


6.字符串的API

◆charAt:根据字符串位置的索引返回指定字符

◆charCodeAt:根据字符串位置的索引返回指定字符的十进制的unicode编码的数值。

    var str="abcdefg";
    console.log(str);//abcdefg

    //使用[]方式来遍历
    for(var i=0;i<str.length;i++){
        console.log(str[i]);
    }

    //使用charAt方法来遍历  根据索引找到字符串 也是根据String对象中数字的key来找到字符
    for(var i=0;i<str.length;i++){
        console.log(str.charAt(i));
    }

    //使用charCodeA方法来遍历  也是根据索引找到字符串
    // 然后再进行十进制unicode码转换,最终变成了变成了数字 48代表1 65代表A 97代表a
    for(var i=0;i<str.length;i++){
        console.log(str.charCodeAt(i));
    }
◆indexOf:根据字符串的字符返回索引值, 从前往后搜索。

◆lastIndexOf:根据字符串的字符返回索引值,从后往前搜索

<script>
    var str="abcdefga";
    //一个是从前面开始 通过字符找索引值
    console.log(str.indexOf("a"));//0
    //一个是从后面开始 通过字符找索引值
    console.log(str.lastIndexOf("a"));//7
</script>
◆encodeURIComponent:对url地址进行uri编码,他是window对象的方法,方法参数是字符串。

◆decodeURICompoent:对uri编码后的url进行解码,他是window对象的方法,方法参数是字符串。

<script>
    var url="http://www.baidu.com?username=_user&password=_-.pwd";
    console.log(url);//http://www.baidu.com?username=_user&password=_-.pwd
    //没什么用
    console.log(encodeURI(url));//http://www.baidu.com?username=_user&password=_-.pwd
    //进行的url编码
    console.log(encodeURIComponent(url));//http%3A%2F%2Fwww.baidu.com%3Fusername%3D_user%26password%3D_-.pwd
    //貌似在url编码后又进行了编码
    console.log(encodeURI(encodeURIComponent(url)));//http%253A%252F%252Fwww.baidu.com%253Fusername%253D_user%2526password%253D_-.pwd

    //对url编码后的url进行了解码
    console.log(decodeURIComponent(encodeURIComponent(url)));//http://www.baidu.com?username=_user&password=_-.pwd

</script>

◆concat:连接两个字符串,+的底层用的就是这个方法
◆slice:截取划分,和数组的silce方法类似,也是指定起始索引和结束索引,来截取原字符串返回新字符串,也是截取时包括左边的索引不包括右边的索引,索引为负数则为字符串的长度加上这个负数索引,如果起始索引大于结束索引则会返回空字符串,如果只有一个起始索引则会截取到最后。
◆substr:截取,和数组的splice方法类似,只不过这个方法只有截取没有替换,指定起始索引和要截取的长度来截取字符串,长度过大就直接等于字符串的长度,长度为负数则为0,索引为负数则字符串的长度加上这个负数索引。

◆substring:截取划分,和字符串的slice方法类似,只不过不同的地方是,索引为负数,那么直接返回全部字符串,如果起始索引大于结束索引,就会智能替换,把小的索引作为起始索引,大的索引作为结束索引。

<script>
    var str1="123";
    var str2="456";
    //字符串连接  和 + 差不多 可能加号底层就是用的这个
    var str3=str1.concat(str2);
    console.log(str1);
    console.log(str2);
    console.log(str3);

    var strs1="i love you my 中国";
    //截取划分
    // 起始索引与结束索引
    // 包左不包右
    // 如果只有一个起始索引值
    // 则默认结束索引值就是数组的长度
    // 如果起始索引大于结束索引
    // 则直接返回空字符串
    // 如果索引值为负数
    // 则为字符串的长度加上这个负数索引
    console.log(strs1.slice(2,4));


    var strs2="i love you my 中国";
    //截取 从起始索引开始
    // 如果只有一个起始索引值
    // 那么截取的长度就是数组剩下的长度
    // 指定长度的字符串
    // 长度超过数组的长度则为数组长度
    // 长度为负数则为0
    // 索引为负数 则为 数组的长度加上这个负数索引
    console.log(strs2.substr(2,5));

    var strs3="i love you my 中国";
    //截取
    //起始索引与结束索引
    // 包左不包右
    // 如果只有一个起始索引值
    // 则默认结束索引值就是数组的长度
    // 如果索引为负数
    // 截取全部
    // 如果起始索引大于结束索引
    // 内部会今后前后索引互换
    console.log(strs3.substring(2,5));
    console.log(strs3.substring(5,2));
    console.log(strs3.substring(-1));


</script>

◆trim:去掉字符串开头部分与结束部分的空格(IE678不支持,但是可以自己封装)。

◆replace:替换原字符串返回新字符串,第一个参数是原字符串中的某一段字符串,第二个参数是替换掉原字符串中的某一段字符串的字符串,使用str.replace(/\s/gi,"")可以替换掉所有的空格,这种方式叫做正则表达式。

<script>

    var str="  a  b  c  d   ";
    console.log(str.trim());//a  b  c  d(去掉了前后空格)但是IE678中不支持

    console.log(str.replace("a","1"));//  1  b  c  d  (把a替换成了1)

    console.log(str.replace(/\s/gi,""));//abcd(使用正则表达式替换掉了所有的空格)

</script>

◆join与split方法,数组通过join方法变成字符串,字符串通过split方法变成数组

<script>
    //数组
    var arr=["吕布","赵云","关羽","张飞","刘备"];
    console.log(arr);//["吕布", "赵云", "关羽", "张飞", "刘备"]
    console.log(arr.join("|"));//吕布|赵云|关羽|张飞|刘备

    //字符串
    var str="吕布|赵云|关羽|张飞|刘备";
    console.log(str);//吕布|赵云|关羽|张飞|刘备

    //如果split方法中的参数为空字符串,那么就会对字符串中每一个字符进行分割然后放入数组中
    console.log(str.split("|"));//["吕布", "赵云", "关羽", "张飞", "刘备"]

</script>

◆str.toLowerCase();//将str变量中的字母无论大小写都变成小写的字母,这个方法会返回新的字符串。

◆str.toUpperCase();//将str变量中的字母无论大小写都变成大写的字母,这个方法会返回新的字符串。

<script>
    var str=" a C d F g N";
    console.log(str.toLowerCase());//全小写
    console.log(str.toUpperCase());//全大写

</script>

◆anchor()方法:    "我是一个锚点".anchor("mao");等于<a name="mao">我是一个锚点</a>

◆link()方法:   "我是一个超链接".link("http://www.baidu.com");等<a href="http://www.baidu.com">我是一个超链接</a>(记住这个)

<script>
    var str="你好";
    console.log(str.anchor());//<a name="undefined">你好</a>
    console.log(str.big());// <big>你好</big>
    console.log(str.sub());// <sub>你好</sub>
    console.log(str.sup());// <sup>你好</sup>
    console.log(str.link());//<a href="undefined">你好</a>
    console.log(str.bold());//<b>你好</b>

</script>


7.计算字符位长度和查找字符串符合要求的字符的索引位置

◆计算字符位长度

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>获取一条字符串的所占的字符位长度</title>
</head>
<body>
<script>
    //需求:传递一个字符串 计算这条字符串所占的字符位长度。
    var str = "i love you my 祖国!!!!!!!!!!!";

    console.log(str+"所占的字符位为:"+getCharlength(str));

    function getCharlength(str) {
        var count = 0;
        for (var i = 0; i < str.length; i++) {
            //占一个字符位的字符 unicode码在0-127
            // 其它的占两个及两个以上的字符位的字符 unicode码不在0-127之间
            if (str.charCodeAt(i) < 128 && str.charCodeAt(i) >= 0) {
                count++;
            } else {
                count += 2;
            }
        }
        return count;
    }

</script>
</body>
</html>

◆查找字符串符合要求的字符的索引位置

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>查找字符串中所有符合要求的字符的索引位置</title>
</head>
<body>
<script>
    //简单版
    // 需求:只找单个字符 a 在字符串中的所有索引的位置
    // 思路:

    var str = "acbjkdsjaflkdjakfgds;akgjlskdk vncjkxnafnkjd fn";
    var arr = [];
    //遍历字符串
    for (var i = 0; i < str.length; i++) {
        if (str.charAt(i) == "a") {
            //把符合要求的索引添加进去
            arr.push(i);
        }
    }
    console.log(arr);

    //简单加点难度版
    // 需求:只要单个词 祖国 在字符串中的所有索引的位置
//
    var str2 = "我爱我的祖国我爱我的祖国我爱我的祖国我爱我的祖国我爱我的祖国我爱我的祖国我爱我的祖国";
    var arr2 = [];
    var index = 0;//每次获取到索引值
    var flag = 0;//一个计数的变量   每次原字符串时的起始索引值
    var chars = "祖国";
    while ((index = str2.indexOf(chars)) != (-1)) {
        //将索引添加到数组中
        arr2.push(index + flag);
        flag =flag+ index + chars.length;
        str2 = str2.slice(index + chars.length);
////        str2=st2.substr(index + chars.length);
////        str2=st2.substring(index + chars.length);
    }

    console.log(arr2);


</script>
</body>
</html>


8.用基础知识去封装简单的jquery

◆简单的选择器封装

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>简单封装JQquery</title>
    <style type=text/css>
        div {
            width: 100px;
            height: 100px;
            margin: 20px;
            background-color: #f09;
        }


    </style>
</head>
<body>
<script>
    //需求 通过传递参数 返回对象  #id .class tag普通标签 针对这三种封装一个返回元素对象的方法
    //思路:首先传递一个参数,先获取该字符串的选择器的标识
    //      #表示id选择器 .表示class选择器 普通标签标识普通选择器
    //      根据判断来返回相应的元素对象
    //                              如果是id 就使用 getElementById
    //                              如果是class 就使用 getElementsByClassName
    //                              如果是tag 就使用 getElementsByTagName
    //步骤:
    //1.先获取选择符号
    //2.对选择符进行判断
    //3.根据判断返回相应的对象
    function $(str) {
        //获取头部选择符
        var head = str.charAt(0);
        //去除id选择器与class选择器标识后的内容
        var body = str.slice(1);
        if (head === "#") { //id
            return document.getElementById(body);
        } else if (head === ".") {//class
            return document.getElementsByClassName(body);
        } else {//tag
            return document.getElementsByTagName(str);
        }

    }

</script>
<div></div>
<div class="box"></div>
<div id="box"></div>
<div class="box"></div>
<div></div>
<script>
    //需求:使用自己封装的JQuery来操作页面元素
    //      id=box的元素背景色变成红色
    //      class=box的元素背景色变成绿色
    //      div元素全部加上 15px的实线黑色边框

    $("#box").style.backgroundColor = "#f00";

    var boxs = $(".box");
    for (var i = 0; i < boxs.length; i++) {
        boxs[i].style.backgroundColor = "#0f0";
    }

    var divs = $("div");
    for (var i = 0; i < divs.length; i++) {
        divs[i].style.border = "15px solid #000";
    }
</script>
</body>
</html>
◆简单的dom操作封装
/**
 * 功能:根据 传递参数 返回相应的对象
 * 说明:参数为:#id选择器、.class选择器、tag普通选择器其中一种
 * @param str
 * @returns {*}
 */
function $(str) {
    //获取头部选择符
    var head = str.charAt(0);
    //去除id选择器与class选择器标识后的内容
    var body = str.slice(1);
    if (head === "#") { //id
        return document.getElementById(body);
    } else if (head === ".") {//class
        return document.getElementsByClassName(body);
    } else {//tag
        return document.getElementsByTagName(str);
    }
}


//******************获取子元素节点*********开始********//
/**
 * 功能:获取第一个子元素节点
 * @param ele
 * @returns {Element|*|Node}
 */
function getfirstchild(ele) {
    return ele.firstElementChild || ele.firstChild;
}

/**
 * 功能:获取最后一个子元素节点
 * @param ele
 * @returns {Element|*|Node}
 */
function getlastchild(ele) {
    return ele.lastElementChild || ele.lastChild;
}

/**
 * 功能:获取对应索引值的子元素节点
 * @param ele
 * @param index
 * @returns {*|HTMLElement}
 */
function getchildofindex(ele, index) {
    return ele.children[index];
}

/**
 * 功能:获取所有的子元素节点
 * @param ele
 * @returns {Array}
 */
function getallchild(ele) {
    var newArr = [];
    var oldArr = ele.children;
    for (var i = 0; i < oldArr.length; i++) {
        if (oldArr[i].nodeType === 1) {
            newArr.push(oldArr[i]);
        }
    }
    return newArr;
}
//******************获取子元素节点*********结束********//


//******************获取兄弟元素节点*********开始********//
/**
 * 功能:获取上一个兄弟元素节点
 * @param ele
 * @returns {Element|*|Node}
 */
function getpresibling(ele) {
    var pre = ele.previousElementSibling || ele.previousSibling;
    return pre;
}

/**
 * 功能:获取下一个兄弟元素节点
 * @param ele
 * @returns {Element|*|Node}
 */
function getnextsibling(ele) {
    return ele.nextElementSibling || ele.nextSibling;
}

/**
 * 功能:获取对应索引值的兄弟元素节点
 * @param ele
 * @param index
 * @returns {*|HTMLElement}
 */
function getsiblingofindex(ele, index) {
    return ele.parentNode.children[index];
}


/**
 * 功能:获取所有的兄弟元素节点(不包括自己)
 * @param ele
 * @returns {Array}
 */
function getallsibling(ele) {
    var newArr = [];
    var oldArr = ele.parentNode.children;
    for (var i = 0; i < oldArr.length; i++) {
        if (oldArr[i] != ele) {
            newArr.push(oldArr[i]);
            //newArr[newArr.length]=oldArr[i];
        }
    }
    return newArr;
}
//******************获取兄弟元素节点*********结束********//


9.数学对象API

◆Math.abs(num);num取绝对值
◆Math.pow(num1,num2);num1的num2次方
◆Math.power(num1,num2);num1的num2次幂
◆Math.sqrt(num);num开平方,相当于Math.power(num,2);
◆Math.sin(num);num的正弦

◆Math.cos(num);num的余弦


10.JS事件绑定与解绑的方式

◆事件绑定

◆事件解绑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件绑定和解绑的几种定义方式</title>
</head>
<button id="btn1">赋诗一首</button>
<button id="btn2">再来一首</button>
<button id="btn3">再来再来</button>
<body>

<script>
    //第一种方式 使用对象.属性的方式定义的,后面定义的事件会覆盖掉前面的
    btn1.onclick=function(){
        console.log("九尺龙泉万卷书,上天生我意何如。");
    }
    btn1.onclick=function(){
        console.log("不能报国平天下,枉为男儿大丈夫。");
    }
    //第二种方式 使用对象[属性名]的方式定义的,后面定义的事件会覆盖掉前面的
    btn2["onclick"]=function(){
        console.log("九尺龙泉万卷书,上天生我意何如。");
    }
    btn2["onclick"]=function(){
        console.log("不能报国平天下,枉为男儿大丈夫。");
    }

    //第三种方式 底层原理是不断保存每次事件的地址,递归执行方法 所以不会被覆盖掉
    //使用addEventListener更加有助于团队开发  因为前面两种方式都会采取最后面的覆盖掉前面的 但是IE678不支持
    btn3.addEventListener("click",fn1);//直接写click 底层会加on
    btn3.addEventListener("click",fn2);
    //使用attachEvent 只有IE678支持
//    btn3.attachEvent("onclick",fn1);//直接写onclick 底层不会再加on
//    btn3.attachEvent("onclick",fn2);

    //addEventListener与attachEvent区别在于,
    // 在前者中用this关键字代表是的事件源对象
    // 在后者中用this关键字代表的是window对象  内部机制不一样

    function fn1(){
        console.log("九尺龙泉万卷书,上天生我意何如。");
    }
    function fn2(){
        console.log("不能报国平天下,枉为男儿大丈夫。");
    }

</script>
<script>
    //针对第一种绑定的方式的解绑
    btn1.onclick=null;

    //针对第二种绑定的方式的解绑
    btn1["onclick"]=null;

    //针对第三种绑定方式的解绑
    btn3.removeEventListener("click",fn1);//解绑单击事件中的fn1
    btn3.removeEventListener("click",fn2);//解绑单击事件中的fn2
    //兼容IE678 因为只有IE678支持
//    btn3.detachEvent("click",fn1)////解绑单击事件中的fn1
//    btn3.detachEvent("click",fn2)////解绑单击事件中的fn2


</script>


</body>
</html>


11.事件的本质

◆事件的本质就是方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>核心-事件的本质就是方法</title>
</head>
<body>
<button id="btn">赋诗一首</button>
<script>

    addEvent("click",fn1,btn);
    addEvent("click",fn2,btn);
    addEvent("click",fn1,btn);
    addEvent("click",fn2,btn);

    //addEventListener的底层原理
    function addEvent(action,fn,element){
        //把上一个事件的方法指针存起来
        var tempEvent=element["on"+action];
        element["on"+action]=function(){
            //如果之前的事件已经绑定了方法
            if(tempEvent){
                //那么先执行之前的事件中的代码 再继续下面的
//                console.log(tempEvent);
                tempEvent();
            }
            //执行绑定的代码
            fn();
        }
    }

    //赋诗的方法
    function fn1(){
        console.log("九尺龙泉万卷书,上天生我意何如。");
    }
    function fn2(){
        console.log("不能报国平天下,枉为男儿大丈夫。");
    }


</script>
</body>
</html>

◆事件方法的添加与移除的本质

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>核心-事件方法的添加与移除的本质</title>
</head>
<body>
<button id="btn">赋诗一首</button>
<script>

    //定义一个事件对象
    var eventObj={};
    //添加事件
    function addEvent(action,fn,element){
       //判断当前事件对象中是否有这个行为的数组
        if(!eventObj[action]){
            //没有就创建
            eventObj[action]=[];
        }
        //有的话每次添加方法都是放入一个事件对象的这种行为数组中(栈中)
        eventObj[action].push(fn);
        element["on"+action]=function(){
            //遍历栈中的方法 按照顺序循环执行
            for(var i=0;i<eventObj[action].length;i++){
                //IE 678使用的是  attachEvent 原理就是这样的
//                eventObj[action][i]();

                //IE678之后用的是这个 addEventListener 原理就是这样的
                //这样就把方法中的this转换成了当前的对象
                element.actionMethod=eventObj[action][i];
                //由当前对象来调用
                element.actionMethod();
            }
        }
    }

    //移除事件
    function removeEvent(action,fn,element){
        //IE678以上
        //根据对象找索引 从后往前找,因为添加的时候是从后面添加的
//        var index= eventObj[action].lastIndexOf(fn);

        //直接切掉这个对象
//        eventObj[action].splice(index,1);

        //为了兼容IE678 因为indexOf和lastIndexOf方法不能用了

        for(var i=eventObj[action].length-1;i>=0;i--){
            //判断对象是否一致
            if(eventObj[action][i]===fn){
                eventObj[action].splice(i,1);
                return;
            }
        }
    }
</script>
<script>
    addEvent("click",fn1,btn);
    addEvent("click",fn2,btn);
    addEvent("click",fn1,btn);
    addEvent("click",fn2,btn);

    removeEvent("click",fn1,btn);

    btn.addEventListener("click",fn3);
    addEvent("click",fn3,btn);
    btn.addEventListener("click",fn1);
    btn.addEventListener("click",fn2);
    btn.addEventListener("click",fn3);
    console.log(btn.onclick);
    //赋诗的方法
    function fn1(){
        console.log("九尺龙泉万卷书,上天生我意何如。");
    }
    function fn2(){
        console.log("不能报国平天下,枉为男儿大丈夫。");
    }
    function fn3(){
        console.log("123")
    }
</script>
</body>
</html>

◆方法的定义方式有三种:
◇function fn(){};
◇var fn=function(){};
◇var fn=new Function("");
◆事件是属于一个对象自己的方法
◇对象.methoed=fn;
◇对象.methoed=function(){};
◇对象.methoed=new Function("");
◆addEventListener与attachEvent可以实现多个方法的绑定,实际原理是将前一个事件如(onclick)的地址存起来,然后重新给这个事件赋值,先调用前一个事件如(onclick)的地址执行的方法,然后再调用你之后绑定的方法
//addEventListener的底层原理
    function addEvent(action,fn,element){
        //把上一个事件的方法指针存起来
        var tempEvent=element["on"+action];
        element["on"+action]=function(){
            //如果之前的事件已经绑定了方法
            if(tempEvent){
                //那么先执行之前的事件中的代码 再继续下面的
//                console.log(tempEvent);
                tempEvent();
            }
            //执行绑定的代码
            fn();
        }
    }
◆addEventListener与attachEvent的原理远远不止上面的那么简单,其实它还进行了将每一个方法存到一个数组里面的操作,当每绑定一个方法,都会进行判断,是有已经有了这个方法,如果有了就不会再绑定重复的方法
//添加事件
    function addEvent(action,fn,element){
        //判断当前事件对象中是否有这个行为的数组
        if(!eventObj[action]){
            //没有就创建
            eventObj[action]=[];
        }
        //判断函数数组中是否有这个函数
        for(var i=0;i<eventObj[action].length;i++){
            if(eventObj[action][i]===fn){
                return;
            }
        }
//        //把当前的事件存到数组中 ,然后等到
//        if(element["on"+action]){
//
//        }
        //有的话每次添加方法都是放入一个事件对象的这种行为数组中(栈中)
        eventObj[action].push(fn);
        element["on"+action]=function(){
            //遍历栈中的方法 按照顺序循环执行
            for(var i=0;i<eventObj[action].length;i++){
                //IE 678使用的是  attachEvent 原理就是这样的
//                eventObj[action][i]();


                //IE678之后用的是这个 addEventListener 原理就是这样的
                //这样就把方法中的this转换成了当前的对象
                element.actionMethod=eventObj[action][i];
                //由当前对象来调用
                element.actionMethod();
            }
        }
    }
★将以上两种结合起来就是系统的addEventListener和attachEvent原理,因为第一种和第二种加起来就能够兼容btn1.onclick=function(){},把它给添加到事件队列中去。
◆removeEventListener与detachEvent的简单原理
   //移除事件
    function removeEvent(action,fn,element){
        //IE678以上
        //根据对象找索引 从后往前找,因为添加的时候是从后面添加的
//        var index= eventObj[action].lastIndexOf(fn);


        //直接切掉这个对象
//        eventObj[action].splice(index,1);


        //为了兼容IE678 因为indexOf和lastIndexOf方法不能用了
        for(var i=eventObj[action].length-1;i>=0;i--){
            //判断对象是否一致
            if(eventObj[action][i]===fn){
                eventObj[action].splice(i,1);
                return;
            }
        }
    }

★removeEventListener与detachEvent的原理不止上面那么简单,上面的是种实现的方式,但是不是最好的实现方式,因为我做的这种实现其实 和系统的实现无不干扰,其实系统的removeEventListener与detachEvent貌似还是另一套机制,当你使用对象.事件=null;的时候,无法去除你用addEventListener和attachEvent绑定的事件,就像你用removeEventListener与detachEvent也无法去除对象.事件=方法名;但是addEventListener和attachEvent绑定的事件可以获取到对象.事件=方法名;绑定的事件,并且还会吧这种方式绑定的事件存到自己的队列里面去,但是无法移除,只能添加。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模拟addEventListener</title>
</head>
<body>
<button id="btn">赋诗一首</button>
<script>

    //定义一个事件对象
    var eventObj={};

    //添加事件
    function addEvent(action,fn,element){
        //判断当前事件对象中是否有这个行为的数组
        if(!eventObj[action]){
            //没有就创建
            eventObj[action]=[];
        }

        //判断函数数组中是否有这个函数
        for(var i=0;i<eventObj[action].length;i++){
            if(eventObj[action][i]===fn){
                return;
            }
        }

//        //把当前的事件存到数组中 ,然后等到
//        if(element["on"+action]){
//
//        }

        //有的话每次添加方法都是放入一个事件对象的这种行为数组中(栈中)
        eventObj[action].push(fn);
        element["on"+action]=function(){
            //遍历栈中的方法 按照顺序循环执行
            for(var i=0;i<eventObj[action].length;i++){
                //IE 678使用的是  attachEvent 原理就是这样的
//                eventObj[action][i]();

                //IE678之后用的是这个 addEventListener 原理就是这样的
                //这样就把方法中的this转换成了当前的对象
                element.actionMethod=eventObj[action][i];
                //由当前对象来调用
                element.actionMethod();
            }
        }
    }

    //移除事件
    function removeEvent(action,fn,element){
        //IE678以上
        //根据对象找索引 从后往前找,因为添加的时候是从后面添加的
//        var index= eventObj[action].lastIndexOf(fn);

        //直接切掉这个对象
//        eventObj[action].splice(index,1);

        //为了兼容IE678 因为indexOf和lastIndexOf方法不能用了

        for(var i=eventObj[action].length-1;i>=0;i--){
            //判断对象是否一致
            if(eventObj[action][i]===fn){
                eventObj[action].splice(i,1);
                return;
            }
        }
    }

</script>
<script>
//    var t={};
//    t[fn1]="2";//把函数作为key
//    console.log(t[fn1]);
//    addEvent("click",fn1,btn);
//    addEvent("click",fn2,btn);
//    addEvent("click",fn1,btn);
//    addEvent("click",fn2,btn);

//    removeEvent("click",fn1,btn);
//
//    btn.addEventListener("click",fn3);
//    addEvent("click",fn3,btn);
    btn.addEventListener("click",fn1);//系统的addEventListener做到了与我的addEvent互不干扰
    btn.addEventListener("click",fn2);
//    btn.addEventListener("click",fn3);
//    btn.removeEventListener("click",fn2);
//    addEvent("click",fn2,btn);
//    console.log(btn.onclick);
    btn.onclick=null;//无法解绑 addEventListener方法绑定的事件  因为addEventListener是另一套机制
//addEvent("click",fn3,btn);

//赋诗的方法
    function fn1(){
        console.log("九尺龙泉万卷书,上天生我意何如。");
    }
    function fn2(){
        console.log("不能报国平天下,枉为男儿大丈夫。");
    }
    function fn3(){
        console.log("123")
    }
</script>
</body>
</html>

posted @ 2018-06-25 22:37  我叫贾文利  阅读(208)  评论(0编辑  收藏  举报