JavaScript - WebAPI - 案例 - 点名系统/打地鼠/计时器/今日代办

点名系统

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>点名系统</title>
    <style type="text/css">
        * {
            font-family: "微软雅黑";
            /*transition-duration: ;*/
        }

        h1,
        h2 {
            animation: changeColor 2s linear infinite;
            animation-direction: alternate;
        }

        h1 {
            background-color: yellowgreen;
            color: white;
            text-align: center;
        }

        #content>div {
            float: left;
            width: 100px;
            height: 50px;
            margin: 5px;
            font-size: 20px;
            line-height: 50px;
            text-align: center;
        }

        .cell {
            background-color: black;
            color: white;
        }

        .current {
            background-color: greenyellow;
            color: blueviolet;
        }

        button {
            display: inline-block;
            height: 50px;
            /*width: 50px;*/
            background-color: yellowgreen;
            color: white;
            font-size: 16px;
            margin: 10px;
        }

        select {
            display: inline-block;
            height: 30px;
            width: 100px;
            border: 1px solid yellowgreen;
            background-color: blanchedalmond;
            color: black;
            font-size: 14px;
            margin: 10px;
        }

        @keyframes changeColor {
            from {
                color: pink;
            }

            to {
                color: blueviolet;
            }
        }
    </style>
</head>

<body>
    <h1>点名系统</h1>
    <h2 id="selectTitle">被选中的小伙伴:</h2>
    <button onclick="go()">开始</button>
    时间:
    <select id="sele">
        <option value="10" selected>10</option>
        <option value="20">20</option>
        <option value="30">30</option>
        <option value="40">40</option>
        <option value="50">50</option>
    </select>

    <div id="content"></div>
</body>

<script src="../js/callnames2.js"></script>


</html>

// 定义数据
const names = ['成庆', '宋华全', '谭志成', '陈凝斌', '林培根', '杨镇', '詹兴杜', '连晓东', '邓旭湘', '麦云景', '石欣锜', '周维', '梁坤', '刘猛', '钟荣', '李昆泉', '龙志东', '蔡敏芳', '廖璐', '许国栋', '周裕涛', '孔创立', '黎炜扬', '蒋茂春', '袁谷音', '陈新裕', '张译文', '彭称枰', '周巧', '梁颖波', '尹美凤', '王浩', '黄家俊', '陈沛全', '麦婉莹', '刘明亮', '陈乐贤', '钟杰', '柯耿明', '张云云', '何育焯', '陈龙光', '周文杰', '张金波', '谢玉山', '梁仕俊', '李旭阳', '黄俊标', '刘家益', '陈光钰']

// 获取相关元素
const selectTitle = document.querySelector('#selectTitle')
const sele = document.querySelector('#sele')
const btn = document.querySelector('button')
const content = document.querySelector('#content')

// 动态生成数据列表
content.innerHTML = names.map(function (item) {
    return `<div class="cell">${item}</div>`
}).join('')

// 点击事件
function go() {
    // 干掉button的点击事件(防止二次点击)
    btn.onclick = null

    // 获取时间
    let time = sele[sele.selectedIndex].value - 0

    // 定义计数器(用来控制定时器执行的次数)
    let count = 0

    // 定义下标: 记录上一个随机过程产生的元素
    let index = -1      // 默认没有

    // 获取没有选中的元素
    const cells = content.querySelectorAll(':not(.current)')

    // 开启定时器: 100ms一次
    let timeId = setInterval(function () {
        // 计数器+1
        count++

        // 干掉上一次定时器随到的元素
        cells[index] && cells[index].classList.remove('current')

        // 产生新随机数
        index = Math.floor(Math.random() * cells.length)

        // 给随机选中的盒子添加类
        cells[index].classList.add('current')

        // 结束定时器
        if (count == time) {
            clearInterval(timeId)

            // 显示数据
            selectTitle.innerHTML += `&nbsp;${cells[index].innerText}`

            // 重新加上点击事件
            btn.onclick = go
        }
    }, 100)
}

打地鼠

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Whack A Mole!</title>
  <link href='reset.css' rel='stylesheet' type='text/css'>
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <h1>Whack-a-mole! <span class="score">0</span></h1>
  <p><button onClick="startGame()">Start</button></p>

  <div class="game">
    <div class="hole hole1">
      <div class="mole"></div>
    </div>
    <div class="hole hole2">
      <div class="mole"></div>
    </div>
    <div class="hole hole3">
      <div class="mole"></div>
    </div>
    <div class="hole hole4">
      <div class="mole"></div>
    </div>
    <div class="hole hole5">
      <div class="mole"></div>
    </div>
    <div class="hole hole6">
      <div class="mole"></div>
    </div>
  </div>

  <h2 class="game-status"></h2>

  <script src="../js/clobber.js"></script>
</body>

</html>
html {
  box-sizing: border-box;
  font-size: 10px;
  background: #ffc600;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  padding: 0;
  margin:0;
  font-family: 'Amatic SC', cursive;
}

h1, h2 {
  text-align: center;
  font-size: 10rem;
  line-height:1;
  margin-bottom: 0;
}

h2 {
  font-size: 8rem;
}

.score {
  background:rgba(255,255,255,0.2);
  padding:0 3rem;
  line-height:1;
  border-radius:1rem;
}

p {
  height: 5rem;
  text-align: center;
}

button {
  margin: 0 auto;
  width: 10rem;
  font-size: 3rem;
  font-family: 'Amatic SC', cursive;
  -webkit-appearance: none;
  appearance: none;
  font-weight: bold;
  background:rgba(255,255,255,0.2);
  border: 1px solid black;
  border-radius: 1rem;
}

.game {
  width:600px;
  height:400px;
  display:flex;
  flex-wrap:wrap;
  margin:0 auto;
}

.hole {
  flex: 1 0 33.33%;
  overflow: hidden;
  position: relative;
}

.hole:after {
  display: block;
  background: url(dirt.svg) bottom center no-repeat;
  background-size:contain;
  content:'';
  width: 100%;
  height:70px;
  position: absolute;
  z-index: 2;
  bottom:-30px;
}

.mole {
  background:url('mole.svg') bottom center no-repeat;
  background-size:60%;
  position: absolute;
  top: 100%;
  width: 100%;
  height: 100%;
  transition:all 0.4s;
}

.hole.up .mole {
  top:0;
}
// 获取元素
const score = document.querySelector('.score')
const start = document.querySelector('button')
const holes = document.querySelectorAll('.hole')

// 地鼠动画
function holeAnimate() {
    // 产生随机数
    let rand = Math.floor(Math.random() * holes.length)

    // 给对应的地鼠增加动画
    holes[rand].classList.add('up')

    // 增加延时器: 地鼠自动回去
    setTimeout(function () {
        holes[rand].classList.remove('up')
    }, 600)
}


// 开始游戏
function startGame() {
    // 清空历史数量
    score.innerHTML = 0

    // 隐藏按钮
    start.style.display = 'none'

    // 开启定时器
    let timeId = setInterval(holeAnimate, 400)

    // 指定时间关闭游戏
    setTimeout(function () {
        clearInterval(timeId)
        // 显示按钮
        start.style.display = 'block'
    }, 10000)
}

// 点击事件
holes.forEach(function (h) {
    h.firstElementChild.onclick = hClick
})

function hClick() {
    // 立即隐藏地鼠
    this.parentElement.classList.remove('up')

    // 被点击数量+1
    score.innerHTML++
}

计时器

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Countdown Timer</title>
  <link href='https://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
  <link rel="stylesheet" href="xome-countdown-timer.css">
</head>

<body>
  <div class="timer">
    <div class="timer__controls">
      <button data-time="20" class="timer__button">20s</button>
      <button data-time="300" class="timer__button">5分钟</button>
      <button data-time="900" class="timer__button"> 15分钟</button>
      <button data-time="1200" class="timer__button">20分钟</button>
      <button data-time="3600" class="timer__button">1个小时</button>
      <!-- <form name="customForm" id="custom"> -->
      <input type="text" name="minutes" placeholder="请输入任意分钟">
      <!-- </form> -->
    </div>
    <div class="display">
      <h1 class="display__time-left"></h1>
      <p class="display__end-time"></p>
    </div>
  </div>

  <script src="../js/countdown2.js"></script>
</body>

</html>
/* vietnamese */
@font-face {
  font-family: "Inconsolata";
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  src: url(QldgNThLqRwH-OJ1UHjlKENVzkWGVkL3GZQmAwLYxYWI2qfdm7Lpp4U8WRL2l2eY.woff2)
    format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
    U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: "Inconsolata";
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  src: url(QldgNThLqRwH-OJ1UHjlKENVzkWGVkL3GZQmAwLYxYWI2qfdm7Lpp4U8WRP2l2eY.woff2)
    format("woff2");
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: "Inconsolata";
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  src: url(QldgNThLqRwH-OJ1UHjlKENVzkWGVkL3GZQmAwLYxYWI2qfdm7Lpp4U8WR32lw.woff2)
    format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

html {
  box-sizing: border-box;
  font-size: 10px;
  background: #8e24aa;
  background: linear-gradient(45deg, #42a5f5 0%, #478ed1 50%, #0d47a1 100%);
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin: 0;
  text-align: center;
  font-family: "Inconsolata", monospace;
}

.display__time-left {
  font-weight: 100;
  font-size: 20rem;
  margin: 0;
  color: white;
  text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.05);
}

.timer {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
}

.timer__controls {
  display: flex;
}

.timer__controls > * {
  flex: 1;
}

.timer__controls form {
  flex: 1;
  display: flex;
}

.timer__controls input {
  flex: 1;
  border: 0;
  padding: 2rem;
}

.timer__button {
  background: none;
  border: 0;
  cursor: pointer;
  color: white;
  font-size: 2rem;
  text-transform: uppercase;
  background: rgba(0, 0, 0, 0.1);
  border-bottom: 3px solid rgba(0, 0, 0, 0.2);
  border-right: 1px solid rgba(0, 0, 0, 0.2);
  padding: 1rem;
  font-family: "Inconsolata", monospace;
}

.timer__button:hover,
.timer__button:focus {
  background: rgba(0, 0, 0, 0.2);
  outline: 0;
}

.display {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.display__end-time {
  font-size: 4rem;
  color: white;
}
.display__time-left::before {
  content: "倒计时间";
}
.display__end-time::before {
  content: "结束时间: ";
}
// 获取相关元素
const timer = document.querySelectorAll('.timer__button')
const left = document.querySelector('.display__time-left')
const end = document.querySelector('.display__end-time')
const minutes = document.querySelector('input')


// 点击事件
timer.forEach(function (item) {
    item.onclick = function () {
        // 取出值,转换成 分:秒 格式放到对应的为止
        let time = item.dataset.time - 0
        setTime(time)
    }
})

// 用户输入回车事件
minutes.onkeydown = function (e) {
    // 非回车事件: 结束
    if (e.keyCode != 13) {
        return
    }

    // 拿值
    let value = this.value.trim()
    if (value.length == 0) return

    // 数字有效性判定
    if (isNaN(value) || parseInt(value) != value || value < 0 || value > 60) {
        minutes.value = ''
        return
    }

    // 清理数据
    minutes.value = ''

    // 动态生成
    setTime(value * 60)

}

// 根据秒数动态生成时间信息
function setTime(time) {
    // 获取分
    let m = Math.floor(time / 60)
    // 获取秒
    let s = time % 60

    // 获取当前时间: 具体的小时和分钟以及秒钟
    let date = new Date()

    let nowH = date.getHours()
    let nowM = date.getMinutes()
    let nowS = date.getSeconds()

    // 判断: 加上当前时间后的逻辑
    nowM += m
    nowS += s

    // 判定是否超过60
    if (nowS > 59) {
        nowS -= 60
        nowM++
    }

    // 判定分钟
    if (nowM > 59) {
        nowH += Math.floor(nowM / 60)
        nowM %= 60
    }

    // 秒钟补0
    if (s < 10) s = '0' + s

    // 计算上午还是下午
    let half = 'am'
    if (nowH >= 12) {
        half = 'pm'
        nowH %= 12
    }

    // 前导0
    if (nowM < 10) nowM = '0' + nowM

    // 修改数据:left和end
    left.innerText = `${m}:${s}`
    end.innerText = `${nowH}:${nowM} ${half}`

    // 调用定时器工作
    go()
}

// 定时器处理
let timeId
function go() {
    // 先清理定时器
    timeId && clearInterval(timeId)

    timeId = setInterval(function () {
        // 取值: 筛选分钟和秒
        let time = left.innerText.trim().split(':').map(function (item) {
            return parseInt(item.trim())
        })
        // console.log(time)

        // 秒数改变(分钟跟着改变)
        time[1]--

        if (time[1] < 0) {
            time[1] = 59
            time[0]--
        }

        // 结束定时器
        if (time[0] == 0 && time[1] == 0) {
            clearInterval(timeId)
        }
        // 前导0补充
        if (time[0] < 10) time[0] = '0' + time[0]
        if (time[1] < 10) time[1] = '0' + time[1]

        // 赋值回元素
        left.innerText = `${time[0]}:${time[1]}`

    }, 10)
}

今日代办

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <link rel="icon" href="./favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite App</title>

  <link rel="stylesheet" href="./_assets/style.1ece50ac.css">

</head>

<body>
  <div id="app">
    <div class="box">
      <h1>待办列表</h1>
      <div class="tool">
        <input autofocus type="text" placeholder="请输入待办事项">
      </div>
      <ul></ul>
      <section>
        <span>0 未完成</span>
        <a href="javascript:;">清理 <b>0</b> 已完成</a>
      </section>
    </div>
  </div>
  <script src="../js/todo2.js"></script>
</body>

</html>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box
}

body {
    background-color: #ccc
}

ul {
    list-style: none
}

li {
    padding: 20px;
    text-align: left;
    font-size: 30px;
    border-bottom: 1px dashed #ccc;
    display: flex;
    justify-content: space-between;
    align-items: center
}

li input {
    margin-right: 10px
}

li button {
    display: none;
    padding: 5px
}

li:hover button {
    display: inline-block;
    cursor: pointer
}

.finished {
    text-decoration: line-through
}

h1 {
    margin-bottom: 10px
}

.box {
    background-color: #fff;
    width: 60vw;
    padding: 20px 20px 0;
    margin: 50px auto
}

.box .tool input {
    width: 100%;
    height: 50px;
    text-indent: 20px;
    font-size: 20px;
    font-style: italic;
    color: #666;
    font-weight: 700
}

section {
    height: 50px;
    display: flex;
    justify-content: space-between;
    align-items: center
}

a {
    text-decoration-color: #666;
    color: inherit
}```
// 1. 获取元素
const input = document.querySelector('#app input')
const undo = document.querySelector('#app span')
const clear = document.querySelector('#app b')

// 2. 回车输入待办事项
input.onkeydown = function (e) {
    // 2.1 判定: 回车有效
    if (e.keyCode != 13) return

    // 2.2 获取当前值
    let value = this.value.trim()
    // 数据已经拿到: 情况输入的值
    this.value = ''

    // 2.3 合法性判定: 不能为空
    if (value.length === 0) return

    // 2.4 读取本地存储可能有的其他数据
    const obj = local('undoList')

    // 2.5 有效性判定: 用户输入的,在之前不能存在:存在就不要了
    if (checkLocal(obj, value)) return

    // 2.6 有效数据: 存入对象
    obj.push({
        checked: false,     // 默认没完成
        value: value,       // 名字叫value,值用户输入
        finished: false     // 默认未完成
    })

    // 2.7 保存到本地存储
    local('undoList', obj)

    // 2.8 动态渲染数据
    show()
}

// 3. 用户点击事件: 事件委托(只操作本地存储数据,不改变页面规则)
document.querySelector('#app ul').onclick = function (e) {
    // 3.1 读取本地
    const obj = local('undoList')

    // 3.2 事件安全判定: 目标必须是input或者button
    // input: 选中不选中操作
    if (e.target.nodeName === 'INPUT') {
        // 获取下标, 在li中存着
        let index = e.target.parentNode.parentNode.dataset.index

        // 修改选中状态
        obj[index].checked = !obj[index].checked

        // 增加完成状态
        obj[index].finished = obj[index].checked ? 'finished' : 'false'

        // 修改数据
        local('undoList', obj)

        // 刷新页面显示
        show()

        // 结束事件
        return
    }

    // button: 删除事件
    if (e.target.nodeName === 'BUTTON') {
        // 获取下标
        let index = e.target.parentNode.dataset.index

        // 删除数据
        obj.splice(index, 1)

        // 修改数据
        local('undoList', obj)

        // 刷新页面显示
        show()

        // 结束事件
        return
    }
}

// 4. 清理数据
clear.parentElement.onclick = function () {
    // 4.1 取出数据
    const obj = local('undoList')

    // 4.2 遍历: 删除对应的数据
    for (let i = 0; i < obj.length; i++) {
        // 判定删除: 已经选中就删除
        if (obj[i].checked) {
            // 删除
            obj.splice(i, 1)

            // 防止后续跳过
            i--
        }
    }

    // 4.3 重新写入本地存储
    local('undoList', obj)

    // 4.4 渲染数据
    show()
}

/**
 * 根据数组对象渲染数据
 * 
 *  */
function show() {
    // 读取本地数据
    const obj = local('undoList')

    // 产生li, 拼接
    let str = ''

    // 定义未完成数量: 默认都没有完成
    let undoCount = obj.length

    for (let i in obj) {
        // 判定是否选中(采用html形式,因为此时js不能操作, 所以就给checkbox添加一个属性: 选中 checked, 没有选中 '')
        let checked = ''     // 默认不选中
        if (obj[i].checked) {
            checked = 'checked'

            // 减少未完成数量
            undoCount--
        }


        // 拼接结构: 使用自定义属性,表示第几个(方便后续事件)
        str += `
            <li data-index="${i}">
                <div>
                    <input type="checkbox" ${checked}>
                    <span class="${obj[i].finished}">${obj[i].value}</span>
                </div>
                <button>X</button>
            </li>
        `
    }

    // 渲染到页面
    document.querySelector('#app ul').innerHTML = str

    // 修改提示效果: 未完成 + 清理 已完成
    undo.innerText = `${undoCount} 未完成`
    clear.innerText = obj.length - undoCount
}
// 默认触发一次: 用户打开,看到历史数据
show()

/**
 * 封装函数: 做一个强大的本地存储功能
 * @param {存储名字} name
 * @param {值} value 
 */
function local(name, value) {
    // 如果name没有值: 结束
    if (name == undefined) return false

    // 尝试读取本地存储
    let obj = localStorage.getItem(name)

    // 将本地存储字符串转成对象: 注意没有的情况
    if (obj == null) obj = []
    else obj = JSON.parse(obj)

    // 2.1 用户没有给如value: 读取全部
    if (value == undefined) return obj

    // 2.2 用户给value值: 新增或者修改
    // 存回去
    localStorage.setItem(name, JSON.stringify(value))
}

/**
 * 验证数组对象中是否包含某个数据
 * @param {数组对象} obj
 * @param {字符串数据} value
*/
function checkLocal(obj, value) {
    // 遍历数组对象obj, 判定里面的值是否存在
    for (let i in obj) {
        if (obj[i].value == value) return true
    }

    // 如果都没有: 返回false
    return false
}```

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Henry_ww

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值