12个JS小练习&收获

116 阅读7分钟

一、瀑布流

1.效果图、代码、草稿

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>瀑布流</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        html {
            width: 100%;
            height: 100%;
        }

        #container {
            width: 656px;
            height: 798px;
            margin: 0 auto;
            /* background-color: aqua; */
            position: relative;
        }

        #container div {
            position: absolute;
            width: 100px;
        }

        #container div img {
            width: 100px;
        }
    </style>
</head>

<body>
    <div id="container">
    </div>
    <script>
        let data = [
            'd1A.png',
            'd2B.png',
            'd3C.png',
            'd4A.png',
            'd5C.png',
            'd1A.png',
        ]

        waterFull(3, 6)
        function waterFull(rowNum, colNum) {

            let topNum1 = 14;
            let topNum2 = 14;
            let topNum3 = 14;
            for (let i = 0; i < rowNum; i++) {
                console.log('aaaaaa')
                let leftNum = 8;
                for (let n = 0; n < colNum; n++) {
                    let odiv = document.createElement('div')
                    let mainBox = document.getElementById('container')
                    mainBox.appendChild(odiv)
                    //这个塞路径的方式不确定是否可行
                    odiv.innerHTML = `<img src="${data[n]}" class='imgA'>`

                    //分别确定盒子高度,使其参差不平
                    let photoArr = document.getElementsByClassName('imgA')
                    if (n === 0 || n === 3 || n === 5) {
                        photoArr[n].style.height = 250 + 'px'
                        odiv.style.top = topNum1 + 'px'

                    } else if (n === 1) {
                        photoArr[n].style.height = 200 + 'px'
                        odiv.style.top = topNum2 + 'px'

                    } else if (n === 2 || n === 4) {
                        photoArr[n].style.height = 150 + 'px'
                        odiv.style.top = topNum3 + 'px'

                    }

                    //给盒子赋上左定位
                    odiv.style.left = leftNum + 'px'
                    leftNum += 110

                }
                topNum1 = topNum1 + 264
                topNum2 = topNum2 + 214
                topNum3 = topNum3 + 164
            }
        }

        isLoading = false
        let a = 3
        window.onscroll = function () {
            // console.log("1111")
            var listHeight = container.offsetHeight
            var listTop = container.offsetTop

            // console.log(listHeight+listTop)

            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop

            var windowHeight = document.documentElement.clientHeight
            
            // console.log()
            if (isLoading) return
            if ((listHeight + listTop) - Math.round(windowHeight + scrollTop) < 50) {
                console.log("到底了")
                isLoading = true
                
                //渲染下一页数据
                setTimeout(function () {
                    waterFull(a, 6)
                    
                    isLoading = false //下一次到底事件继续触发
                }, 800)
                a += 3
                console.log(a)
            }
            
        }
    </script>
</body>

2.思路分析

(1)本次流程特点:先进行分析,直接根据分析写代码,最后改bug

(2)瀑布流

①先做第一行的图片,来确定container的宽高,子盒子的宽高和间距,为了做出瀑布流的效果,就必须有的图片长,有的图片短

②这种多行多列,而且有相同创建逻辑的,一般都是使用2个for循环创建的,外循环控制行数,内循环控制列数,为了便于下次创建不同行不同列的瀑布流,封装成了函数——这也给下方懒加载功能的实现创造了便利

③进一步思考如何做出瀑布流的布局样式——可以使用定位,通过改变每个图片的left和top来实现瀑布流的样式,同时找出left和top的变化规律,塞入循环之中

(3)懒加载

使用了b站视频中现成的代码。但是其中无限加载的功能需要进行替换,只要不断增加函数的行数就可以实现无限加载

3.难点分析

主要难点在于如何实现瀑布流图片参差不平的效果,有2点要素

①改变height ②改变top——因为height改变后需要再次调整top

我这里是用一个if语句判断,使不同列的图片有不同的高,每一行,每一列的top值都不一样,都在有规律的增加,要分别使用3个变量进行自增,而且是每次内循环结束后再自增,初始值必须放在外循环之外,如果在外循环内,那在末尾的变量自增将失效。

二、放大镜

1.效果图、代码、草稿

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>放大镜</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        #left {
            width: 600px;
            height: 400px;
            background-color: antiquewhite;
            margin-left: 20px;
            margin-top: 40px;
            float: left;
            position: relative;
            background-image: url(c.jpg);
            background-size: cover;
            overflow: hidden;
        }

        #mouseBox {
            width: 150px;
            height: 100px;
            background-color: rgba(14, 182, 78, 0.3);
            position: absolute;
            pointer-events: none;
        }

        #right {
            width: 600px;
            height: 400px;
            /* background-color: antiquewhite; */
            margin-left: 20px;
            margin-top: 40px;
            float: left;
            position: relative;
            /* display: none; */
            overflow: hidden;
        }

        #right img {
            position: absolute;

        }
    </style>
</head>

<body>
    <!-- 左边的原图片盒子 -->
    <div id="left" style="width: 600px;
    height: 400px;">
        <div id="mouseBox"></div>
    </div>
    <div id="right">
        <img src="c.jpg" id="img">
    </div>
    <script>
        left.onmouseover = function () {
            this.firstElementChild.style.display = "block"
            right.style.display = 'block'
        }
        left.onmouseleave = function () {
            this.firstElementChild.style.display = "none"
            right.style.display = 'none'
        }
        //下面做放大镜功能
        function bigger(multiple) {
            //获取放大后的图片宽高
            let currentWidth = left.clientWidth
            let currentHeight = left.clientHeight
            let biggerWidth = currentWidth * multiple
            let biggerHeight = currentHeight * multiple
            //把宽高赋给图片
            // let biggerImg = document.querySelector('#right img')
            // console.log(biggerImg)
            // biggerImg.setAttribute('style',`width:${biggerWidth};height:${biggerHeight}`)
            img.style.width = biggerWidth + 'px';
            img.style.height = biggerHeight + 'px';
            left.onmousemove = function (evt) {
                this.firstElementChild.style.left = evt.offsetX + -75 + "px"
                this.firstElementChild.style.top = evt.offsetY + -50 + "px"
                img.style.left = -(evt.offsetX - 75) * multiple + "px"
                img.style.top = -(evt.offsetY - 50) * multiple + "px"
            }
        }
        bigger(10)
    </script>
</body>

2.思路分析

(1)鼠标跟随(放大镜的镜)

通过evt.offsetX/Y来获取鼠标距离父元素的距离,然后对镜子和放大的图片进行相应的位移即可,当然,不要忘记给镜子添加 pointer-events: none; 不然会产生闪动

(2)放大(放大镜的放大)

通过left.clientWidth来获得照片的初始宽高,乘以相应倍数之后放进变量储存起来,核心是计算放大图片的left和top

三、动态表格

1.效果图、代码

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* 弹窗 (background) */
        .modal {
            display: none;
            /* 默认隐藏 */
            position: fixed;
            /* z-index: 1; */
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            overflow: auto;
            background-color: rgba(0, 0, 0, 0.4);
        }

        /* 弹窗内容 */
        .modal-content {
            position: relative;
            background-color: #fefefe;
            cursor: pointer;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding: 0;
            border: 1px solid #888;
            width: 30%;
            box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
            -webkit-animation-name: animatetop;
            -webkit-animation-duration: 0.4s;
            animation-name: animatetop;
            animation-duration: 0.4s
        }

        /* 添加动画 */
        @-webkit-keyframes animatetop {
            from {
                top: -300px;
                opacity: 0
            }

            to {
                top: 50%;
                opacity: 1
            }
        }

        @keyframes animatetop {
            from {
                top: -300px;
                opacity: 0
            }

            to {
                top: 50%;
                opacity: 1
            }
        }

        /* 关闭按钮 */
        .close {
            color: white;
            float: right;
            font-size: 28px;
            font-weight: bold;
        }

        .close:hover,
        .close:focus {
            color: #000;
            text-decoration: none;
            cursor: pointer;
        }

        .modal-header {
            padding: 2px 16px;
            background-color: #5cb85c;
            color: white;
        }

        .modal-body {
            padding: 2px 16px;
            text-align: center;
        }

        fieldset {
            text-align: center;
            width: 80%;
            position: relative;
            left: 50%;
            margin-left: -40%;
        }

        .modal-body div {
            width: 80%;
            display: inline-block;
        }

        td {
            text-align: center;
        }
    </style>
</head>

<body>
    <!-- 打开弹窗按钮 -->
    <button id="myBtn">创建新记录</button>

    <!-- 弹窗 -->
    <div id="myModal" class="modal">

        <!-- 弹窗内容 -->
        <div class="modal-content">
            <div class="modal-header">
                <span class="close">&times;</span>
                <center>
                    <h2>创建新记录</h2>
                </center>

            </div>
            <div class="modal-body">
                <div>
                    学号
                    <input id="schoolNum" value="">
                </div>
                <div>
                    姓名
                    <input id="name" value="">
                </div>
                <div>
                    性别
                    <input id="sex" value="">
                </div>
                <div>
                    二级学院
                    <input id="secondColl" value="">
                </div>
                <div>
                    班级
                    <input id="class" value="">
                </div>
                <div>
                    专业
                    <input id="major" value="">
                </div>
                <div>
                    辅导员
                    <input id="counsellor" value="">
                </div>
                <br>
                <button id="create">创建</button>
            </div>
        </div>
    </div>
    <center>

        <table id="table" border="1" width="80%" height="80%" cellspacing="0" cellpadding="0">
        </table>
    </center>
    <script>
        //创建弹窗
        //————————————————————————————————————————————————————————————
        // 获取弹窗
        var modal = document.getElementById('myModal');

        // 打开弹窗的按钮对象
        var btn = document.getElementById("myBtn");

        // 获取 <span> 元素,用于关闭弹窗 that closes the modal
        var span = document.getElementsByClassName("close")[0];

        // 点击按钮打开弹窗
        btn.onclick = function () {
            modal.style.display = "block";
        }

        // 点击 <span> (x), 关闭弹窗
        span.onclick = function () {
            modal.style.display = "none";
        }
        create.onclick = function () {
            modal.style.display = "none";
        }
        //——————————————————————————————————————————————————————————————
        //创建tr>创建td>创建数据数组>创建删除按钮>依次插入节点(创建表格)>设置单元格属性
        //——————————————————————————————————————————————————————————————
        let data = [
            {
                schoolNum: 23210501,
                name: '曹媛媛',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210502,
                name: '丁薇',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210503,
                name: '马婧',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210504,
                name: '孟嘉',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210505,
                name: '倪嘉琪',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210506,
                name: '',
                sex: '',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210507,
                name: '王晓冉',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210508,
                name: '王璇',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210509,
                name: '王璇',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
            {
                schoolNum: 23210510,
                name: '朱梦涵',
                sex: '女',
                secondColl: '计算机工程学院',
                class: 232105,
                major: '计算机科学与技术',
                counsellor: '冯雪冰',
            },
        ]

        //封装函数
        function createTable(data) {
            let arrNum = Object.keys(data).length;//获取对象数
            let oTable = document.getElementById('table')//获取table标签
            for (let i = 0; i < arrNum; i++) { //根据对象数确定行数
                let otr = document.createElement('tr'); //创建行
                oTable.appendChild(otr) //把行插入到table下
                for (let item in data[0]) { //对象特有的 for..in遍历
                    let otd = document.createElement('td'); //创建列
                    otr.appendChild(otd) //把列插入到行内
                    otd.innerHTML = data[i][item]//把对象中的key的value插入到单元格中,同时确保第一行的数据对应数组中第一个对象的数据
                }
                let otd2 = document.createElement('td'); //因为for in是对象的key有几个就循环几次,所以不能利用其来插入最后的删除单元格
                otr.appendChild(otd2); //把“删除”单元格插入到对应的行中
                otd2.innerHTML = `<button id="delateA"> 
                                      删除
                                  </button>`//这里本来是想用lastChild.innerHTML来插入按钮标签的,但是一直出问题,所以用了最为方便的``直接暴力插入
                if ((i % 2) != 0) { //用来设置未操作时的单元格背景颜色 ,之所以用!=0,因为i的初始值是0,意味着i为偶数时对应的才是奇数行
                    otr.setAttribute('style', 'background-color:black;color:white;')
                }
            }
        }

        createTable(data) //调用函数来进行初始单元格设置

        //删除按钮属性
        let allBtn = document.getElementsByTagName('button') //必须用getElementsBy...的方式来获取伪数组,querySelector和ById获得的都是第一个符合条件的标签

        for (let i = 2; i < allBtn.length; i++) { //给每个开始就有的“删除”按钮设置“删除”事件,因为开始的[0][1]按钮不是删除作用,所以设置i的初始值为2
            allBtn[i].onclick = handler
        }
        function handler() {
            this.parentNode.parentNode.remove() // 把删除按钮的爷爷删掉,也就是删除其所在的tr,因为内容都是嵌套在tr中,所以一起消失
            
            let trLength = document.getElementsByTagName('tr')//删除某行之后,可能会导致原来的奇偶顺序打乱,所以要重新设置背景颜色
            console.log(trLength[1])
            for (let i = 0; i < trLength.length; i++) {
                if ((i % 2) != 0) {
                    trLength[i].setAttribute('style', 'background-color:black;color:white;')
                } else { //给偶数行设置完颜色后,也要给奇数行设置(覆盖掉错误的颜色)
                    trLength[i].setAttribute('style', 'background-color:white;color:black;')
                }
            }
        }
        //添加按钮属性
        create.onclick = getValue
        function getValue() {
            let getValueA = document.getElementsByTagName('input')//获取弹窗表单中的所有input标签并返回伪数组

            let oTable = document.getElementById('table') //再次获得table标签,用于后面插入
            let otr = document.createElement('tr');
            for (let i = 0; i < getValueA.length; i++) {
                oTable.appendChild(otr)
                let otd = document.createElement('td');
                otr.appendChild(otd)
                otd.innerHTML = getValueA[i].value
            }
            let otd2 = document.createElement('td');
            otr.appendChild(otd2);
            otd2.innerHTML = `<button id="delateA">
                                      删除
                                  </button>`
            let o = 1;
            
            let allBtn = document.getElementsByTagName('button')
            for (let i = 1+o; i < allBtn.length; i++) {
                allBtn[i].onclick = handler
            }
            o++
            let trLength = document.getElementsByTagName('tr')
            console.log(trLength[1])
            for (let i = 0; i < trLength.length; i++) {
                console.log(i)
                if ((i % 2) != 0) {
                    trLength[i].setAttribute('style', 'background-color:black;color:white;')
                } else {
                    trLength[i].setAttribute('style', 'background-color:white;color:black;')
                }
            }

        }

        create.onmouseout = loseValue
        function loseValue() {
            let getValueA = document.getElementsByTagName('input')
            for (let i = 0; i < getValueA.length; i++) {
                getValueA[i].setAttribute('value', '')
            }
        }
    </script>
</body>

2.思路分析

(1)弹窗

通过添加和删除display:none;来弹出弹窗,同时给弹窗的盒子设置z-index使在弹窗存在时,只能点击弹窗中的内容

(2)表格

①初始表格

将数据以对象的形式放入数组之中,创建一个单元格就读取相应的数据填入其中(详见注释)

②删除按钮

在外循环末尾创建一个单元格,再把按钮插入其中(直接用innerHTML暴力插入),点击删除按钮会直接删除它的爷爷(也就是其所在的tr),然后将该行全部删除

③添加表格

获取input标签的伪数组,用getValueA[i].value来获取弹窗输入框中中输入的信息,经过循环,逐一插入到新行的单元格中

④黑白背景

在初始表格创建时设置一次背景,在删除行的时候设置一次,在添加行的时候设置一次,每次设置都要给奇数行和偶数行分别设置

3.难点分析

①使用for...in循环(对象特有的循环)来将数组的对象中的数据插入到相应的单元格中

②之所以偶数行的设置要用!=0因为在循环中i的初始值是0,所以奇数时才代表的偶数行

四、滚动弹幕

1.效果图、代码

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>滚动弹幕</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        #container {
            overflow: hidden;
            width: 1000px;
            height: 600px;
            background-color: antiquewhite;
            position: relative;
            margin: 0 auto;
            top: 100px;
        }

        #main {
            width: 1000px;
            height: 90%;
            background-color: aqua;
        }

        #bottom {
            width: 1000px;
            height: 10%;
            text-align: center;
        }

        #inputA {
            width: 400px;
            height: 40px;
            margin-top: 8px;
            font-size: 22px;
            padding-left: 6px;
            border-radius: 6px;
            font-family: '楷体';
        }

        #buttonA {
            width: 40px;
            height: 42px;
            position: relative;
            left: -4px;
            top: -3px;
            border-radius: 6px;
        }

        #main div {
            /* position: absolute; */
            border: 2px solid #aaaaaa;
            border-radius: 10px;
            width: 400px;
            padding-left: 10px;
            position: absolute;
            right: -412px;
            /* 412 */
        }

        @keyframes move {
            from {
                right: 0;
            }

            to {
                right: calc(100% + 200px);
            }
        }
    </style>
</head>

<body>
    <div id="container">
        <div id="main">
            <!-- <div id="line1">这也太好看了吧!!!</div>
            <div id="line2">真的吗兄弟,我真觉得一般般吧</div>
            <div id="line3">兄弟们,这个我是真喜欢</div>
            <div id="line4">南邮通达潘子轩实名开导</div>
            <div id="line5">阿巴阿巴</div>
            <div id="line6">这里竟然还有通达的?</div> -->
        </div>
        <div id="bottom">
            <input type="text" value="发个友善的弹幕见证当下" id="inputA">
            <button id="buttonA">发送</button>
        </div>
    </div>
    <script>
        //输入框的小功能——点击清除&自动恢复
        let clearText = document.getElementById("inputA")
        clearText.onclick = function () {
            clearText.setAttribute('value', '')
        }
        clearText.onmouseleave = function () {
            let timer = setTimeout(function () {
                clearText.setAttribute('value', '发个友善的弹幕见证当下')
            }, 2000)
        }
        //发送建功能——获取文字>储存文字>创建div>插入节点>插入文字>添加属性>添加动画
        buttonA.onclick = handler
        function handler() {
            //获取文字
            let inputValue = clearText.value;
            //console.log(inputValue);
            //创建div
            let valueDiv = document.createElement('div');
            let mainDiv = document.getElementById('main');
            //插入节点
            mainDiv.appendChild(valueDiv);
            //插入文字
            valueDiv.innerHTML = inputValue;
            //设置随机top值
            let topValue = getRnd2(0, 400);
            //console.log(topValue)
            valueDiv.style.top = topValue + 'px'
            //设置动画
            valueDiv.style.animation = "move " + (5 + Math.random() * 5) + "s linear infinite";
        }

        function getRnd2(min, max) {
            if (min > max) {
                console.error("参数有误")
                return
            }
            return Math.floor(Math.random() * (max - min + 1)) + min
        }

        //溢出清除盒子,降低内存负荷
    </script>
</body>

2.思路分析

先布置出弹幕的基本样式,然后获取input输入框中输入的value,将其赋到新创建的div中,当确定按钮点击后,随机设置div的top值,并给其绑定从右至左的动画

五、三级联动

1.效果图、代码

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>三级下拉</title>
</head>

<body>
    <form>
        省:
        <select id="sheng">
        </select>
        市:
        <select id="shi">
        </select> 
        区:
        <select id="qu">
        </select>
    </form>
    <script>
        //先创建装有省市区数据的数组,之后要导入到相应的option中
        //省
        let dataS = [
            '江苏省',
            '福建省',
        ]

        //市
        let dataS1 = [
            // '南京市',
            // '无锡市',
            // '徐州市',
            // '常州市',
            '苏州市',
            // '南通市',
            // '连云港市',
            // '淮安市',
            // '盐城市',
            '扬州市',
            // '镇江市',
            // '泰州市',
            // '宿迁市'
        ]

        let dataS2 = [
            '福州市',
            '厦门市'
            // '泉州市',
            // '漳州市',
            // '莆田市',
            // '宁德市',
            // '龙岩市',
            // '三明市',
            // '南平市'
        ]

        //区
        let dataJ1 = [
            '姑苏区',
            '虎丘区',
            '吴中区',
            '相城区',
            '吴江区',
        ]

        let dataJ2 = [
            '邗江区',
            '广陵区',
            '江都区',
        ]

        let dataF1 = [
            '辖鼓楼区',
            '台江区',
            '仓山区',
            '晋安区',
            '马尾区',
            '长乐区'
        ]

        let dataF2 = [
            '思明区',
            '海沧区',
            '湖里区',
            '集美区',
            '同安区',
            '翔安区'
        ]

        //封装一个导入数据的函数
        function dataImport(data, id) {
            for (let i = 0; i < data.length; i++) {
                let oOption = document.createElement("option");
                id.appendChild(oOption)
                oOption.innerHTML = data[i];
            }

        }
        //调用函数导入省的信息
        dataImport(dataS, sheng)
        dataImport(dataS1,shi)
        dataImport(dataJ1,qu)

        let os = document.getElementsByTagName('select')

        let idNum = 0;

        //封装一个函数:判断value并导入相应的市和区的数据
        function judgeImporter(index, id, data1, data2, data3) {
            if (os[index].selectedIndex === 0) {
                dataImport(data1, id)
                idNum = 1;
            } else if (os[index].selectedIndex === 1) {
                dataImport(data2, id)
                idNum = 2;
            }
        }

        function judgeImporter2(index, id, data1, data2, data3) {
            if (os[index].selectedIndex === 0) {
                dataImport(data1, id)
            } else if (os[index].selectedIndex === 1) {
                dataImport(data2, id)
            }
        }

        //封装一个函数:用来删除讨人厌的之前的数据
        function dataDelate(id) {
            let el = document.getElementById(id);
            let children1 = el.childNodes;
            for (var i = children1.length - 1; i >= 0; i--) {
                el.removeChild(children1[i])
            }
        }

        let proSel = document.getElementById('sheng')
        proSel.onchange = function () {
            dataDelate("shi")
            judgeImporter(0, shi, dataS1, dataS2)
        }
        let citSel = document.getElementById('shi')
        citSel.onchange = function () {
            dataDelate("qu")
            if (idNum === 1) {
                judgeImporter2(1, qu, dataJ1, dataJ2)
            } else if (idNum === 2) {
                judgeImporter2(1, qu, dataF1, dataF2)
            }

        }

    </script>
</body>

2.思路分析

分别将省、市、区的数据放入数组之中方便调用,先对表单进行初始化——默认value(江苏省,苏州市,虎丘区),假如要的碰巧就是这个那么就不需要改动,但是不是就会点击第一个省选项或是第二个市选项,这时候对前2个表单做改变检测即可,如果点击第一个,就要对第二个市和第三个区选项进行一次清除,然后再进行匹配相应的市和区并填充到第二、三表单中,如果点击第二个,保留第一个表单中的选项,对第三个区选项进行一次清除,再进行匹配导入即可

六、登录界面

1.效果图、代码

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        body,html {
            width: 100%;
            height: 100%;
        }
        section {
            width: 100%;
            height: 100%;
            background-color: antiquewhite;
            position: relative;
        }
        #container {
            width: 800px;
            height: 400px;
            background-color: white;
            position: absolute;
            left: 50%;
            margin-left: -400px;
            top:50%;
            margin-top: -200px;
            display: flex;
        }
        #photoBox {
            width: 50%;
            height: 80%;
            padding: 40px 20px;
            text-align: center;
            position: relative;
            background-color: aqua;
        }

        #signBox {
            width: 50%;
            height: 80%;
            padding: 40px 20px;
            position: relative;
            background-color: aqua;
        }
        #photoBox img {
            position: absolute;
            top:80px;
            left: 80px;
            width:367.5px;
            height: 225px;
        }
        #abab {
            position: absolute;
            top:80px;
            right: 80px;
            height: 205px;
            border: 1px solid black;
            border-radius: 5%;
            padding-top: 20px;
            background-color: aquamarine;
            text-align: left;
            padding-left: 16px;
            padding-right: 16px;            
        }
        #niCheng {
            width: 167px;
        }
        #name {
            width: 168px;
        }
        #num {
            width: 158px;
        }
        #phNum {
            width: 152px;
        }
        #niCheng {
            width: 167px;
        }
        #niCheng {
            width: 167px;
        }
        #key2 {
            width: 136px;
        }
    </style>
</head>

<body>
    <section>
        <div id="container">
            <div id="photoBox">
                <img src="a3.jpg">
            </div>
            <div id="signBox">
                <form id="abab">
                    <div id="niChengBox">
                        昵称
                        <input id="niCheng" type="text" value="n<=10" class="aa">
                    </div>
                    <div id="nameBox">
                        姓名
                        <input id="name" type="text" value="n<=4" class="aa">
                    </div>
                    <div id="numBox">
                        QQ号
                        <input id="num" type="text" value="5<n<10" class="aa">
                    </div>
                    <div id="phNumBox">
                        手机号
                        <input id="phNum" type="text" value="n=11" class="aa">
                    </div>
                    <div id="youXiangBox">
                        邮箱
                        <input id="youXiang" type="text" class="aa">
                    </div>
                    <div id="keyBox">
                        密码
                        <input id="key" type="text" class="aa" value="字母和数字,8<n<16">
                    </div>
                    <div id="keyBox2">
                        确认密码
                        <input id="key2" type="text" class="aa" value="字母和数字,8<n<16">
                    </div>
                    <button id="confirm1" class="aa">
                        确认
                    </button>
                    <button id="reset" class="aa">
                        重置
                    </button>
                </form>
            </div>
        </div>
    </section>
    <script>
        let round = document.getElementsByClassName("aa")
        for (let i = 0; i < round.length; i++) {
            round[i].onclick = function () {
                round[i].setAttribute("value", "  ")
            }
        }
        reset.onclick = function () {
            let sure2 = confirm('请确定是否重置')
            if (sure2 === true) {
                for (let i = 0; i < round.length; i++) {
                    console.log(round[i].value)
                    round[i].value = ""
                }
                alert('请重新输入')
            }else {
                alert('已取消')
            }
        }

        function isValid(str) { return /^\w+$/.test(str); }

        confirm1.onclick = function () {
            let sure1 = confirm('请确定是否提交') 
            if (sure1 === true) {

                let data = []
                for (let n = 0; n < round.length; n++) {
                    data[n] = round[n].value.trim().length;
                }
                console.log(data)

                if (data[0] > 1 && data[0] <= 10
                    && data[1] > 1 && data[1] <= 4
                    && data[2] > 5 && data[2] <= 10
                    && data[3] === 11
                    && data[5] > 8 && data[5] <= 15
                    && data[6] > 8 && data[6] <= 15) {
                    alert('已经成功提交')
                    if (isValid(data[5]) == false) {
                        alert('密码请输入英文和数字的组合')
                    }
                    if (data[5] != data[6]) {
                        alert('请确认两次密码输入一致')
                    }
                } else {
                    alert('输入无效,请重新输入')
                }
            }
        }

    </script>
</body>

2.流程分析

①先做一个简单的登录界面

②放置input表单和提交重置按钮

③效果1:输入框中有相应提示,点击后消失——初始有value,然后用循环把所有input都绑上点击事件,点击后把value设置为""即可

④效果2:提交按钮的条件判断——长度判断因为要判断的过多,先把去空长度放进数组中,再调用判断

是否由英文字母和数字组成用正则表达式判断,这里直接封装了一个简单的判断函数

⑤效果3:点击重置按钮后删除所有表单中信息,并重新展示提示信息

七、验证码

1.效果图、代码

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        section {
            width: 100%;
            height: 200px;
        }

        #container {
            width: 250px;
            height: 200px;
            background-color: aquamarine;
            position: relative;
            top: 100px;
            left: 600px;
            padding-top: 200px;
            padding-left: 20px;
            padding-right: 20px;
        }
    </style>
</head>

<body>
    <section>
        <div id="container">
            <input id="shuRuKuang" type="text" value="请输入验证码" name="aaa">
            <br>
            <button id="yanZheng">获得验证码</button>
            <br>
            <button id="anNiu">提交</button>
        </div>
    </section>

    <script>
        let num = false;
        yanZheng.onclick = function () {
            alert('验证码为:0505')
            let timerId = setTimeout(function () {
                yanZheng.removeAttribute('disabled', 'disabled')
                anNiu.removeAttribute('disabled', 'disabled')
            }, 60000);

            if (num === false) {
                console.log('我被禁用了')
                yanZheng.setAttribute('disabled', 'disabled')
                anNiu.setAttribute('disabled', 'disabled')
            }

            var countDownDate = new Date().getTime() + 60000;
            var x = setInterval(function () {
                var now = new Date().getTime(); // 获取当前时间的毫秒数
                var distance = countDownDate - now; // 计算剩余时间的毫秒数
                var seconds = Math.floor((distance % (1000 * 60)) / 1000); // 计算剩余秒数
                console.log(seconds); // 在控制台输出剩余秒数
                yanZheng.innerHTML = `${seconds}秒`
                if (distance < 0) { // 如果倒计时结束 
                    clearInterval(x); // 清除定时器 
                    yanZheng.innerHTML = '获得验证码'
                }
            }, 1000);
        }
        
        anNiu.onclick = function () {
            let code = document.getElementById('shuRuKuang').value
            console.log(code)
            if (code === '0505') {
                alert('输入成功')
            }
        }

    </script>
</body>

2.流程分析

“发送验证码”按钮:包含发送验证码提示,点击后禁用,禁用时显示一分钟倒计时

①用了绑定点击事件,点击后alert和添加属性disabled即可实现前两个功能

②倒计时

先获取当前时间的毫秒数用作计算(直接new Date()给出的是详细时间,放在间隔定时器后会每1秒获取一次进行计算,达到实时更新的效果),然后用反引号和innerHTML把剩余的秒数实时的插入到盒子中显示,最后如果剩余秒数为0就清除定时器并还原内容到”获得验证码“

八、返回顶部

1.效果图、代码

2023-11-12_10-20-04 00_00_00-00_00_30.gif

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        div {
            width: 100%;
            height: 800px;
            /* background-color: aquamarine; */
        }

        #btn {
            position: fixed;
            color: rgb(40, 39, 38);
            bottom: 0px;
            right: 0px;
        }

        .block {
            display: none;
        }
    </style>
</head>

<body>
    <div id="line1"></div>
    <button id="btn" class="block">返回顶部</button>
    <script>
        isLoading = false
        window.onscroll = function () {
            // console.log("1111")
            var listHeight = line1.offsetHeight
            var listTop = line1.offsetTop

            // console.log(listHeight+listTop)

            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop

            var windowHeight = document.documentElement.clientHeight
            // console.log(list.offsetTop,list.offsetHeight,windowHeight,scrollTop)
            // console.log()
            if (isLoading) return//解决距离底部[0,50]区间内重复触发的问题
            if ((listHeight + listTop) - Math.round(windowHeight + scrollTop) < 50) {
                console.log("到底了")
                isLoading = true
                btn.removeAttribute('class', 'block')
            } else {
                btn.setAttribute('class', 'block')
            }
        }
        btn.onclick = function () {
            let aa = document.documentElement.scrollTop
            console.log(aa)

            let timer = setInterval(() => {
                // 获取当前滚动高度
                const top = document.body.scrollTop || document.documentElement.scrollTop;
                // 设置滚动速度
                const speed = top / 10;
                if (document.body.scrollTop !== 0) {
                    document.body.scrollTop -= speed;
                } else {
                    document.documentElement.scrollTop -= speed;
                }
                if (top >= 0 && top <=2) {
                    clearInterval(timer)
                }
                console.log(top)
            }, 16)
        }
    </script>
</body>

2.流程分析

(1)到底出现返回顶部按钮

借用懒加载的到底判断,并给按钮添加display:block显示

(2)由快到慢返回顶部

设置一个speed,每次/10,越来越小,然后获取滚动条滚动距离,间隔定时器循环一次就-speed,造成由快到慢的效果

九、电子时钟

1.效果图、代码

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=, initial-scale=1.0">
    <title>刻度时钟</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        html {
            width: 100%;
            height: 100%;
        }

        #container {
            margin: 0 auto;
            width: 1200px;
            height: 200px;
            position: relative;
        }

        ul {
            list-style: none;
        }

        /* 刻度时钟 */
        .time-clock {
            width: 200px;
            height: 200px;
            border: 3px solid rgb(21, 121, 141);
            border-radius: 50%;
            margin: 50px;
            position: relative;
            top: 100px;
            left: 50%;
            margin-left: -100px;
        }

        /* 表盘 */
        .time-clock ul {
            width: 100%;
            height: 100%;
            position: relative;
        }

        .time-clock ul li {
            width: 2px;
            height: 4px;
            background: rgb(21, 121, 141);
            position: absolute;
            left: 50%;
            top: 0;
            transform-origin: center 100px;
        }

        #hour {
            width: 4px;
            height: 25px;
            background: rgb(8, 126, 139);
            position: absolute;
            left: 50%;
            top: 125px;
            margin: -50px 0 0 -3px;
            transform-origin: center bottom;
        }

        #minute {
            width: 3px;
            height: 40px;
            background: rgb(8, 126, 139);
            position: absolute;
            left: 50%;
            top: 140px;
            margin: -80px 0 0 -2px;
            transform-origin: center bottom;
        }

        #second {
            width: 2px;
            height: 60px;
            background: red;
            position: absolute;
            left: 50%;
            top: 160px;
            margin: -120px 0 0 -1px;
            transform-origin: center bottom;
        }

        #ball {
            width: 8px;
            height: 8px;
            background: black;
            position: absolute;
            left: 50%;
            top: 50%;
            border-radius: 50%;
            transform: translate(-50%, -50%);
        }

        #box {
            position: absolute;
            top: 400px;
            left: 50%;
            margin-left: -60px;
            font-size: 30px;
            text-align: center;
            border: 2px solid #aaaaaa;
            background-color: #aaaaaab9;
        }
    </style>
</head>

<body>
    <div id="container">
        <div id="box"></div>
        <div class="time-clock">
            <ul>
            </ul>
            <div id="hour"></div>
            <div id="minute"></div>
            <div id="second"></div>
            <div id="ball"></div>
        </div>
        <script>
            //刻度时钟
            let oul = document.querySelector(`ul`);
            let hour = document.querySelector(`#hour`);
            let minute = document.querySelector(`#minute`);
            let second = document.querySelector(`#second`);

            for (var i = 0; i < 60; i++) {
                var oli = document.createElement(`li`);
                oli.style.transform = `rotate(` + i * 6 + `deg)`;
                if (i % 5 == 0) {
                    oli.style.height = `9px`
                }
                oul.appendChild(oli);
            }
            //定时器
            function run() {
                var date = new Date();
                var currentHour = date.getHours();
                var currentMinute = date.getMinutes();
                var currentSecond = date.getSeconds();

                hour.style.transform = `rotate(` + (currentHour * 30 + currentMinute / 2) + `deg)`
                minute.style.transform = `rotate(` + currentMinute * 6 + `deg)`
                second.style.transform = `rotate(` + currentSecond * 6 + `deg)`
            }
            run();

            setInterval(run, 1000);

            setInterval(function () {
                let currentDate = new Date();
                let currentHour = currentDate.getHours()
                if (currentHour <= 9)
                    currentHour = "0" + currentHour;
                let currentMinute = currentDate.getMinutes()
                if (currentMinute <= 9)
                    currentMinute = "0" + currentMinute;
                let currentSecond = currentDate.getSeconds()
                if (currentSecond <= 9)
                    currentSecond = "0" + currentSecond;
                box.innerHTML = `${currentHour}:${currentMinute}:${currentSecond}`
            }, 1000)
        </script>
</body>

2.流程分析

(1)数字时钟:数字时钟很好做,获取当前时间的时、分、秒,再用反引号插入到盒子中即可

(2)刻度时钟:先做一个大盒子放在最外面,用border-radius=40%画出表框,刻度有小时刻度和分钟刻度,总共60个,所以做一个60次的循环,逢5就赋上更大的height制造小时刻度,最后用旋转来做表针

十、选项卡

1.效果图、代码

2023-11-12_10-48-37 00_00_00-00_00_30.gif

    <style>
        *{
            padding: 0;
            margin: 0;
        }
        ul {
            list-style: none;
        }
        .header {
            display: flex;
            width: 500px;
        }
        .header li {
            flex: 1;
            height: 50px;
            line-height: 50px;
            text-align: center;
            border:1px solid black;
        }
        .box {
            position: relative;
        }
        .box li {
            position: absolute;
            left: 360px;
            top: 0;
            width: 1000px;
            height: 500px;
            background-color: antiquewhite;
            display: none;
        }
        .header .active {
            background-color: aquamarine;
        }
        .box .active {
            display:block;
        }
        #photobox{
            position: relative;
            width:490px;
            height: 300px;
            left:-50px;
            top:40px;
        }
        #photobox img{
            width:490px;
            height: 300px;
        }
        #textbox {
            position: relative;
            top:80px;
            left: -10px;
            font-size: 16px;
            font-family: '微软雅黑';
            text-align: center;
        }
    </style>
</head>
<body>
    <center>
        <ul class="header">
            <li class="active">英雄联盟</li>
            <li>王者荣耀</li>
            <li>明日方舟</li>
            <li>APEX Legend</li>
        </ul>
        <ul class="box">
            <li class="active">
                <div id="photobox">
                    <img src="a1.jpeg">
                </div>
                <div id="textbox">
                    <span>
                        《英雄联盟》(League of Legends,简称LOL)是由美国拳头游戏(Riot Games)开发、中国内地由腾讯游戏
                        <br>
                        代理运营的英雄对战MOBA竞技网游。游戏里拥有数百个个性英雄,并拥有排位系统、符文系统等特色系统。
                    </span>
                </div>
            </li>
            <li>
                <div id="photobox">
                    <img src="a2.png">
                </div>
                <div id="textbox">
                    <span>
                        王者荣耀》是由腾讯游戏天美工作室群开发并运营在Android、IOS平台上的MOBA类国产手游,于2015年11月26日
                        <br>
                        在Android、iOS平台上正式公测,游戏曾经使用名称有《英雄战迹》、《王者联盟》。《王者荣耀》的外服版本为
                        <br>
                        《传说对决》(Arena Of Valor)。
                    </span>
                </div>
            </li>
            <li>
                <div id="photobox">
                    <img src="a3.jpg">
                </div>
                <div id="textbox">
                    <span >
                        《明日方舟》是一款魔物主题的策略手游。
                        <br>
                        在游戏中,玩家将管理一艘满载“ 魔物干员”的方舟,为调查来源神秘的矿石灾难而踏上旅途。
                    </span>
                </div>
            </li>
            <li>
                <div id="photobox">
                    <img src="a4.jpg">
                </div>
                <div id="textbox">
                    <span>
                        Apex Legends is a free-to-play hero shooter game 
                        <br>
                        where legendary competitors battle for glory, fame, and fortune on the fringes of the Frontier.
                    </span>
                </div>
            </li>
        </ul>
    </center>

    <script>
         //获取header的所有li,box的所有li
        let oHeaderItems = document.querySelectorAll(".header li")
        let oBoxItems = document.querySelectorAll(".box li")
        //给header的所有li绑事件
        for (let i = 0; i < oHeaderItems.length; i++) {
            //给每个li通过自定义属性添加索引,便于确定点击的是哪一个li
            oHeaderItems[i].dataset.index = i ;
            oHeaderItems[i].onclick = handler
        }
        console.log(oHeaderItems)
        function handler () {
            //无论点击哪个li都把所有的激活样式全部清空,等后面再重新赋上去就行
            for (var m = 0; m <oHeaderItems.length; m++) {
                oHeaderItems[m].classList.remove("active")
                oBoxItems[m].classList.remove("active")
            }
            //通过this来获取当前点击的对象的index索引,并赋值给变量记录下来
            let index = this.dataset.index;
            console.log(index)
            oHeaderItems[index].classList.add("active")
            oBoxItems[index].classList.add("active")
        }
    </script>
</body>

2.流程分析

核心是利用z-index覆盖,点击选项卡的时候清除所有的active标签,给当前所在的选项卡和对应的内容页赋上active

十一、轮播图

1.效果图、代码

2023-11-12_10-51-54 00_00_00-00_00_30.gif

2.流程分析

(1)轮播主体:在间隔定时器中不停地给图片设置z-index覆盖,i不断增加,如果到头了就使i归0重新开始

(2)悬浮暂停:鼠标进入关闭定时器并使两个左右切换按钮上浮

(3)左右切换:点击后清除所有的图片上浮属性和小圆点的active属性,往左或往右赋上上浮并相应的给小圆点赋active

(4)小圆点导航:和选项卡逻辑一致

3.自我批评

(1)轮播主体开始有很多bug,比如到最后页面时无法切回到第一张再次开始轮播,再比如无法做到丝滑轮播效果,虽然积极解决问题但是后者的问题还是没有达到解决,现在处于一种能用但是用的不舒服的情况

(2)没有封装函数,就是根据脑中的思路硬写,然后找bug解决bug,流程还不够清晰明了,代码看起来也很乱,没有头绪

(3)注释内容过少,重新看甚至自己都会想不清楚是怎么写的

十二、随机点名

1.效果图、代码

2023-11-12_11-07-33 00_00_00-00_00_30.gif

2.流程分析

核心就是随机点名:先获取需要进行点名的人选,放入一个数组中,然后用Math.random()*data.length获得一个随机数,再用反引号插入盒子,整体放入一个定时器,不停获取,不停展示,达到滚动展示的效果

十三、总结

1.不足

(1)开始的demo页面不够美观

(2)代码不够简洁,不够语义化(注释少,封装函数少,变量名称过于随意)

(3)对JS主要常用代码不够熟悉,在前几个demo编写时想一个小功能就要回看一下笔记,浪费时间

(4)逻辑不够清晰,很少能快速分析出功能并给出解决方案

2.收获

(1)12次实战练习后对常用的代码——元素属性获取,标签获取,对象、数组的操作等代码更加熟悉

(2)分析问题能力增强:确定了个人分析流程——先分析功能,再以画图加笔记的形式给出主体的代码框架,然后根据草稿搭建主体框架,接着填充细小的,优化用户体验的功能,最后试运行找bug并解决bug

(3)总结了常见的找bug的办法:①控制台报红的英文内容推断 ②console.log输出提示字符或者变量进行类断点测试 ③查看元素,观察元素的标签、内容、属性是否被添加或者是添加错误 ④上网搜索寻求帮助和启发