admin管理员组

文章数量:1587994

JavaScript-WebAPIs学习记录

浏览器交互效果

console.dir(); // 更好地查看里面的属性和方法

javascript:void(0); 或者 javascript:; 可以阻止a链接跳转

DOM

编程接口

获取元素

1、通过getElementById(‘字符串’)获取id名字,返回的是一个元素对象

2、通过getElementsByTagName(‘标签名’)获取标签名字,返回的是一个伪数组(arguments)

每个对象还是元素对象;没有元素返回任然是空的伪数组

还可以根据某个元素(父元素内部所有指定标签名子元素)

element.getElementsByTagName('标签名')
<ul id="ul">
    <li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
</ul>
<script>
    //父元素必须是单个对象(必须指明是哪个元素对象),获取的时候不包括父元素自己
    // var ul = document.getElementsByTagName('ul');
    // console.log(ul[0].getElementsByTagName('li'));
    var ul = document.getElementById('ul');
console.log(ul.getElementsByTagName('li'));
</script>

3、通过getElementsByClassName(‘类名’)获取类名,返回一个伪数组(高版本浏览器)

4、通过document.querySelector(‘符号选择器’)根据制定选择器返回第一个对象

var boxs = document.getElementsByClassName('box');
console.log(boxs);

var box1 = document.querySelector('.box');
console.log(box1);
var nav = document.querySelector('#nav');
console.log(nav);
var li = document.querySelector('li');
console.log(li);

var boxs = document.querySelectorAll('.box');
console.log(boxs);
var lis = document.querySelectorAll('li');
console.dir(lis);

5、css属性选择器document.querySelector(‘’[属性名=属性值]‘’)

let username = document.querySelector('[name=username]') //css属性选择器:通过属性进行选择标签 【属性=值】值不需要引号 等号两边不要加空格

6、获取doby元素和html元素

//1、获取body元素
var bodyEle = document.body;
console.log(bodyEle);
console.dir(bodyEle);

//2、获取html
var htmlEle = document.documentElement;
console.log(htmlEle);

事件

1、事件源(获取元素);

2、事件类型(点击、经过;绑定事件、注册事件);

3、事件处理程序(通过匿名函数赋值;添加事件处理程序)

常见鼠标事件

操作元素

1、改变元素内容

innerText 不识别html标签、空格和换行,可读写

innerHTML识别html标签、空格和换行,可读写(W3C标准)

var btn = document.querySelector('button');
var time1 = document.querySelector('.time1'); 
btn.onclick = function() { //添加点击事件
    //time1.innerText = '2023-4-10';
    time1.innerText = getDate(); //点击显示
}

var time2 = document.querySelector('.time2'); //直接显示,不添加事件
time2.innerText = getDate();

function getDate() {
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1; //加1
    var dates = date.getDate();
    var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
    var day = date.getDay();
    return '当前日期是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];
}
2、改变元素属性

操作常见属性src、href、id、alt、title等等;

操作表单元素type、value、checked、selected、disabled等等;

经常用this代替时间函数的调用者

//获取元素;
//元素.属性名 = 操作;
element.属性名   获取元素本身的属性
修改属性案例

全天问候

//全天问候
var ts = document.getElementById('ts');
var img = document.getElementsByTagName('img');

var date = new Date();
var h = date.getHours();

if(h > 6 && h < 12) {
    ts.innerHTML = '上午好!';
    img[0].src = './images/bg1.png';
} else if(h >= 12 && h <= 18) {
    ts.innerHTML = '下午好!';
    img[0].src = './images/bg2.png';
} else {
    ts.innerHTML = '晚上好!';
    img[0].src = './images/bg3.png';
}

密码框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>密码框</title>
</head>
<style>
    .box {
        position: relative;
        width: 400px;
        border-bottom: 1px solid #aaa;
        border-radius: 2px;
        margin: 100px auto;
    }
    .box input {
        width: 370px;
        height: 30px;
        border: 0;
        outline: none;
    }
    .box img {
        position: absolute;
        top: 12px;
        right: 2px;
        width: 24px;
    }
</style>
<body>
    <div class="box">
        <label for="">
            <img src="./images/bg1.png" alt="" id="eye">
        </label>
        <input type="password" name="" id="pwd">
    </div>
    <script>
        //1、获取元素
        var eye = document.getElementById('eye');
        var pwd = document.getElementById('pwd');
        //2、注册事件,处理程序
        var flag = 0; //定义flag判断是否点击
        eye.onclick = function() {
            //点击一次,flag变化
            if(flag == 0) {
                pwd.type = 'text';
                eye.src = './images/bg2.png';
                flag = 1; //赋值
            } else {
                pwd.type = 'password';
                eye.src = './images/bg1.png';
                flag = 0;
            }
        }
    </script>
</body>
</html>
3、样式属性修改

element.style 行内样式修改

element.className 类名样式修改 (可以用多类名选择器,保留原来类名)

特别的:className容易覆盖以前的类名样式,我们可以通过classList方式追加和删除类名

// 追加
element.classList.add('类名');

// 删除
element.classList.remove('类名');

//切换类名,有则删除,无则添加
element.classList.toggle('类名');

// 是否包含 如果有则返回true 如果没有该类名则返回false
element.classList.contains('类名')

注意

JS里面的样式采用的是驼峰命名法 比如 fontSize、 backgroundColor

JS修改style样式操作,产生的是行内样式,CSS权重比较高

样式属性案例

二维码隐藏

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>密码框</title>
</head>
<style>
    .box {
        position: relative;
        width: 74px;
        height: 88px;
        border: 1px solid #ccc;
        margin: 100px auto;
        font-size: 12px;
        text-align: center;
        color: #f40;
    }
    img {
        width: 60px;
        margin-top: 5px;
    }
    .close-btn {
        position: absolute;
        top: -1px;
        left: -16px;
        width: 14px;
        height: 14px;
        border: 1px solid #ccc;
        line-height: 14px;
        font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
        cursor: pointer;
    }
</style>
<body>
    <div class="box">
        CSDN
        <img src="./images/CSDN.jpg" alt="">
        <i class="close-btn">x</i>
    </div>
    <script>
        //1、获取元素
        var btn = document.querySelector('.close-btn');
        var box = document.querySelector('.box');
        //2、注册事件,处理程序
        btn.onclick = function() {
            box.style.display = 'none';
        }
    </script>
</body>
</html>

循环精灵图

<style>
    * {
        margin: 0;
        padding: 0;
    }
    
    li {
        list-style-type: none;
    }
    
    .box {
        width: 250px;
        margin: 100px auto;
    }
    
    .box li {
        float: left;
        width: 24px;
        height: 24px;
        background-color: red;
        margin: 15px;
        background: url(./images/精灵图.png) no-repeat;
    }
</style>
</head>

<body>
    <div class="box">
        <ul>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>
    <script>
        //获取元素
        var lis = document.querySelectorAll('li');
        //精灵图有规律
        for (var i = 0; i < lis.length; i++) {
            //让 索引号乘以44 就是每个li的背景坐标 y就是垂直坐标
            var y = -44 * i;
            lis[i].style.backgroundPosition = '0 ' + y + 'px';
        }
    </script>
</body>

表单隐藏文本内容

<style>
    input {
        color: #999;
        border: 1px solid rgb(0, 35, 234);
        border-radius: 2px;
    }
</style>
<body>
    <input type="text" value="手机">
    <script>
        var text = document.querySelector('input');
        text.onfocus = function(){
            if(this.value == '手机') {
                this.value = '';
            }
            this.color = '#333';
        }
        text.onblur = function() {
            if(this.value == '') {
                this.value = '手机';
            }
            this.color = '#999';
        }
    </script>
</body>

密码框验证

<style>
    div {
        width: 600px;
        margin: 100px auto;
    }
    .message {
        display: inline-block;
        font-size: 12px;
        color: #999;
        background: url(images/bg1.png) no-repeat left center;
        background-size: 20px;
        padding-left: 20px;
    }
    .wrong {
        color: red;
        background-image: url(./images/bg2.png);
    }
    .right {
        color: green;
        background-image: url(./images/bg3.png);
    }
</style>
<body>
    <div class="regiser">
        <input type="password" class="ipt">
        <p class="message">请输入6~16位密码</p>
    </div>
    <script>
        var ipt = document.querySelector('.ipt');
        var message = document.querySelector('.message');
        ipt.onblur = function() {
            if(this.value.length < 6 || this.value.length > 16) {
                message.className = 'message wrong';
                message.innerHTML = '请输入6~16位数的密码!';
            } else {
                message.className = 'message right';
                message.innerHTML = '正确!';
            }
        }
    </script>
</body>

排他思想

<style>
    body {
        background: url(./images/bg1.png) no-repeat fixed;
        background-size: 100%;
        background-position: center top;
        /* height: 100%;
        width: 100%; */
    }
    .box {
        margin: 10px auto;
        width: 120px;
    }
    button {
        width: 10px;
        height: 15px;
        border-radius: 50%;
    }
</style>
<body>
    <div class="box">
        <button></button>
        <button></button>
        <button></button>
    </div>
    <script>
        var btns = document.getElementsByTagName('button'); //伪数组
        for(var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                //去掉所有按钮背景色
                for(var j = 0; j < btns.length; j++) {
                    btns[j].style.backgroundColor = '';
                }
                //给点击的按钮附加背景色
                this.style.backgroundColor = 'green';
            }
        }
    </script>
</body>

图片换肤

<style>
    * {
        margin: 0;
        padding: 0;
    }

    li {
        list-style: none;
    }

    body {
        background: url(./images/bg1.png) fixed;
        background-size: 100%;
        background-position: center top;
        /* height: 100%;
        width: 100%; */
    }

    .bj {
        overflow: hidden;
        margin: 15px auto;
        background-color: #fff;
        width: 400px;
        padding-top: 3px;
    }

    .bj li {
        float: left;
        margin: 0 1px;
        cursor: pointer; /* 鼠标一只手 */
    }

    .bj img {
        width: 98px;
        height: 52px;
    }
</style>
<body>
    <ul class="bj">
        <li><img src="./images/bg1.png" alt=""></li>
        <li><img src="./images/bg2.png" alt=""></li>
        <li><img src="./images/bg3.png" alt=""></li>
        <li><img src="./images/bg4.png" alt=""></li>
    </ul>

    <script>
        var imgs = document.querySelector('.bj').querySelectorAll('img');

        for(var i = 0; i < imgs.length; i++) {
            imgs[i].onclick = function() {
                document.body.style.backgroundImage = 'url(' + this.src + ')';
            }
        }
    </script>
</body>

鼠标隔行变色

<style>
    table {
        width: 800px;
        margin: 100px auto;
        text-align: center;
        border-collapse: collapse;
        font-size: 14px;
    }
     thead tr {
        height: 30px;
        background-color: skyblue;
     }

     tbody tr {
        height: 30px;
     }

     tbody td {
        border-bottom: 1px solid #d7d7d7;
        font-size: 12px;
        color: blue;
     }

     .bg {
        background-color: pink;
     }
</style>
<body>
    <table>
        <thead>
            <tr>
                <th>数字</th>
                <th>大写字母</th>
                <th>小写字母</th>
                <th>字符</th>
                <th>字符串</th>
                <th>函数</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>A</td>
                <td>a</td>
                <td>'A'</td>
                <td>'Aa'</td>
                <td>Aa()</td>
            </tr>
            <tr>
                <td>2</td>
                <td>B</td>
                <td>b</td>
                <td>'B'</td>
                <td>'Bb'</td>
                <td>Bb()</td>
            </tr>
            <tr>
                <td>3</td>
                <td>C</td>
                <td>c</td>
                <td>'C'</td>
                <td>'Cc'</td>
                <td>Cc()</td>
            </tr>
            <tr>
                <td>4</td>
                <td>D</td>
                <td>d</td>
                <td>'D'</td>
                <td>'Dd'</td>
                <td>Dd()</td>
            </tr>
            <tr>
                <td>5</td>
                <td>E</td>
                <td>e</td>
                <td>'E'</td>
                <td>'Ee'</td>
                <td>Ee()</td>
            </tr>
        </tbody>
    </table>

    <script>
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        //利用循环绑定事件
        for(var i = 0; i < trs.length; i++) {
            //鼠标经过事件 onmouseover
            trs[i].onmouseover = function() {
                this.className = 'bg';
            }
            //鼠标离开事件 onmouseout
            trs[i].onmouseout = function() {
                this.className = '';
            }
        }
    </script>
</body>

全选和取消

<body>
    <table>
        <thead>
            <tr>
                <th>全选:<input type="checkbox" id="j_cbAll"></th>
                <th>大写字母</th>
                <th>小写字母</th>
                <th>字符</th>
                <th>字符串</th>
                <th>函数</th>
            </tr>
        </thead>
        <tbody id="j_tb">
            <tr>
                <td><input type="checkbox"></td>
                <td>A</td>
                <td>a</td>
                <td>'A'</td>
                <td>'Aa'</td>
                <td>Aa()</td>
            </tr>
            <tr>
                <td><input type="checkbox"></td>
                <td>B</td>
                <td>b</td>
                <td>'B'</td>
                <td>'Bb'</td>
                <td>Bb()</td>
            </tr>
            <tr>
                <td><input type="checkbox"></td>
                <td>C</td>
                <td>c</td>
                <td>'C'</td>
                <td>'Cc'</td>
                <td>Cc()</td>
            </tr>
            <tr>
                <td><input type="checkbox"></td>
                <td>D</td>
                <td>d</td>
                <td>'D'</td>
                <td>'Dd'</td>
                <td>Dd()</td>
            </tr>
            <tr>
                <td><input type="checkbox"></td>
                <td>E</td>
                <td>e</td>
                <td>'E'</td>
                <td>'Ee'</td>
                <td>Ee()</td>
            </tr>
        </tbody>
    </table>

    <script>
        //表格隔行变色
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        //利用循环绑定事件
        for(var i = 0; i < trs.length; i++) {
            //鼠标经过事件 onmouseover
            trs[i].onmouseover = function() {
                this.className = 'bg';
            }
            //鼠标离开事件 onmouseout
            trs[i].onmouseout = function() {
                this.className = '';
            }
        }

        //全选和取消全选
        var j_cbAll = document.getElementById('j_cbAll');
        var j_tbs = document.getElementById('j_tb').getElementsByTagName('input');
        j_cbAll.onclick = function() {
            //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;
                //每次点击下面的复选框都要循环检查所有按钮是否被选中
                for(var j = 0; j < j_tbs.length; j++) {
                    if (!j_tbs[j].checked) { //没有选中
                        flag = false;
                        //退出,提高效率;只要有一个没选中就不用继续了
                        break;
                    }
                }
                j_cbAll.checked = flag;
            }
        }
    </script>
</body>
4、获取自定义元素属性

element.getAttribute(‘属性’); 主要用于获得自定义的属性;

H5新增规范和要求:以 date- 开头自定义属性 //ie11以上

//dateset是一个集合里面存放了所有以date开头的自定义属性

element.dateset.属性   //自定义有多个 - 链接属性名用驼峰命名  h5新增
element.dateset['属性']  //自定义有多个 - 链接属性名用驼峰命名比如:indexName  h5新增
//可以setAttribute('属性', '要设置的属性值'); getAttribute('属性');
5、设置元素属性值

element.setAttribute(‘属性’, ‘要设置的属性值’); 主要用于设置自定义的属性

6、移除属性

element.removeAttribute(‘属性’);

tab栏切换(重要)

<style>
    .tab{
        width: 618px;
        height: 400px;
        margin: 0 auto;
    }

    .tab_list {
        /* border-bottom: 1px solid block; */
        height: 40px;
        background-color: rgb(211, 208, 208);
        margin: 0 auto;
    }

    .tab_list li {
        float: left;
        height: 39px;
        line-height: 39px;
        padding: 0 20px;
        text-align: center;
        cursor: pointer;
        list-style: none;
      
    }

    .tab_list .current {
        background-color: rgb(157, 23, 23);
        color: aliceblue;
    }

    .tab_con{
        width: 600px;
        height: 75px;
        margin: 0 auto;
    }

    .item{
        height: 60px;
        padding: 10px;
        margin: 10px auto;
    }

    .item_info {
        padding: 20px 0 0 20px;
    }

    .item {
        display: none;
    }
</style>
</head>
<body>
    <div class="tab">
        <div class="tab_list">
            <ul>
                <li class="current">商品介绍</li>
                <li>规格与包装</li>
                <li>售后保障</li>
                <li>商品评价</li>
                <li>手机社区</li>
            </ul>
        </div>

        <div class="tab_con">
            <div class="item" style="display: block;">
                商品模块介绍
            </div>
            <div class="item">
                规格包装模块内容
            </div>
            <div class="item">
                售后保障模块
            </div>
            <div class="item">
                商品介绍模块
            </div>
            <div class="item">
                手机社区模块
            </div>
        </div>
    </div>
<script>
    // 1.上的模块选项卡,点击某个,当前这一个底色会是红色,其余不变(排他思想)修改类名的方式。
    // 获取元素
    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() {
            //清楚所有current 其余的li清楚class这个类
            for(var i = 0; i < lis.length; i++){
                lis[i].className = '';
            }
            //留下自己的
            this.className = 'current';

            //2.显示内容模块
            var index = this.getAttribute('index');
            //清楚所有 让其余的item这些div隐藏
            for(var i = 0; i < items.length; i++){
                items[i].style.display = 'none';
            }
            //留下我自己让对应的item显示出来
            items[index].style.display = 'block';
        }
    }
</script>
</body>

节点操作

利用父子兄关系操作元素(元素、属性、文本:1、2、3)

1、父节点parentNode

最近原则,找不到为null

2、子节点childNodes

包含所有的元素节点、属性节点和文本节点,需要用循环遍历出元素节点

一般用 parent.children 获取子元素的元素节点

parent.firstChild //注意第一个可能是文本节点

parent.lastChild //注意最后一个可能是文本节点

parent.firstElementChild //返回第一个子元素节点,ie9

parent.lastElementChild //返回最后一个子元素节点,ie9

一般使用

parent.children[0]

parent.children[parent.children.length - 1]

下拉菜单

<style>
    li {
        list-style: none;
    }
    
    a {
        text-decoration: none;
        color: #000;
    }

    .nav {
        height: 30px;
        width: 322px;
        background-color: #eee;
        margin: 0 auto;
        padding: 0;
        line-height: 30px;
    }

    .nav li {
        position: relative;
        float: left;
        width: 80px;
        font-size: 14px;
        text-align: center;
        margin: 0 auto;
        /* left: 50%;
        transform: translate(-50%); */
        background-color: #eee;
    }

    .nav li:hover {
        background-color: #FFF5DA;
    }

    .nav ul {
        display: none;
        position: absolute;
        top: 30px;
        left: 0;
        margin: 5px auto;

        /* left: 50%;
        transform: translate(-50%); */
        
        padding: 0;
        width: 100%;
        border-left: 1px solid #000;
        border-right: 1px solid #000;
    }

    .nav ul li {
        margin: 0 auto;
        margin-top: 4px;
        border-bottom: 1px solid #000;
        line-height: 20px;
        text-align: center;
    }
    .nav ul li:hover {
        background-color: #FFF5DA;
    }
</style>
<body>
    <ul class="nav">
        <li>
            <a href="#">微博</a>
            <ul>
                <li><a href="">私信</a></li>
                <li><a href="">评论</a></li>
                <li><a href="">@我</a></li>
            </ul>
        </li>
        <li>
            <a href="#">微博</a>
            <ul>
                <li><a href="">私信</a></li>
                <li><a href="">评论</a></li>
                <li><a href="">@我</a></li>
            </ul>
        </li>
        <li>
            <a href="#">微博</a>
            <ul>
                <li><a href="">私信</a></li>
                <li><a href="">评论</a></li>
                <li><a href="">@我</a></li>
            </ul>
        </li>
        <li>
            <a href="#">微博</a>
            <ul>
                <li><a href="">私信</a></li>
                <li><a href="">评论</a></li>
                <li><a href="">@我</a></li>
            </ul>
        </li>
    </ul>
    <script>
        var nav = document.querySelector('.nav');
        var lis = nav.children;
        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';
            }
        }
    </script>
</body>
3、兄弟节点nextSibling、previousSibling

nextSibling、previousSibling 包含所有元素节点、属性节点和文本节点,需要用循环遍历出元素节点(可以自己封装一个函数)

一般用 node.nextElementSibling 获取下一个兄弟元素节点 ie9

node.previousElementSibling 获取上一个兄弟元素节点 ie9

4、创建节点

①document.createElement(‘tagName’); 动态创建元素节点

②node.appendChild(child); 追加元素 node为父级,child为子级;

node.insertBefore(child, 指定元素) 指定元素前边添加

<body>
    <ul>
        <li>1111</li>
    </ul>
    <script>
        //创建元素节点
        var li = document.createElement('li');
        //添加节点  node.appendChild(child);   node为父级,child为子级
        var ul = document.querySelector('ul');
        ul.appendChild(li);

        //添加元素,node.insertBefore(child, 指定元素)   指定元素前边添加
        var lili = document.createElement('li');
        ul.insertBefore(lili, ul.children[0]);
    </script>
</body>

发布留言

<style>
    textarea {
        margin-left: 440px;
        width: 30em;
        height: 10em;
    }

    ul li {
        width: 30em;
        background-color: pink;
        color: red;
        font-size: 14px;
        margin: 15px auto;
    }
</style>
<body>
    <textarea name="" id="" placeholder="请输入留言"></textarea>
    <button>发布</button>
    <ul>
        <!-- <li>222</li> -->
    </ul>
    <script>
        var btn = document.querySelector('button');
        var text = document.querySelector('textarea');
        var ul = document.querySelector('ul');

        btn.onclick = function() {
            if (text.value == '') {
                alert('输入内容不能为空!')
                return false;
            } else {
                var li = document.createElement('li');
                li.innerHTML = text.value;
                ul.insertBefore(li, ul.children[0]);
            }
        }
    </script>
</body>
5、删除节点

node.removeChild(child) 删除子节点,返回删除元素

<body>
    <button>删除</button>
    <ul>
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
    </ul>
    <script>
        var btn = document.querySelector('button');
        var ul = document.querySelector('ul');
        btn.onclick = function() {
            if (ul.children.length != 0) {
                ul.removeChild(ul.children[0]);
            } else {
                alert('没有可删除的了!');
                this.disabled = true;
                return false;
            }
        }
    </script>
</body>

发布和删除留言

<style>
    textarea {
        margin-left: 440px;
        width: 31em;
        height: 10em;
    }

    ul li {
        width: 30em;
        background-color: pink;
        color: red;
        font-size: 14px;
        margin: 15px auto;
    }
    li a {
        text-decoration: none;
        float: right;
    }
</style>
<body>
    <textarea name="" id="" placeholder="请输入留言"></textarea>
    <button>发布</button>
    <ul>
        <!-- <li>222</li> -->
    </ul>
    <script>
        var btn = document.querySelector('button');
        var text = document.querySelector('textarea');
        var ul = document.querySelector('ul');

        btn.onclick = function() {
            if (text.value == '') {
                alert('输入内容不能为空!')
                return false;
            } else {
                var li = document.createElement('li');
                //添加留言和删除链接
                li.innerHTML = text.value + "<a href='javascript:;' >删除</a>";
                ul.insertBefore(li, ul.children[0]);
                //删除元素
                var as = document.querySelectorAll('a');
                for (var i = 0; i < as.length; i++) {
                    as[i].onclick = function() {
                        //node.removeChild(child)   要删除li是当前a的父元素 this.parentNode
                        ul.removeChild(this.parentNode);
                    }
                }
            }
        }
    </script>
</body>
6、复制节点

①node.cloneNode() 复制;参数为空或者false,浅拷贝,只复制标签不复制内容;可以添加true,深复制

②node.appendChild(child); 追加元素 node为父级,child为子级;

node.insertBefore(child, 指定元素) 指定元素前边添加

动态生成表格

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态生成表格</title>
    <style>
        a {
            text-decoration: none;
            color: red;
            padding: 0 2px;
            border: 1px solid black;
            border-radius: 2px;
        }
        table {
            width: 500px;
            margin: 100px auto;
            border-collapse: center;
        }

        td,
        th {
            border: 1px solid #333;
            text-align: center;
        }

        thead tr {
            height: 40px;
            background-color: #ccc;
        }
        tbody tr {
            height: 30px;
        }
    </style>
</head>
<body>
    <table>
        <thead>
            <tr>
                <th>姓名</th>
                <th>科目</th>
                <th>成绩</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
    <script>
        //1、准备数据
        var dates = [{
            name: '张三',
            subject: 'JavaScript',
            score: 98
        },{
            name: '李四',
            subject: 'JavaScript',
            score: 89
        },{
            name: '王五',
            subject: 'JavaScript',
            score: 71
        },{
            name: '赵六',
            subject: 'JavaScript',
            score: 65
        }];
        //2、向tbody里面创建行,行数等于数组长度
        var tbody = document.querySelector('tbody');
        for (var i = 0; i < dates.length; i++) {
            //创建tr
            var tr = document.createElement('tr');
            tbody.appendChild(tr);
            for (var k in dates[i]) { // 里面的for循环 列数 td
                //创建单元格td
                var td = document.createElement('td');
                //赋值
                td.innerHTML = dates[i][k];
                tr.appendChild(td);
            }
            // 3、创建有删除2个字的单元格
            var td = document.createElement('td');
            td.innerHTML = '<a href="javascript:;">删除</a>';
            tr.appendChild(td);
        }

        //4、删除操作
        var as = document.querySelectorAll('a');
        for (var i = 0; i < as.length; i++) {
            as[i].onclick = function() {
                alert('确定要删除吗?三思!')
                //点击a删除所在行, "a.td.tr"  node.removeChild(child);
                tbody.removeChild(this.parentNode.parentNode);
            }
        }
    </script>
</body>
</html>
7、三种动态创建元素

①document.write(); 创建元素,如果页面文档流加载完毕,再调用这句话会导致重绘

像是点击创建

window.onload = funcion(){
	document.write('<div>123</div>');
}

②innerHTML 拼接字符串效率低,如果采取数组模式 [join(‘’)逗号分隔] 效率更高

③document.createElement(‘tagName’); 结构清晰

8、重绘和回流

重绘
简单来说就是重新绘画,当给一个元素更换颜色、更换背景,虽然不会影响页面布局,但是颜色或背景变了,就会重新渲染页面,这就是重绘。

回流
当增加或删除dom节点,或者给元素修改宽高时,会改变页面布局,那么就会重新构造dom树然后再次进行渲染,这就是回流。

总结
重绘不会引起dom结构和页面布局的变化,只是样式的变化,有重绘不一定有回流。
回流则是会引起dom结构和页面布局的变化,有回流就一定有重绘。
不管怎么说都是会影响性能。
怎么进行优化或减少?
1.多个属性尽量使用简写,例如:boder可以代替boder-width、boder-color、boder-style
2.创建多个dom节点时,使用documentfragment创建
3.避免使用table布局
4.避免设置多层内联样式,避免节点层级过多
5.避免使用css表达式
6.将频繁重绘或回流的节点设置为图层,图层能够阻止该节点的渲染行为影响到别的节点(例:will-change\video\iframe等标签),浏览器会自动将该节点变为图层

事件高级

注册事件

1、传统注册方式以on开头的事件具有唯一性,会覆盖之前的;

2、方法监听注册事件,同一个元素同一个事件可以注册多个监听器,按注册顺序执行

w3c标准,addEventListener()它是一个方法,ie9之前可以用attachEvent代替

eventTarget.addEventListener(type, listener[,  userCapture]);
//type是字符串需要加'',不用添加on
//listener不需要添加(),直接写函数名
//userCapture可有可无,默认为false
eventTarget.attachEvent(eventNameWithOn, callback)  //ie9以前,独有
//eventNameWithOn  带on的事件字符串
//callback  事件处理函数

3、封装

删除事件

1、传统解绑 eventTarget.οnclick=null;

2、方法监听注册方式

eventTarget.removeEventListener(type, listener[, userCapture]);

eventTarget.detachEvent(eventNameWithOn, callback);

3、封装

事件流

事件发生的传播过程

事件捕获–>处于目标阶段–>冒泡阶段

eventTarget.removeEventListener(type, listener[, userCapture]);

如果 userCapture为ture ,则处于捕获阶段;如果为false或者为空,为冒泡阶段

注意:

事件对象

eventTarget.onclick = function(event) {};

eventTarget.removeEventListener(type, function(event) {});

1、event 就是事件对象监听函数小括号内,当形参来看,不需要传实参去

2、事件对象必须有了事件才会存在,系统自建

3、事件对象里面有相关属性方法

4、事件对象我们可以自己命名 e等

5、有兼容性问题 ie678 通过 window.event 兼容性写法: e = e || window.event;

6、e.target (ie9以上有效,之前的用e.srcElement)返回的是触发事件对象(点击哪个就返回那个),this (currentTarget有兼容性问题)返回绑定事件方法

7、e.preventDefault(); 阻止默认行为(事件)|| e.returnValue; || return false;(后边代码不执行,只限于传统注册方式)

阻止事件冒泡

e.stopPropagation(); //阻止传播 ie678用:window.event.cancelBubble = ture;

e.preventDefault();阻止默认行为

<script>
    //禁用鼠标右键菜单
    document.addEventListener('contextmenu', function(e) {
    e.preventDefault();
});
	//禁用鼠标选中
	document.addEventListener('selectstart', function(e) {
    e.preventDefault();
});
</script>

事件委托

不是每个子节点单独设置事件监听器,而是时间监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点

可以与e.target配合使用

鼠标事件对象和键盘事件对象

1、常用鼠标事件对象

鼠标位置图片

<style>
    img {
        position: absolute;
        width: 40px;
    }
</style>
</head>
<body>
    <img src="images/angle2.gif" alt="">
    <script>
        var angle = document.querySelector('img');
        document.addEventListener('mousemove', function(e) {
            var x = e.pageX;
            var y = e.pageY;
            angle.style.left = x - 20 + 'px';
            angle.style.top = y - 20 + 'px';
        })
    </script>
</body>

2、常见键盘事件

键盘事件触发事件
onkeyup某个按钮按键被松开时触发
onkeydown某个按钮按键被按下时触发
onkeypress某个按钮按键被按下时触发 但是他不识别功能键 比如 ctrl shif 箭头等

先执行onkeydown,再执行onkeypress,最后执行onkeyup;记得事件监听没有on

keydown、keyup 不区分大小写,A和a的e.keyCode返回值ASCII都是65;

keypress 区分大小写,A和a的e.keyCode返回值ASCII分别是65和97;

    <style>
        .father {
            margin: 100px auto;
            position: relative;
            width: 178px;
            height: 150px;
        }

        .big {
            position: absolute;
            display: none;
            top: -40px;
            width: 171px;
            border: 1px solid rgb(0, 0, 0, 0.2);
            box-shadow: 0 2px 4px rgb(0, 0, 0, 0.2);
            padding: 5px 0;
            font-size: 20px;
            line-height: 20px;
            color: #333;
        }

        .big::before {
            content: '';
            width: 0;
            height: 0;
            position: absolute;
            top: 28px;
            left: 18px;
            border: 8px solid; /* 盒子边框尺寸要等于一半盒子高度 */
            border-color: #fff transparent transparent;  /* 颜色要设置为透明transparent */
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="big">123</div>
        <input type="text" placeholder="请输入..." class="son">
    </div>
    
    <script>
        var big = document.querySelector('.big');
        var search = document.querySelector('input');
        document.addEventListener('keyup', function(e) {
            console.log(e.keyCode);
            if (e.keyCode === 83) {
                search.focus();
            }
        });
        
//此处不能用keydown、keypress
        search.addEventListener('keyup', function () {
            if (this.value == '') {
                big.style.display = 'none';
            } else {
                big.style.display = 'block';
                big.innerHTML = this.value;
            }
        })
    </script>
</body>

注意:keydown和keypress在文本框内特点,就是他们触发的时候,文字还没有落入文本框内

BOM

浏览器对象模型

window对象常见事件

1、窗口加载事件

文档内容加载完毕才会触发

window.onload = function(){}
或者
window.addEventListener('load', function(){})

window.onload 传统的方式只能写一次,多个以后者为准

addEventListioner('DOMContentLoaded',function(){})

DOM加载完毕,不包括图片 fash css等就可以执行,速度比load快

2、调整窗口大小事件
window.onresize = function() {};
winow.addEventListioner('resize', function(){});

经常利用它完成响应式布局,window.innerWidth当前屏幕宽度

3、定时器

①隔了多少时间调用(回调函数)

window.setTimeout(function(){}, 延时时间(毫秒))

window可以省略;

可以直接写函数 ,或者 写函数名 或者 采用字符串 ‘ 函数名()’三种格式;

毫秒默认是0,写的话必须是毫秒数

可以给他赋值一个标识符

还可以通过递归函数实现定时器的功能

    <div></div>
    <script>
        let div = document.querySelector('div')
        function fn() {
            div.innerHTML = new Date().toLocaleString()
            setTimeout(fn, 1000)
        }
        fn()
    </script>

②停止setTimeout() 定时器

window.clearTimeout(timeoutID);

③setInterval()定时器,每隔一段时间就调用一次

window.setInterval(回调函数, [间隔毫秒数]);

window可以省略;

可以**直接写函数 **,或者 写函数名 或者 采用字符串‘函数名()’三种格式;

毫秒默认是0,写的话必须是毫秒数

可以给他赋值一个标识符

案例

倒计时

    <style>
        span {
            display: inline-flex;
            width: 20px;
            height: 20px;
            margin: 10px auto;
            padding: 10px;
            background-color: black;
            color: aliceblue;
        }
    </style>
    <script>
        window.addEventListener('load', function(){
            var hour = document.querySelector('.hour');
            var minute = document.querySelector('.minute');
            var second = document.querySelector('.second');
            var inputTime = +new Date('2023-4-21 18:00:00'); //用户输入时间的总毫秒数
            countDown(); // 先调用一次,防止第一次刷新空白
            //开启定时器
            setInterval(countDown, 1000);
            // 转化公式如下:
            //   h = parseInt(总秒数 / 60 / 60); //计算小时,注意不用%24求余,因为没有天数
            //   m = parseInt(总秒数 / 60 % 60); //计算分数
            //   s = parseInt(总秒数 % 60); //计算当前秒数
            function countDown() {
                var nowTime = +new Date(); //当前时间的总毫秒数
                var times = (inputTime - nowTime) / 1000; //剩余时间总秒数
                var h = parseInt(times / 60 / 60); //计算小时,注意不用%24求余,因为没有天数
                h = h < 10 ? '0' + h : h;
                hour.innerHTML = h;
                var m = parseInt(times / 60 % 60); //计算分数
                m = m < 10 ? '0' + m : m;
                minute.innerHTML = m;
                var s = parseInt(times % 60); //计算当前秒数
                s = s < 10 ? '0' + s : s;
                second.innerHTML = s;
            }
        })
    </script>
</head>
<body>
    <span class="hour"></span>
    <span class="minute"></span>
    <span class="second"></span>
</body>

开启停止定时器

    <title>开启停止定时器</title>
    <script>
        window.addEventListener('load', function() {
            var begin = document.querySelector('.begin');
            var stop = document.querySelector('.stop');
            var times = null; //全局变量,null一个空对象
            begin.addEventListener('click', function() {
                times = setInterval(function() {
                    console.log('你好吗!');
                }, 1000)
            })
            stop.addEventListener('click', function() {
                clearTimeout(times);
            })
        })
    </script>
</head>
<body>
    <button class="begin">开启定时器</button>
    <button class="stop">停止定时器</button>
</body>

发送3秒计时器

<script>
    window.addEventListener('load', function() {
    var btn = document.querySelector('button');
    var time = 3; //定义剩下秒数
    btn.addEventListener('click', function() {
        //禁用按钮
        this.disabled = true;
        //定义定时器           
        var timer = setInterval(function() {
            if (time == 0) {
                //时间到了,清楚定时器复原按钮和时间
                clearInterval(timer);
                btn.disabled = false;
                btn.innerHTML = '发送';
                time = 3; //重新开始
            } else {
                btn.innerHTML = time + '秒';
                time--;
            }
        }, 1000)
        })
	})
</script>

this

指向父元素

同步、异步(面试)

事件循环(event loop)

location对象

location.reload 刷新

点击10秒后跳转

<button>点击</button>
<div></div>
<script>
    var btn = document.querySelector('button');
    var div = document.querySelector('div');
    btn.addEventListener('click', function() {
        time = 10;
        setInterval(function() {
            if (time == 0) {
                location.href = 'http://www.baidu';
                time = 10;
            } else {
                div.innerHTML = '你将在' + time + '秒钟后跳转到页面';
                time--;
            }
        }, 1000)
    })
</script>

表单提交欢迎

    登录页面
    <form action="index.html">
        用户名:<input type="text" name="uname">
        <input type="submit" value="登录">
    </form>
    
        首页
    <div></div>
    <script>
        console.log(location.search); // ?uname=ssy
        //1、截取字符,substa('起始位置',截取几个字符)
        var params = location.search.substr(1); //默认截取后边全部
        console.log(params); // uname=ssy
        //2、分隔,split('分隔符'),分割为数组
        var str = params.split('='); 
        console.log(str); // ['uname','ssy']
        //3、调用
        var div = document.querySelector('div');
        div.innerHTML = str[1] + '欢迎你!';
    </script>

location 对象方法

navigator对象

它包含浏览器相关信息

常用userAgent属性检查浏览器版本及平台

history对象

PC端网页特效

元素偏移量offset系列

1、offset概述

2、offset与style区别

案例

鼠标在盒子中的坐标

    <style>
        * {
            margin: 0;
            padding: 0;
        }
        .box {
            position: relative;
            margin: 200px;
            width: 400px;
            height: 400px;
            background-color: rgb(54, 219, 134);
        }
    </style>
</head>
<body>
    <div class="box"></div>
    <script>
        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;
        })
    </script>
</body>

模态框拖拽

    <style>
        * {
            padding: 0px;
            margin: 0px;
        }
        .login-header {
            width: 100%;
            text-align: center;
            height: 30px;
            font-size: 24px;
            line-height: 30px;
        }
        .login {
            display: none;
            width: 512px;
            height: 280px;
            position: fixed;
            border: #ebebeb solid 1px;
            left: 50%;
            top: 50%;
            background: #ffffff;
            box-shadow: 0px 0px 20px #ddd;
            z-index: 9999;
            transform: translate(-50%, -50%);
        }
        .login-title {
            width: 100%;
            margin: 10px 0px 0px 0px;
            text-align: center;
            line-height: 40px;
            height: 40px;
            font-size: 18px;
            position: relative;
            cursor: move;
        }
        .login-input-content {
            margin-top: 20px;
        }
        .login-button {
            width: 50%;
            margin: 30px auto 0px auto;
            line-height: 40px;
            font-size: 14px;
            border: #ebebeb 1px solid;
            text-align: center;
        }
        .login-bg {
            display: none;
            width: 100%;
            height: 100%;
            position: fixed;
            top: 0px;
            left: 0px;
            background: rgba(0, 0, 0, .3);
        }
        a {
            text-decoration: none;
            color: #000000;
        }
        .login-button a {
            display: block;
        }
        .login-input input.list-input {
            float: left;
            line-height: 35px;
            height: 35px;
            width: 350px;
            border: #ebebeb 1px solid;
            text-indent: 5px;
        }
        .login-input {
            overflow: hidden;
            margin: 0px 0px 20px 0px;
        }
        .login-input label {
            float: left;
            width: 90px;
            padding-right: 10px;
            text-align: right;
            line-height: 35px;
            height: 35px;
            font-size: 14px;
        }
        .login-title span {
            position: absolute;
            font-size: 12px;
            right: -20px;
            top: -30px;
            background: #ffffff;
            border: #ebebeb solid 1px;
            width: 40px;
            height: 40px;
            border-radius: 20px;
        }
    </style>
</head>
<body>
    <div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</a></div>
    
    <!-- 弹出框 -->
    <div id="login" class="login">
        <div id="title" class="login-title">登录会员
            <span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
        </div>
        <div class="login-input-content">
            <div class="login-input">
                <label>用户名:</label>
                <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
            </div>
            <div class="login-input">
                <label>登录密码:</label>
                <input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
            </div>
        </div>
        <div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div>
    </div>

    <!-- 遮盖层 -->
    <div id="bg" class="login-bg"></div>

    <script>
        // 1. 获取元素
        var login = document.querySelector('.login');
        var mask = document.querySelector('.login-bg');
        var link = document.querySelector('#link');
        var closeBtn = document.querySelector('#closeBtn');
        var title = document.querySelector('#title');
        // 2. 点击弹出层这个链接 link  让mask 和login 显示出来
        link.addEventListener('click', function() {
                mask.style.display = 'block';
                login.style.display = 'block';
            })

        // 3. 点击 closeBtn 就隐藏 mask 和 login 
        closeBtn.addEventListener('click', function() {
                mask.style.display = 'none';
                login.style.display = 'none';
            })

        // 4. 开始拖拽
            // (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
        title.addEventListener('mousedown', function(e) {
            var x = e.pageX - login.offsetLeft;
            var y = e.pageY - login.offsetTop;
            
            // (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值
            document.addEventListener('mousemove', move)
            function move(e) {
                login.style.left = e.pageX - x + 'px';
                login.style.top = e.pageY - y + 'px';
            }

            // (3) 鼠标弹起,就让鼠标移动事件移除
            document.addEventListener('mouseup', function() {
                document.removeEventListener('mousemove', move);
            })
        })
    </script>
</body>

放大镜

//css
.preview_img {
    position: relative;
    height: 398px;
    border: 1px solid #ccc;
}
.preview_img>img {
    width: 400px;
}
.mask {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    width: 300px;
    height: 300px;
    background: #FEDE4F;
    opacity: .5;
    border: 1px solid #ccc;
    cursor: move;
}
.big {
    display: none;
    position: absolute;
    left: 410px;
    top: 0;
    width: 500px;
    height: 500px;
    background-color: pink;
    z-index: 999;
    border: 1px solid #ccc;
    overflow: hidden;
}
.big img {
    position: absolute;
    top: 0;
    left: 0;
}

//body
<div class="preview_img">
    <img src="images/sj3.png" alt="" >
    <div class="mask"></div>
	<div class="big">
    	<img src="images/sj3.png" alt="" class="bigImg">
    </div>
</div>

//js
/* detail.js */
window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    // 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function() {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function() {
        mask.style.display = 'none';
        big.style.display = 'none';
    })
        // 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
    preview_img.addEventListener('mousemove', function(e) {
        // (1). 先计算出鼠标在盒子内的坐标
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // console.log(x, y);
        // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
        // (3) 我们mask 移动的距离
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;
        // (4) 如果x 坐标小于了0 就让他停在0 的位置
        // 遮挡层的最大移动距离
        var maskMax = preview_img.offsetWidth - mask.offsetWidth;
        if (maskX <= 0) {
            maskX = 0;
        } else if (maskX >= maskMax) {
            maskX = maskMax;
        }
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';
        // 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
        // 大图
        var bigIMg = document.querySelector('.bigImg');
        // 大图片最大移动距离
        var bigMax = bigIMg.offsetWidth - big.offsetWidth;
        // 大图片的移动距离 X Y
        var bigX = maskX * bigMax / maskMax;
        var bigY = maskY * bigMax / maskMax;
        bigIMg.style.left = -bigX + 'px';
        bigIMg.style.top = -bigY + 'px';

    })
})

元素可视区client系列

区别注意

offsetX和offsetY

offsetX:鼠标点击位置相对于触发事件对象的水平距离

offsetY:鼠标点击位置相对于触发事件对象的垂直距离

clientX和clientY

clientX:鼠标点击位置相对于浏览器可视区的水平距离(不会计算水平滚动的距离)

clientY:鼠标点击位置相对于浏览器可视区的垂直距离(不会计算垂直滚动条的距离)

screenX 和screenY

screenX和screenY

screenX:鼠标点击位置相对于电脑屏幕左上角的水平距离

screenY:鼠标点击位置相对于电脑屏幕左上角的垂直距离

pageX和pageY

pageX:clientX+X1(X1为水平滚动的距离)

pageY:clientY+Y1(Y1为垂直滚动的距离)

特别的

window.pageYOffset 返回页面被卷去的头部距离

window.pageXOffset 返回页面被卷去的左侧距离

立即执行函数

立即执行函数 ( function(){} ) ( ) 或者 ( function(){}() )

主要作用:

创建一个独立的作用域。 避免了命名冲突问题

代码演示:

<script>
// 1.立即执行函数: 不需要调用,立马能够自己执行的函数
function fn() {
    console.log(1);
}
fn();
// 2. 写法 也可以传递参数进来
// 1.(function() {})()    或者  2. (function(){}());
(function(a, b) {
    console.log(a + b);
    var num = 10;
})(1, 2); // 第二个小括号可以看做是调用函数
(function sum(a, b) {
    console.log(a + b);
    var num = 10; // 局部变量
}(2, 3));
// 3. 立即执行函数最大的作用就是 独立创建了一个作用域, 里面所有的变量都是局部变量 不会有命名冲突的情况
</script>

淘宝 flexible.js 源码分析

(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系列

1、scroll 概述

scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。

scroll案例作用
element.scrollTop返回被卷去的上侧距离,返回数值不带单位,可读写
element.scrollLeft返回被卷去的左侧距离,返回数值不带单位
element.scrollWidth返回自身实际的宽度,不含边框,返回数值不带单位
element.scrollHeight返回自身实际的高度,不含边框,返回数值不带单位

页面被卷去的头部:

如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll事件。

2、案例

scroll案例

   <title>scroll案例</title>
    <style>
        .slider-bar {
            position: absolute;
            left: 50%;
            top: 300px;
            margin-left: 600px;
            width: 45px;
            height: 130px;
            background-color: rgb(47, 36, 168);
        }
        a {
            text-decoration: none;
            color: rgb(136, 13, 13);
        }
        
        .w {
            width: 1200px;
            margin: 10px auto;
        }

        .header {
            height: 150px;
            background-color: rgb(120, 11, 27);
        }

        .banner {
            height: 250px;
            background-color: rgb(238, 206, 46);
        }

        .main {
            height: 1000px;
            background-color: yellowgreen;
        }

        span {
            display: none;
            position: absolute;
            bottom: 0;
        }
    </style>
</head>
<body>
    <div class="slider-bar">
        <span class="goBack">
            <a href="#">返回顶部</a>
        </span>
    </div>
    <div class="header w">头部区域</div>
    <div class="banner w">banner区域</div>
    <div class="main w">主体部分</div>

    <script>
        //1. 获取元素
        var sliderbar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
        var bannerTop = banner.offsetTop;
            // 当我们侧边栏固定定位之后应该变化的数值
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        // 获取main 主体元素
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2. 页面滚动事件 scroll
        document.addEventListener('scroll', function() {
            // console.log(11);
            // window.pageYOffset 页面被卷去的头部
            // console.log(window.pageYOffset);
            // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
            if (window.pageYOffset >= bannerTop) {
                sliderbar.style.position = 'fixed';
                sliderbar.style.top = sliderbarTop + 'px';
            } else {
                sliderbar.style.position = 'absolute';
                sliderbar.style.top = '300px';
            }
            // 4. 当我们页面滚动到main盒子,就显示 goback模块
            if (window.pageYOffset >= mainTop) {
                goBack.style.display = 'block';
            } else {
                goBack.style.display = 'none';
            }
        })
    </script>
</body>

电梯导航

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>电梯导航</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .aside {
            position: fixed;
            margin: 140px 10px;
            width: 80px;
        }

        .aside .aside1, .aside .aside2, .aside .aside3, .aside .aside4 {
            width: 80px;
            float: left;
            text-align: center;
            line-height: 40px;
            transition: all 100ms;
            cursor: pointer;
        }

        .active {
            background-color: red;
        }

        .content {
            margin: 100px auto;
            width: 800px;
            height: 2300px;
        }

        .content .content1, .content .content2, .content .content3, .content .content4 {
            float: left;
            width: 800px;
            height: 500px;
            font-size: 20px;
            text-align: center;
            line-height: 500px;
            color: #fcfc10;
        }

        .content1 {
            background-color: red;
        }
        .content2 {
            background-color: green;
        }
        .content3 {
            background-color: blue;
        }
        .content4 {
            background-color: black;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="aside">
            <div class="aside1 active">小红</div>
            <div class="aside2">小绿</div>
            <div class="aside3">小蓝</div>
            <div class="aside4">小黑</div>
        </div>
        <div class="content">
            <div class="content1">小红的内容</div>
            <div class="content2">小绿的内容</div>
            <div class="content3">小蓝的内容</div>
            <div class="content4">小黑的内容</div>
        </div>
    </div>

    <script>
        let aside = document.querySelector('.aside')
        let asides = aside.children
        let content = document.querySelector('.content')
        let contents = content.children

        for (let i = 0; i < asides.length; i++) {
            asides[i].addEventListener('click', function() {
                // 或者使用排他思想
                // for (let i = 0; i < asides.length; i++) {
                //     asides[i].classList.remove('active')
                // }
                document.querySelector('.aside .active').classList.remove('active')
                this.classList.add('active')

                // 内容盒子移动,页面滚动
                document.documentElement.scrollTop = contents[i].offsetTop
            })
        }
    </script>
</body>
</html>

页面被卷去的头部兼容性解决方案

需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:

  1. 声明了 DTD,使用 document.documentElement.scrollTop
  2. 未声明 DTD,使用 document.body.scrollTop
  3. 新方法 window.pageYOffset和 window.pageXOffset,IE9 开始支持
function getScroll() {
    return {
      left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
      top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    };
 } 
getScroll().left

三大系列总结

三大系列比较作用
e.offsetWidthh返回自身包括padding、边框、内容区的宽度, 返回数值不带单位
e.clientWidth返回自身包括padding、内容区域宽度,不含边框,不带单位
e.scrollWidth返回自身实际宽度,不含边框,不带单位

常见用法:

  1. offset经常用于获取元素位置:offsetLeft offsetTop
  2. client经常用于获取元素大小,clientWidth clientHeight
  3. scroll经常用于获取滚动距离scrollTop scrollLeft
  4. 注意页面滚动距离通过window.pageYOffset获取

mouseenter和mouseover的区别

1、mouseenter和mouseleave
  • 当鼠标移动到元素上时就会触发mouseenter 事件,mouseenter 只会经过自身盒子触发
  • mouseenter不会冒泡
  • 跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡
2、mouseover和mouseout
  • mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发
  • mouseover会冒泡

动画函数封装

1、动画实现原理

核心原理:通过定时器setInterval()不断移动盒子的位置

实现步骤

1、获得盒子当前的位置

2、让盒子在当前位置加上移动的距离(步长)

3、注意元素需要添加定位,才能使用element.style.left

4、利用定时器不断重复这个操作

5、加上一个结束定时器的条件

停止的条件:让当前盒子的位置等于目标位置就停止定时器(实质是删除定时器)

6、回调函数写的位置:定时器结束的位置,当动画执行结束后才去调用该该函数

案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动画</title>
    <style>
        div {
            position: absolute;
            width: 100px;
            height: 100px;
            background-color: #965b5b;
        }
        span {
            position: absolute;
            left: 0;
            top: 200px;
            width: 200px;
            height: 200px;
            background-color: #0ee978;
        }
    </style>
</head>
<body>
    <button>点击小绿才走</button>
    <div></div>
    <span>小绿</span>
    
    <script>
        //动画原理函数封装
        //性能优化,利用对象给不同的元素添加不同的定时器
        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, 500);
        btn.addEventListener('click', function() {
            animate(span, 200);
        })
    </script>
</body>
</html>
2、缓动效果原理

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

思路

①让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
②核心算法:(目标值-现在的位置)/ 10 作为每次移动的距离
③停止的条件是:让当前盒子位置等于目标位置就停止定时器

案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>缓动动画</title>
    <style>
        span {
            position: absolute;
            left: 0;
            top: 200px;
            width: 200px;
            height: 200px;
            background-color: #0ee978;
        }
    </style>
</head>
<body>
    <button class="btn500">点击小绿走到500</button>
    <button class="btn1000">点击小绿走到1000</button>
    <span>小绿</span>
    
    <script>
        //动画原理函数封装
        //性能优化,利用对象给不同的元素添加不同的定时器
        function animate(obj,target) {
            // 不断点击按钮,动画效果会越来越快,因为开启太多定时器了
            // 解决方案,让元素只有一个定时器执行

            // 清楚以前的定时器,只保留当前的
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                //步长公式
                // 把步长值改为整数,避免小数问题
                // var step = Math.ceil((target - obj.offsetLeft) / 10); //进一法,向上取整
                var step = (target - obj.offsetLeft) / 10;
                // 判断步长是否大于0,小于0向下取整
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if(obj.offsetLeft == target) {
                    // 停止动画定时器
                    clearInterval(obj.timer);
                }
                obj.style.left = obj.offsetLeft + step + 'px';
            },15);
        }

        var span = document.querySelector('span');
        var btn500 = document.querySelector('.btn500');
        var btn1000 = document.querySelector('.btn1000');
        btn500.addEventListener('click', function() {
            animate(span, 500);
        });
        btn1000.addEventListener('click', function() {
            animate(span, 1000);
        })
    </script>
</body>
</html>
3、动画函数添加回调函数

回调函数原理:函数可以作为一个参数,将这个函数作为一个参数传导另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调

案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>缓动动画</title>
    <style>
        span {
            position: absolute;
            left: 0;
            top: 200px;
            width: 200px;
            height: 200px;
            background-color: #0ee978;
        }
    </style>
</head>
<body>
    <button class="btn500">点击小绿走到500</button>
    <button class="btn1000">点击小绿走到1000</button>
    <span>小绿</span>
    
    <script>
        //动画原理函数封装
        //性能优化,利用对象给不同的元素添加不同的定时器
        function animate(obj, target, callback) {
            // 不断点击按钮,动画效果会越来越快,因为开启太多定时器了
            // 解决方案,让元素只有一个定时器执行

            // 清楚以前的定时器,只保留当前的
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                //步长公式
                // 把步长值改为整数,避免小数问题
                // var step = Math.ceil((target - obj.offsetLeft) / 10); //进一法,向上取整
                var step = (target - obj.offsetLeft) / 10;
                // 判断步长是否大于0,小于0向下取整
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if(obj.offsetLeft == target) {
                    // 停止动画定时器
                    clearInterval(obj.timer);
                    // 回调函数写到定时器结束位置
                    if (callback) {
                        // 调用函数
                        callback();
                    }
                }
                obj.style.left = obj.offsetLeft + step + 'px';
            },15);
        }

        var span = document.querySelector('span');
        var btn500 = document.querySelector('.btn500');
        var btn1000 = document.querySelector('.btn1000');
        btn500.addEventListener('click', function() {
            animate(span, 500);
        });
        btn1000.addEventListener('click', function() {
            animate(span, 1000, function() {
                span.style.backgroundColor = 'red';
            });
        })
    </script>
</body>
</html>
4、动画函数封装到单独js文件

动画函数的使用较为频繁,将其封装到一个独立js文件需要时直接引用

<script>
    var sliderbar = document.querySelector('.sliderbar');
    var con = document.querySelector('.con');
    var span = document.querySelector('span');
    var conLeft = con.offsetLeft;
 
    span.addEventListener('mouseenter', function () {
        animate(con, con.offsetLeft - con.offsetWidth, function () {
            sliderbar.children[0].innerHTML = '→';
        });
    })
    span.addEventListener('mouseout', function() {
        animate(con, conLeft, function () {
            sliderbar.children[0].innerHTML = '←';
        });
    })
</script>
5、节流阀

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

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

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

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

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

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

6、案例

轮播图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>轮播图</title>
    <style>
        .lunbo li a img{
            width: 850px;
            height: 450px;
        }
        .lunbo li{
            float: left;
        }
        .lunbo{
            position: absolute;
            top: 0;
            left: 0;
            margin: 0;
            padding: 0;
            width: 600%;
            /* 父盒子比ul小,一行显示不完,所以不能浮动,必须设置ul的宽度足够宽 */
        }
        .focus{
            position: relative;
            width: 850px;
            height: 450px;
            background-color: purple;
            margin: 100px auto;
            overflow: hidden;
        }
        .arrow-l,.arrow-r{
            display: none;
            position: absolute;
            top: 50%;
            margin-top: -20px;
            width: 24px;
            height: 40px;
            background: rgba(0, 0, 0, .3);
            text-align: center;
            text-decoration: none;
            line-height: 40px;
            color: #fff;
            font-family: 'icomoon';
            font-size: 18px;
            z-index: 2;
        }
        .arrow-r{
            right: 0;
        }
        .circle li{
            float: left;
            width: 8px;
            height: 8px;
            border: 2px solid rgba(255, 255, 255, 0.5);
            border-radius: 50%;
            margin: 0 3px;
            cursor: pointer;
        }
        .circle{
            position: absolute;
            bottom: 10px;
            left: 300px;
        }
        .focus img{
            width: 850px;
            height: 450px;
        }
        ul li{
            list-style: none;
        }
        .current{
            background-color: #fff;
        }
    </style>
</head>
<script src="js/JS.js"></script>
<body>
    <div class="focus f1">
        <!-- 左侧按钮 -->
        <a href="javascript:;" class="arrow-l">&lt;</a>
        <!-- 右侧按钮 -->
        <a href="javascript:;" class="arrow-r">&gt;</a>
        <!-- 核心滚动区域 -->
        <ul class="lunbo">
            <li>
                <a href="#"><img src="./images/bg1.png" alt="" class="image"></a>
            </li>
            <li>
                <a href="#"><img src="./images/bg2.png" alt=""></a>
            </li>
            <li>
                <a href="#"><img src="./images/bg3.png" alt=""></a>
            </li>
            <li>
                <a href="#"><img src="./images/bg4.png" alt=""></a>
            </li>
        </ul>
        <!-- 下面的小圆圈 -->
        <ul class="circle">
        </ul>
    </div>
</body>
</html>


window.onload = function() {
    //性能优化,利用对象给不同的元素添加不同的定时器
    function animate(obj, target, callback) {
        // 不断点击按钮,动画效果会越来越快,因为开启太多定时器了
        // 解决方案,让元素只有一个定时器执行

        // 清楚以前的定时器,只保留当前的
        clearInterval(obj.timer);
        obj.timer = setInterval(function() {
            //步长公式
            // 把步长值改为整数,避免小数问题
            // var step = Math.ceil((target - obj.offsetLeft) / 10); //进一法,向上取整
            var step = (target - obj.offsetLeft) / 10;
            // 判断步长是否大于0,小于0向下取整
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            if(obj.offsetLeft == target) {
                // 停止动画定时器
                clearInterval(obj.timer);
                // 回调函数写到定时器结束位置
                // if (callback) {
                //     // 调用函数
                //     callback();
                // }
                callback && callback();
            }
            obj.style.left = obj.offsetLeft + step + 'px';
        },15);
    }

    
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    var focusWidth = focus.offsetWidth;
    //鼠标经过就显示和隐藏
    focus.addEventListener('mouseenter',function(){
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
        clearInterval(timer);
        timer = null; // 清楚定时器变量
    })
    focus.addEventListener('mouseleave', function(){
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
        timer = setInterval(function(){
            //手动调用点击事件(直接调用右侧的点击事件)
            arrow_r.click();
        },2000)
    })
    //动态生成小圆圈
    var lunbo = focus.querySelector('.lunbo');
    var circle = focus.querySelector('.circle');
    // var index;
    for (var i = 0; i < lunbo.children.length; i++) {
        var li = document.createElement('li');
        // 自定义属性
        li.setAttribute('index', i);
        circle.appendChild(li);
        //排他思想绑定点击事件
        li.addEventListener('click', function() {
            for (var i = 0; i < circle.children.length; i++) {
               circle.children[i].className = '';
            //    circle.children[i].index=i;
        }
        this.className = 'current';
        var index = this.getAttribute('index');
        //当点击了li就要把当前的index值拿给num
        num = index; //全局变量
        //当点击了li就要把当前的index值拿给circle1
        circle1 = index; //全局变量
        // 调用函数
        animate(lunbo, - index * focusWidth);
        })
    }
    circle.children[0].className='current';

    //右侧箭头点击图片移动开始
    // 将第一个小li克隆一份放到轮播的最后
    var first = lunbo.children[0].cloneNode(true);
    lunbo.appendChild(first);
    var num = 0;
    var circle1 = 0; //控制小圆圈的播放
    //节流阀flag
    var flag = true;

    arrow_r.addEventListener('click', function() {
        if (flag) {
            flag = false; //关闭节流阀
            if(num == lunbo.children.length -1){
                lunbo.style.left = 0;
                num = 0;
            }
            num++;
            animate(lunbo, - num * focusWidth, function() {
                flag = true; //开启节流阀
            });
            circle1++;
            //如果circle1==4 说明走到了最后我们克隆的这张照片了 我们就复原
            if(circle1 == circle.children.length){
                circle1 = 0;
            }
            //先清除其余小圆圈的current类名
            for(var i = 0; i < circle.children.length; i++){
                circle.children[i].className = '';
            }
            //留下当前的小圆圈的current类名
            circle.children[circle1].className='current';
        }
    })

    //左侧箭头点击图片移动开始
    arrow_l.addEventListener('click', function() {
        if (flag) {
            flag = false; //关闭节流阀
            if(num == 0){
                num = lunbo.children.length - 1;
                lunbo.style.left = - num * focusWidth + 'px';
                
            }
            num--;
            animate(lunbo, - num * focusWidth, function() {
                flag = true; //开启节流阀
            });
            circle1--;
            //如果circle1<0 说明走到了第一张图片 
            if(circle1 < 0){
                circle1 = circle.children.length-1;
            }
            // circle1 = circle1 < 0 ? circle.children.length-1 : circle1;
            //先清除其余小圆圈的current类名
            for(var i = 0; i < circle.children.length; i++){
                circle.children[i].className = '';
            }
            //留下当前的小圆圈的current类名
            circle.children[circle1].className = 'current';
        }
    })

    //自动播放功能
    var timer = setInterval(function(){
        //手动调用点击事件(直接调用右侧的点击事件)
        arrow_r.click();
    },2000)
        
}

返回顶部

滚动窗口至文档中特定位置

window.scroll(x,y)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>scroll案例-返回顶部</title>
    <style>
        .slider-bar {
            position: absolute;
            left: 50%;
            top: 300px;
            margin-left: 600px;
            width: 45px;
            height: 130px;
            background-color: rgb(47, 36, 168);
        }
        a {
            text-decoration: none;
            color: rgb(136, 13, 13);
        }
        
        .w {
            width: 1200px;
            margin: 10px auto;
        }

        .header {
            height: 150px;
            background-color: rgb(120, 11, 27);
        }

        .banner {
            height: 250px;
            background-color: rgb(238, 206, 46);
        }

        .main {
            height: 1000px;
            background-color: yellowgreen;
        }

        span {
            display: none;
            position: absolute;
            bottom: 0;
        }
    </style>
</head>
<body>
    <div class="slider-bar">
        <span class="goBack">
            <a href="javascript:;">返回顶部</a>
        </span>
    </div>
    <div class="header w">头部区域</div>
    <div class="banner w">banner区域</div>
    <div class="main w">主体部分</div>

    <script>
        //1. 获取元素
        var sliderbar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
        var bannerTop = banner.offsetTop;
            // 当我们侧边栏固定定位之后应该变化的数值
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        // 获取main 主体元素
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2. 页面滚动事件 scroll
        document.addEventListener('scroll', function() {
            // console.log(11);
            // window.pageYOffset 页面被卷去的头部
            // console.log(window.pageYOffset);
            // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
            if (window.pageYOffset >= bannerTop) {
                sliderbar.style.position = 'fixed';
                sliderbar.style.top = sliderbarTop + 'px';
            } else {
                sliderbar.style.position = 'absolute';
                sliderbar.style.top = '300px';
            }
            // 4. 当我们页面滚动到main盒子,就显示 goback模块
            if (window.pageYOffset >= mainTop) {
                goBack.style.display = 'block';
            } else {
                goBack.style.display = 'none';
            }
        })
        goBack.addEventListener('click', function() {
            // window.scroll(0,0);
            animate(window, 0);
        });

            //性能优化,利用对象给不同的元素添加不同的定时器
        function animate(obj, target, callback) {
            // 不断点击按钮,动画效果会越来越快,因为开启太多定时器了
            // 解决方案,让元素只有一个定时器执行

            // 清楚以前的定时器,只保留当前的
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                //步长公式
                // 把步长值改为整数,避免小数问题
                // var step = Math.ceil((target - window.pageYOffset) / 10); //进一法,向上取整
                var step = (target - window.pageYOffset) / 10;
                // 判断步长是否大于0,小于0向下取整
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if(window.pageYOffset == target) {
                    // 停止动画定时器
                    clearInterval(obj.timer);
                    // 回调函数写到定时器结束位置
                    // if (callback) {
                    //     // 调用函数
                    //     callback();
                    // }
                    callback && callback();
                }
                // obj.style.left = window.pageYOffset + step + 'px';
                window.scroll(0, window.pageYOffset + step)
            },15);
        }
    </script>
</body>
</html>

筋斗云

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
        *{
            padding: 0;
            margin: 0;
        }
        .c-nav{
            position: relative;
            width: 1088px;
            margin: 0 auto;
            overflow: hidden;
            background-color: palegreen;
            opacity: .4;
        }
        ul {
            float: left;
            width: 100%;
        }
        li {
            float: left;
            padding: 0 20px;
            line-height: 40px;
            height: 40px;
            box-sizing: border-box;
            list-style: none;
            cursor: pointer;
        }
        a {
            text-decoration: none;
            color: black;
            cursor: pointer;
            line-height: 40px;
            height: 40px;
            font-size: 24px;
        }
        span {
            position: absolute;
            top: 3px;
            left: 0;
            width: 136px;
            height: 30px;
            background: url('images/bg1.png') no-repeat;
            z-index: -3;
        }
    </style>
    <script src="js/animate.js"></script>
</head>
<body>
    <div id="c_nav" class="c-nav">
        <span class="cloud"></span>
        <ul>
            <li class="current"><a href="">首页新闻</a></li>
            <li><a href="#">师资力量</a></li>
            <li><a href="#">活动策划</a></li>
            <li><a href="#">企业文化</a></li>
            <li><a href="#">招聘信息</a></li>
            <li><a href="#">公司简介</a></li>
            <li><a href="#">我是佩奇</a></li>
            <li><a href="#">啥是佩奇</a></li>
        </ul>
    </div>
    <script>
        var cloud = document.querySelector('.cloud');
        var c_nav = document.querySelector('.c-nav');
        var lis = c_nav.querySelectorAll('li');

        // 这个current作为筋斗云的起始位置
        var current = 0;
        for (var i = 0; i < lis.length; i++) {
            // 鼠标经过,把当前小li的位置作为目标值
            lis[i].addEventListener('mouseenter', function() {
                animate(cloud, this.offsetLeft);
            });
            // 鼠标离开,回到current
            lis[i].addEventListener('mouseleave', function() {
                animate(cloud, current);
            })
            // 当鼠标电机时,就把当前位置作为起始位置
            lis[i].addEventListener('click', function() {
                current = this.offsetLeft;
            })
        }
    </script>
</body>
</html>

移动端网页特效

触屏事件

1、触摸事件概述
触屏touch事件说明
touchstart手指触摸到一个DOM事件时触发
touchmove手指在一个DOM元素滑动时触发
touchend手指从一个DOM元素上移开时触发
2、触摸事件对象(TouchEvent)
触摸列表说明
touches正在触摸屏幕的所有手指的一个列表
targetTouches ☆正在触摸当前DOM元素上的手指的一个列表
changedTouches手指状态发生了改变的列表,从无到有,从有到无变化
3、clasList属性

clasList属性是HTML5新增属性,返回元素的类名,但只有ie10以上版本支持。得到伪数组形式,可以添加、移除和切换类名。

添加类名:element.classList.add(‘类名’); //追加类

移除类:element.classList.remove(‘类名’);

切换类:element.classList.toggle(‘类名’); // 存在则移除,不存在则添加

4、click延时解决方案

移动端click时间会有300ms的延时,原因是移动端屏幕双击会缩放(double tap to zoom)页面。

解决方案

1、禁用缩放。

<meta name="viewport" content="user-scalable=no">

2、利用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(){ // 执行代码 });

3、使用插件。fastclick插件解决300ms延迟。

网址:https://github/ftlabs/fastclick

案例

轮播图加返回顶部

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PC端轮播图</title>
    <style>
        .top {
            display: flex;
            /* 固定定位跟父级没有关系 以屏幕为准 */
            position: fixed;
            top: 0;
            z-index: 999;
        }
        .goBack {
            display: none;
            /*返回顶部的小箭头先隐藏,当页面滚动到某一部分的时候才显示*/
            position: fixed;
            bottom: 50px;
            right: 20px;
            width: 30px;
            height: 30px;
            border: 1px solid rgba(16, 8, 8, 0.2);;
            border-radius: 50%;
            background: url(images/back.png) no-repeat;
            background-size: 25px 25px;
            background-position: 2px;
        }

        .focus{
            position: relative;
            width: 350px;
            height: 180px;
            background-color: purple;
            margin: 100px auto;
            overflow: hidden;
        }

        .lunbo{
            position: absolute;
            top: 0;
            left: 0;
            margin: 0;
            margin-left: -100%;
            padding: 0;
            width: 600%;
            /* 父盒子比ul小,一行显示不完,所以不能浮动,必须设置ul的宽度足够宽 */
        }

        .lunbo li{
            float: left;
            width: 350px;
            height: 180px;
        }

        .lunbo li img{
            width: 350px;
            height: 180px;
        }

        .circle{
            position: absolute;
            bottom: 6px;
            right: 5px;
            margin: 0;
        }

        .circle li{
            display: inline-block;
            width: 6px;
            height: 4px;
            border: 1px solid rgba(255, 255, 255, 0.5);
            border-radius: 2px;
            margin: 0 1px;
            cursor: pointer;
            transition: all .3s;
        }

        .circle .current{
            width: 15px;
            background-color: #fff;
        }

        ul li{
            list-style: none;
        }
    </style>
    <script>
        window.addEventListener('load', function() {
            var focus = document.querySelector('.focus');
            var lunbo = document.querySelector('.lunbo');
            var circle = document.querySelector('.circle');
            
            // 1、获取focus宽度
            var w = focus.offsetWidth;
            // 2、利用定时器自动轮播图片
            var index = 0;
            var timer = setInterval(function() {
                index++;
                var translatex = -index * w;
                lunbo.style.transition = 'all .4s'; // 过渡效果
                lunbo.style.transform = 'translateX(' + translatex + 'px)';  // 移动效果
            }, 2000);

            // 过渡完成,再去判断 监听过渡完成事件 transitionend
            lunbo.addEventListener('transitionend', function() {
                // console.log(1);
                // 无缝滚动
                if (index >= 4) {
                    index = 0;
                     // 去掉过渡效果,瞬间跳 让lunbo快速跳到目标位置
                    lunbo.style.transition = 'none';
                    // 利用最新的索引号乘以宽度 去滚动图片
                    var translatex = -index * w;
                    lunbo.style.transform = 'translateX(' + translatex + 'px)';  // 移动效果
                } else if (index < 0) {
                    index = 3;
                    // 去掉过渡效果 让lunbo快速跳到目标位置
                    lunbo.style.transition = 'none';
                    // 利用最新的索引号乘以宽度 去滚动图片
                    var translatex = -index * w;
                    lunbo.style.transform = 'translateX(' + translatex + 'px)';  // 移动效果
                }

                // 3、小圆点更随变化,排他思想
                // 把circle中的current选出来,再去掉
                circle.querySelector('.current').classList.remove('current');
                // 让当前索引号 的li 加上 current
                circle.children[index].classList.add('current');
            });

            // 4、手指划动轮播图
            // 触摸,获取初始坐标
            var startX = 0;
            var moveX = 0; // 声明同一个全局变量,存放手指移动距离
            var flag = false; // 区别用户到底移动了没手指
            lunbo.addEventListener('touchstart', function(e) {
                startX = e.targetTouches[0].pageX;
                // 手指触摸的时候就停止计时器
                clearInterval(timer);
            });

            // 计算手指移动距离,并且移动盒子
            lunbo.addEventListener('touchmove', function(e) {
                // 计算机移动距离
                moveX = e.targetTouches[0].pageX - startX;
                // 移动盒子: 原来的位置 + 手指移动的距离
                var translatex = -index * w + moveX;
                // 手指拖动不需要过渡效果
                lunbo.style.transition = 'none';
                lunbo.style.transform = 'translateX(' + translatex + 'px)';  // 移动效果

                flag = true;  // 确定手指移动了
                e.preventDefault(); // 阻止手指默认滚动屏幕功能
            });

            // 手指离开 根据移动距离去判断是回弹还是播放上一张下一张
            lunbo.addEventListener('touchend', function(e) {
                if(flag) {
                    if (Math.abs(moveX) > 100) { //移动距离大于100像素
                        if (moveX > 0) { //如果向右滑,播放上一张,moveX为正
                            index--;
                        } else { //如果向左滑,播放下一张,moveX为负
                            index++;
                        }
                        var translatex = -index * w;
                        lunbo.style.transition = 'all .4s'; //过渡效果
                        lunbo.style.transform = 'translateX(' + translatex + 'px)';  // 移动效果
                    } else { // 小于100像素,回弹效果
                        var translatex = -index * w;
                        lunbo.style.transition = 'all .2s'; //过渡效果
                        lunbo.style.transform = 'translateX(' + translatex + 'px)';  // 移动效果
                    }
                }

                //手指离开,开启定时器
                clearInterval(timer); //清楚之前的,保证只有一个,防止越来越快
                // 开启
                timer = setInterval(function() {
                    index++;
                    var translatex = -index * w;
                    lunbo.style.transition = 'all .4s'; // 过渡效果
                    lunbo.style.transform = 'translateX(' + translatex + 'px)';  // 移动效果
                }, 2000);
            });

            // 返回顶部
            var goBack = document.querySelector('.goBack');
            var nav = document.querySelector('nav');
            window.addEventListener('scroll', function() {
                if (window.pageYOffset >= nav.offsetTop) {
                    goBack.style.display = 'block';
                } else {
                    goBack.style.display = 'none';
                }
            });
            goBack.addEventListener('click', function() {
                window.scroll(0, 0);
            });
        })
    </script>
</head>
<body>
    <div class="top">-------------------------------------------------------------</div>
    <!-- 返回顶部模块 -->
    <div class="goBack"></div>
    <!-- 轮播图 -->
    <div class="focus">
        <!-- 核心滚动区域 -->
        <ul class="lunbo">
            <li>
                <img src="./images/bg4.png" alt="">
            </li>
            <li>
                <img src="./images/bg1.png" alt="">
            </li>
            <li>
                <img src="./images/bg2.png" alt="">
            </li>
            <li>
                <img src="./images/bg3.png" alt="">
            </li>
            <li>
                <img src="./images/bg4.png" alt="">
            </li>
            <li>
                <img src="./images/bg1.png" alt="">
            </li>
        </ul>
        <!-- 下面的小圆圈 -->
        <ul class="circle">
            <li class="current"></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>

    <nav>--------------------------------------------------------------------------</nav>

    <div class="content">
        不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码(hashCode)应当相同。<br>
        Java 对于 eqauls 方法和 hashCode 方法是这样规定的:<br>(1)如果两个对象相同(equals 方法返回 true),那
        么它们的 hashCode 值一定要相同;<br>(2)如果两个对象的 hashCode 相同,它们并不一定相同。<br>当然,你未必要按照
        要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增加新元素
        的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。<br>
        关于 equals 和 hashCode 方法,很多 Java 程序员都知道,但很多人也就是仅仅知道而已,在 Joshua Bloch
        的大作《Effective Java》(很多软件公司,《Effective Java》、《Java 编程思想》以及《重构:改善既有代码质量》
        是 Java 程序员必看书籍,如果你还没看过,那就赶紧去买一本吧)中是这样介绍 equals 方法的。<br>
        首先 equals 方法必须满足自反性(x.equals(x)必须返回 true)、对称性(x.equals(y)返回 true 时,y.equals(x)
        也必须返回 true)、传递性(x.equals(y)和 y.equals(z)都返回 true 时,x.equals(z)也必须返回 true)和一致性(当
        x 和 y 引用的对象信息没有被修改时,多次调用 x.equals(y)应该得到同样的返回值),而且对于任何非 null 值的引
        用 x,x.equals(null)必须返回 false。<br>实现高质量的 equals 方法的诀窍包括:<br>1. 使用==操作符检查"参数是否为这个
        对象的引用";<br>2. 使用 instanceof 操作符检查"参数是否为正确的类型";<br>3. 对于类中的关键属性,检查参数传入对象
        的属性是否与之相匹配;<br>4. 编写完 equals 方法后,问自己它是否满足对称性、传递性、一致性;<br>5. 重写 equals 时
        总是要重写 hashCode;<br>6. 不要将 equals 方法参数中的 Object 对象替换为其他的类型,在重写时不要忘掉
        @Override 注解。<br>
    </div>
</body>
</html>

移动端常用开发插件

Swiper插件

网址:Swiper中文网-轮播图幻灯片js插件,H5页面前端开发

移动端视频插件

zy.media.js网址:https://github/ireaderlab/zyMedia

其他开发插件
  • superslide:http://www.superslide2/

  • iscroll:https://github/cubiq/iscroll

常用开发框架

框架概述

框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。

插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小。

前端常用的框架有Bootstrap、Vue、Angular、React等。既能开发PC端,也能开发移动端

前端常用的移动端插件有swiper、superslide、iscroll等。

框架:大而全,一整套解决方案

插件︰小而专一,某个功能的解决方案

本地存储

本地存储特性

1、数据存储在用户浏览器中

2、设置、读取方便、甚至页面刷新不丢失数据

3、容量较大,sessionStorage约5M、localStorage约20M

4、只能存储字符串,可以将对象用 JSON.stringify(复杂数据类型) 编码后存储,用 JSON.parse(JSON字符串) 取出

window.sessionStorage

1、生命周期为关闭浏览器窗口

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

3、以键值对的形式存储使用

存储数据

sessionStorage.setItem('key',value)

获取数据

sessionStorage.getItem('key')

删除数据

sessionStorage.removeItem('key')

删除所有数据

sessionStorage.clear()

window.localStorage

1、生命周期永久生效,除非手动删除,即使关闭页面也会存在

2、可以多窗口(页面)共享(同一个浏览器可以共享)

3、以键值对的形式存储使用

存储数据

localStorage.setItem('key',value)

获取数据

localStorage.getItem('key')

删除数据

localStorage.removeItem('key')

删除所有数据

localStorage.clear()

案例

记住用户名

    <input type="text" id="username"> <input type="checkbox" name="" id="remember"> 记住用户名
    <script>
        var username = document.querySelector('#username');
        var remember = document.querySelector('#remember');
        if (localStorage.getItem('username')) {
            username.value = localStorage.getItem('username');
            remember.checked = true;
        }
        remember.addEventListener('change', function() {
            if (this.checked) {
                localStorage.setItem('username', username.value);
            } else {
                localStorage.removeItem('username');            }
        })
    </script>

本文标签: JavaScriptWebAPIs