整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

如何用 160 行代码,实现动态炫酷的可视化图表?

如何用 160 行代码,实现动态炫酷的可视化图表?

者 | 前端劝退师

责编 | 伍杏玲

某天在逛社区时看到一帖子:

react-dynamic-charts — A React Library for Visualizing Dynamic Data

这是一个国外大佬在其公司峰会的代码竞赛中写的一个库:react-dynamic-charts,用于根据动态数据创建动态图表可视化。

它的设计非常灵活,允许你控制内部的每个元素和事件。使用方法也非常简单,其源码也是非常精炼,值得学习。

但因其提供了不少API,不利于理解源码。所以以下实现有所精简:

准备通用工具函数

1. getRandomColor:随机颜色

const getRandomColor==> {
const letters='0123456789ABCDEF';
let color='#';
for (let i=0; i < 6; i++) {
color +=letters[Math.floor(Math.random() * 16)]
}
return color;
};

2. translateY:填充Y轴偏移量

const translateY=(value)=> {

return `translateY(${value}px)`;

}


使用useState Hook声明状态变量

我们开始编写组件DynamicBarChart

const DynamicBarChart=(props)=> {
const [dataQueue, setDataQueue]=useState([]);
const [activeItemIdx, setActiveItemIdx]=useState(0);
const [highestValue, setHighestValue]=useState(0);
const [currentValues, setCurrentValues]=useState({});
const [firstRun, setFirstRun]=useState(false);
// 其它代码...
}

1. useState的简单理解:

const [属性, 操作属性的方法]=useState(默认值);

2. 变量解析

dataQueue:当前操作的原始数据数组

activeItemIdx: 第几“帧”

highestValue: “榜首”的数据值

currentValues: 经过处理后用于渲染的数据数组

firstRun: 第一次动态渲染时间

内部操作方法和对应useEffect

请配合注释使用:

// 动态跑起来~
function start {
if (activeItemIdx > 1) {
return;
}
nextStep(true);
}
// 对下一帧数据进行处理
function setNextValues {
// 没有帧数时(即已结束),停止渲染
if (!dataQueue[activeItemIdx]) {
iterationTimeoutHolder=;
return;
}
// 每一帧的数据数组
const roundData=dataQueue[activeItemIdx].values;
const nextValues={};
let highestValue=0;
// 处理数据,用作最后渲染(各种样式,颜色)
roundData.map((c)=> {
nextValues[c.id]={
...c,
color: c.color || (currentValues[c.id] || {}).color || getRandomColor
};

if (Math.abs(c.value) > highestValue) {
highestValue=Math.abs(c.value);
}

return c;
});

// 属性的操作,触发useEffect
setCurrentValues(nextValues);
setHighestValue(highestValue);
setActiveItemIdx(activeItemIdx + 1);
}
// 触发下一步,循环
function nextStep (firstRun=false) {
setFirstRun(firstRun);
setNextValues;
}

对应useEffect:

// 取原始数据
useEffect(=> {
setDataQueue(props.data);
}, );
// 触发动态
useEffect(=> {
start;
}, [dataQueue]);
// 设触发动态间隔
useEffect(=> {
iterationTimeoutHolder=window.setTimeout(nextStep, 1000);
return=> {
if (iterationTimeoutHolder) {
window.clearTimeout(iterationTimeoutHolder);
}
};
}, [activeItemIdx]);

useEffect示例:

useEffect(=> {
document.title=`You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

为什么要在 effect 中返回一个函数?

这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。

整理用于渲染页面的数据

const keys=Object.keys(currentValues);
const { barGapSize, barHeight, showTitle }=props;
const maxValue=highestValue / 0.85;
const sortedCurrentValues=keys.sort((a, b)=> currentValues[b].value - currentValues[a].value);
const currentItem=dataQueue[activeItemIdx - 1] || {};
  • keys: 每组数据的索引

  • maxValue: 图表最大宽度

  • sortedCurrentValues: 对每组数据进行排序,该项影响动态渲染。

  • currentItem: 每组的原始数据

开始渲染页面

大致的逻辑就是:

  1. 根据不同Props,循环排列后的数据:sortedCurrentValues

  2. 计算宽度,返回每项的label、bar、value

  3. 根据计算好的高度,触发transform。

<div className="live-chart">
{
<React.Fragment>
{
showTitle &&
<h1>{currentItem.name}</h1>
}
<section className="chart">
<div className="chart-bars" style={{ height: (barHeight + barGapSize) * keys.length }}>
{
sortedCurrentValues.map((key, idx)=> {
const currentValueData=currentValues[key];
const value=currentValueData.value
let width=Math.abs((value / maxValue * 100));
let widthStr;
if (isNaN(width) || !width) {
widthStr='1px';
} else {
widthStr=`${width}%`;
}

return (
<div className={`bar-wrapper`} style={{ transform: translateY((barHeight + barGapSize) * idx), transitionDuration: 200 / 1000 }} key={`bar_${key}`}>
<label>
{
!currentValueData.label
? key
: currentValueData.label
}
</label>
<div className="bar" style={{ height: barHeight, width: widthStr, background: typeof currentValueData.color==='string' ? currentValueData.color : `linear-gradient(to right, ${currentValueData.color.join(',')})` }} />
<span className="value" style={{ color: typeof currentValueData.color==='string' ? currentValueData.color : currentValueData.color[0] }}>{currentValueData.value}</span>
</div>
);
})
}
</div>
</section>
</React.Fragment>
}
</div>

定义常规propTypes和defaultProps

DynamicBarChart.propTypes={
showTitle: PropTypes.bool,
iterationTimeout: PropTypes.number,
data: PropTypes.array,
startRunningTimeout: PropTypes.number,
barHeight: PropTypes.number,
barGapSize: PropTypes.number,
baseline: PropTypes.number,
};

DynamicBarChart.defaultProps={
showTitle: true,
iterationTimeout: 200,
data: ,
startRunningTimeout: 0,
barHeight: 50,
barGapSize: 20,
baseline: ,
};

export {
DynamicBarChart
};

如何使用

import React, { Component } from "react";

import { DynamicBarChart } from "./DynamicBarChart";

import helpers from "./helpers";
import mocks from "./mocks";

import "react-dynamic-charts/dist/index.css";

export default class App extends Component {
render {
return (
<DynamicBarChart
barGapSize={10}
data={helpers.generateData(100, mocks.defaultChart, {
prefix: "Iteration"
})}
iterationTimeout={100}
showTitle={true}
startRunningTimeout={2500}
/>
)
}
}

1. 批量生成Mock数据

helpers.js:

function getRandomNumber(min, max) {
return Math.floor(Math.random * (max - min + 1) + min);
};

function generateData(iterations=100, defaultValues=[], namePrefix={}, maxJump=100) {
const arr=;
for (let i=0; i <=iterations; i++) {
const values=defaultValues.map((v, idx)=> {
if (i===0 && typeof v.value==='number') {
return v;
}
return {
...v,
value: i===0 ? this.getRandomNumber(1, 1000) : arr[i - 1].values[idx].value + this.getRandomNumber(0, maxJump)
}
});
arr.push({
name: `${namePrefix.prefix || ''} ${(namePrefix.initialValue || 0) + i}`,
values
});
}
return arr;
};

export default {
getRandomNumber,
generateData
}

mocks.js:

import helpers from './helpers';
const defaultChart=[
{
id: 1,
label: 'Google',
value: helpers.getRandomNumber(0, 50)
},
{
id: 2,
label: 'Facebook',
value: helpers.getRandomNumber(0, 50)
},
{
id: 3,
label: 'Outbrain',
value: helpers.getRandomNumber(0, 50)
},
{
id: 4,
label: 'Apple',
value: helpers.getRandomNumber(0, 50)
},
{
id: 5,
label: 'Amazon',
value: helpers.getRandomNumber(0, 50)
},
];
export default {
defaultChart,
}

一个乞丐版的动态排行榜可视化就做好喇。

完整代码

import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import './styles.scss';

const getRandomColor==> {
const letters='0123456789ABCDEF';
let color='#';
for (let i=0; i < 6; i++) {
color +=letters[Math.floor(Math.random() * 16)]
}
return color;
};

const translateY=(value)=> {
return `translateY(${value}px)`;
}

const DynamicBarChart=(props)=> {
const [dataQueue, setDataQueue]=useState([]);
const [activeItemIdx, setActiveItemIdx]=useState(0);
const [highestValue, setHighestValue]=useState(0);
const [currentValues, setCurrentValues]=useState({});
const [firstRun, setFirstRun]=useState(false);
let iterationTimeoutHolder=;

function start {
if (activeItemIdx > 1) {
return;
}
nextStep(true);
}

function setNextValues {
if (!dataQueue[activeItemIdx]) {
iterationTimeoutHolder=;
return;
}

const roundData=dataQueue[activeItemIdx].values;
const nextValues={};
let highestValue=0;
roundData.map((c)=> {
nextValues[c.id]={
...c,
color: c.color || (currentValues[c.id] || {}).color || getRandomColor
};

if (Math.abs(c.value) > highestValue) {
highestValue=Math.abs(c.value);
}

return c;
});
console.table(highestValue);

setCurrentValues(nextValues);
setHighestValue(highestValue);
setActiveItemIdx(activeItemIdx + 1);
}

function nextStep (firstRun=false) {
setFirstRun(firstRun);
setNextValues;
}

useEffect(=> {
setDataQueue(props.data);
}, );

useEffect(=> {
start;
}, [dataQueue]);

useEffect(=> {
iterationTimeoutHolder=window.setTimeout(nextStep, 1000);
return=> {
if (iterationTimeoutHolder) {
window.clearTimeout(iterationTimeoutHolder);
}
};
}, [activeItemIdx]);

const keys=Object.keys(currentValues);
const { barGapSize, barHeight, showTitle, data }=props;
console.table('data', data);
const maxValue=highestValue / 0.85;
const sortedCurrentValues=keys.sort((a, b)=> currentValues[b].value - currentValues[a].value);
const currentItem=dataQueue[activeItemIdx - 1] || {};

return (
<div className="live-chart">
{
<React.Fragment>
{
showTitle &&
<h1>{currentItem.name}</h1>
}
<section className="chart">
<div className="chart-bars" style={{ height: (barHeight + barGapSize) * keys.length }}>
{
sortedCurrentValues.map((key, idx)=> {
const currentValueData=currentValues[key];
const value=currentValueData.value
let width=Math.abs((value / maxValue * 100));
let widthStr;
if (isNaN(width) || !width) {
widthStr='1px';
} else {
widthStr=`${width}%`;
}

return (
<div className={`bar-wrapper`} style={{ transform: translateY((barHeight + barGapSize) * idx), transitionDuration: 200 / 1000 }} key={`bar_${key}`}>
<label>
{
!currentValueData.label
? key
: currentValueData.label
}
</label>
<div className="bar" style={{ height: barHeight, width: widthStr, background: typeof currentValueData.color==='string' ? currentValueData.color : `linear-gradient(to right, ${currentValueData.color.join(',')})` }} />
<span className="value" style={{ color: typeof currentValueData.color==='string' ? currentValueData.color : currentValueData.color[0] }}>{currentValueData.value}</span>
</div>
);
})
}
</div>
</section>
</React.Fragment>
}
</div>
);
};

DynamicBarChart.propTypes={
showTitle: PropTypes.bool,
iterationTimeout: PropTypes.number,
data: PropTypes.array,
startRunningTimeout: PropTypes.number,
barHeight: PropTypes.number,
barGapSize: PropTypes.number,
baseline: PropTypes.number,
};

DynamicBarChart.defaultProps={
showTitle: true,
iterationTimeout: 200,
data: ,
startRunningTimeout: 0,
barHeight: 50,
barGapSize: 20,
baseline: ,
};

export {
DynamicBarChart
};

styles.scss

.live-chart {
width: 100%;
padding: 20px;
box-sizing: border-box;
position: relative;
text-align: center;
h1 {
font-weight: 700;
font-size: 60px;
text-transform: uppercase;
text-align: center;
padding: 20px 10px;
margin: 0;
}

.chart {
position: relative;
margin: 20px auto;
}

.chart-bars {
position: relative;
width: 100%;
}

.bar-wrapper {
display: flex;
flex-wrap: wrap;
align-items: center;
position: absolute;
top: 0;
left: 0;
transform: translateY(0);
transition: transform 0.5s linear;
padding-left: 200px;
box-sizing: border-box;
width: 100%;
justify-content: flex-start;

label {
position: absolute;
height: 100%;
width: 200px;
left: 0;
padding: 0 10px;
box-sizing: border-box;
text-align: right;
top: 50%;
transform: translateY(-50%);
font-size: 16px;
font-weight: 700;
display: flex;
justify-content: flex-end;
align-items: center;
}

.value {
font-size: 16px;
font-weight: 700;
margin-left: 10px;
}

.bar {
width: 0%;
transition: width 0.5s linear;
}
}
}

原项目地址:

react-dynamic-charts:https://dsternlicht.github.io/react-dynamic-charts/

结语

一直对实现动态排行榜可视化感兴趣,无奈多数都是基于D3或echarts实现。而这个库,不仅脱离图形库,还使用了React 16的新特性。也让我彻底理解了React Hook的妙用。

声明:本文系作者投稿,版权归作者所有。

5G进入元年,物联网发展愈加火爆!

你是否身怀绝技、却无人知晓;别让你的IoT项目再默默无闻了!

继第一届AI优秀案例评选活动之后,2019年案例评选活动再度升级,CSDN将评选出TOP 30优秀IoT案例,赶快扫码参与评选吧!重磅福利,等你来领!

着大数据的发展,现在有很多企业和个人都开始关注于服务器,服务器的安全问题日渐成为一个大问题,防不胜防,一有闪失重要的资料就被病毒或是黑客破坏甚至于被窃取。所以做好服务器安全除了各种安全策略外,安装一款安全软件也非常重要,市面上的安全软件种类繁多,参差不齐,收费的,免费的等等,今天给大家引荐一款完全免费,功能齐全的安全软件----安全狗。

一、多引擎,精准查杀网页木马、各类病毒

独有的安全狗云查杀引擎、网马引擎与专业的二进制病毒引擎结合,精确查杀各类网页木马和主流病毒。多引擎智能查杀病毒,全面保障服务器安全。

二、三层网络防护实时保护网络安全

强有力的网络防护,实时监测IP和端口访问的合法性,实时拦截ARP攻击、Web攻击(抗CC)、DDOS攻击、暴力破解。

三、全面的文件/注册表保护杜绝非法篡改

全面开放保护规则,同时支持监控网站目录,有效保护重要文件、目录与注册表不被篡改与删除,实时防止网站被上传网页木马。系统默认规则与用户自定义规则全支持,灵活定制,全面保护。

四、底层驱动防护,屏蔽入侵提权

驱动级保护,拦截更快速,有效防止未授权的非法用户登录服务器,实时阻止非法创建和修改系统帐号。

五、服务器安全加固避免配置风险

全面的服务器体检,暴露安全隐患,当前系统健康情况了然于心,同时提供贴心的优化建议措施,加固服务器更简单,系统运行更快速,更安全。

六、服务器安全狗内嵌网站安全狗,让你的网站安心无忧。

服务器安全狗软件,专注服务器安全,老牌安全厂商出品值得信赖。

如需了解更多请访问官网:https://www.safedog.cn/server_safedog.html?showTinyTitle=true


五节:函数传参,改变Div任意属性的值

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函数传参,改变Div任意属性的值</title>
    <link rel="stylesheet" href="../css/common.css">
    <style>
        .wrap{
            width: 500px;
            margin: 0 auto;
            text-align: center;
        }
        label{
            display: block;
            margin: 10px;
        }
        button{
            margin-left: 10px;
        }
        .content{
            width: 200px;
            height: 200px;
            color: white;
            background: black;
            margin: 5px auto;
            padding: 10px;
            font-size: 14px;
            line-height: 20px;
            text-align: left;
        }
    </style>
</head>
<body>
<div class="wrap">
    <label>
        <span>属性名:</span>
        <input type="text" value="background"/>
    </label>
    <label>
        <span>属性值:</span>
        <input type="text" value="blue"/>
    </label>
    <button>确定</button>
    <button>重置</button>
    <p class="content">在上方输入“属性名”和“属性值”,点击确认按钮查看效果。</p>
</div>
<script>
    function change(elem, attr, value) {
        elem.style[attr]=value;
    }
    window.onload=function () {
        var inputs=document.getElementsByTagName("input");
        var btns=document.getElementsByTagName("button");
        var cons=document.getElementsByClassName("content");
        btns[0].onclick=function () {
            change(cons[0], inputs[0].value, inputs[1].value);
        };
        btns[1].onclick=function () {
            cons[0].style.cssText=" ";
            console.log(cons[0].style)
        }
    }
</script>
</body>
</html>

我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年我花了一个月整理了一份最适合2020年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。


第六节:图片列表:鼠标移入/移出改变图片透明度

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>图片列表:鼠标移入/移出改变图片透明度</title>
    <link rel="stylesheet" href="../css/common.css">
    <style>
        .wrap{
            width: 700px;
            margin: 10px auto;
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
        }
        .pics{
            flex-basis: 128px;
            height: 128px;
            opacity: 0.5;
            cursor: pointer;
            border: 1px solid #999;
            margin-top: 5px;
        }
    </style>
</head>
<body>
<div class="wrap">
    <div class="pics"><img src="../image/1.jpg" /></div>
    <div class="pics"><img src="../image/2.jpg" /></div>
    <div class="pics"><img src="../image/3.jpg" /></div>
    <div class="pics"><img src="../image/4.jpg" /></div>
    <div class="pics"><img src="../image/5.jpg" /></div>
    <div class="pics"><img src="../image/6.jpg" /></div>
    <div class="pics"><img src="../image/7.jpg" /></div>
    <div class="pics"><img src="../image/8.jpg" /></div>
    <div class="pics"><img src="../image/9.jpg" /></div>
    <div class="pics"><img src="../image/10.jpg" /></div>
</div>
<script>
    window.onload=function () {
        var pictures=document.getElementsByClassName("pics");
        for (var i=0; i < pictures.length; i++) {
            pictures[i].onmouseover=function () {
                this.style.opacity="1";
            };
            pictures[i].onmouseout=function () {
                this.style.cssText=" ";
            }
        }
    }
</script>
</body>
</html>

第七节:简易选项卡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>简易选项卡</title>
    <link rel="stylesheet" href="../css/common.css">
    <style>
        .wrap{
            width: 500px;
            margin: 10px auto;
        }
        .nav{
            background: black;
            height: 30px;
            display: flex;
            border: 1px solid black;
        }
        .nav>li{
            color: white;
            flex-basis: 60px;
            font-size: 14px;
            line-height: 30px;
            text-align: center;
        }
        .content{
            width: 100%;
            border: 1px solid black;
        }
        .content>li{
            font-size: 14px;
            line-height: 30px;
            margin-left: 10px;
        }
    </style>
</head>
<body>
<div class="wrap">
    <ul class="nav">
        <li class="item" style="background: #ccc">第一课</li>
        <li class="item">第二课</li>
        <li class="item">第三课</li>
    </ul>
    <ul class="content" style="display: block;">
        <li>网页特效原理分析</li>
        <li>响应用户操作</li>
        <li>提示框效果</li>
        <li>事件驱动</li>
        <li>元素属性操作</li>
        <li>动手编写第一个JS特效</li>
        <li>引入函数</li>
        <li>网页换肤效果</li>
        <li>展开/收缩播放列表效果</li>
    </ul>
    <ul class="content" style="display: none;">
        <li>改变网页背景颜色</li>
        <li>函数传参</li>
        <li>高重用性函数的编写</li>
        <li>126邮箱全选效果</li>
        <li>循环及遍历操作</li>
        <li>调试器的简单使用</li>
        <li>典型循环的构成</li>
        <li>for循环配合if判断</li>
        <li>className的使用</li>
        <li>innerHTML的使用</li>
        <li>戛纳印象效果</li>
        <li>数组</li>
        <li>字符串连接</li>
    </ul>
    <ul class="content" style="display: none;">
        <li>JavaScript组成:ECMAScript、DOM、BOM,JavaScript兼容性来源</li>
        <li>JavaScript出现的位置、优缺点</li>
        <li>变量、类型、typeof、数据类型转换、变量作用域</li>
        <li>闭包:什么是闭包、简单应用、闭包缺点</li>
        <li>运算符:算术、赋值、关系、逻辑、其他运算符</li>
        <li>程序流程控制:判断、循环、跳出</li>
        <li>命名规范:命名规范及必要性、匈牙利命名法</li>
        <li>函数详解:函数构成、调用、事件、传参数、可变参、返回值</li>
        <li>定时器的使用:setInterval、setTimeout</li>
        <li>定时器应用:站长站导航效果</li>
        <li>定时器应用:自动播放的选项卡</li>
        <li>定时器应用:数码时钟</li>
        <li>程序调试方法</li>
    </ul>
</div>
<script>
    window.onload=function () {
        var items=document.getElementsByClassName("item");
        var cons=document.getElementsByClassName("content");
        for (var i=0; i < items.length; i++) {
            items[i].index=i;
            items[i].onmouseover=function () {
                for (var n=0;n < items.length; n++) {
                    items[n].style.background="black";
                }
                this.style.background="#ccc";
                for (var m=0;m < cons.length;m++) {
                    cons[m].style.display="none";
                }
                cons[this.index].style.display="block"
            }
        }
    }
</script>
</body>
</html>

第八节:简易JS年历

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>简易JS年历</title>
    <link rel="stylesheet" href="../css/common.css">
    <style>
        .wrap{
            width: 300px;
            margin: 0 auto;
            background: #eaeaea;
            padding: 10px;
        }
        ul{
            width: 100%;
            display: flex;
            flex-wrap: wrap;
            justify-content: space-around;
        }
        ul>li{
            flex-basis: 70px;
            height: 70px;
            background: #424242;
            margin: 5px 0 5px 0;
            color: white;
            font-size: 25px;
            text-align: center;
            line-height: 35px;
            border: 1px solid black;
        }
        .current{
            background: white;
            color: #f69;
        }
        .content{
            background: #f1f1f1;
            border: 1px solid white;
            font-size: 14px;
            color: #666;
            padding: 10px;
        }
        .content>h2{
            line-height: 30px;
        }
        .content>p{
            line-height: 30px;
        }
    </style>
</head>
<body>
<div class="wrap">
    <ul>
        <li><strong>1</strong><br/>JAN</li>
        <li><strong>2</strong><br/>FER</li>
        <li><strong>3</strong><br/>MAR</li>
        <li><strong>4</strong><br/>APR</li>
        <li><strong>5</strong><br/>MAY</li>
        <li class="current"><strong>6</strong><br/>JUN</li>
        <li><strong>7</strong><br/>JUL</li>
        <li><strong>8</strong><br/>AUG</li>
        <li><strong>9</strong><br/>SEP</li>
        <li><strong>10</strong><br/>OCT</li>
        <li><strong>11</strong><br/>NOV</li>
        <li><strong>12</strong><br/>DEC</li>
    </ul>
    <div class="content">
        <h2>
            <strong>6</strong>月节日
        </h2>
        <p>端午节:6月4日至6日放假3天。</p>
    </div>
</div>
<script>
    window.onload=function () {
        var lis=document.getElementsByTagName("li");
        var con=document.getElementsByClassName("content")[0];
        var str=con.getElementsByTagName("strong")[0];
        var pCon=con.getElementsByTagName("p")[0];
        var oArray=[
            "元旦:1月1日至3日放假三天。",
            "春节:2月2日至8日放假7天。",
            "妇女节:3月8日妇女节,与我无关。",
            "清明节:4月3日至5日放假3天",
            "劳动节:4月30日至5月2日放假3天。",
            "端午节:6月4日至6日放假3天。",
            "小暑:7月7日小暑。不放假。",
            "七夕节:8月6日七夕节。不放假。",
            "中秋节:9月10日至12日放假3天。",
            "国庆节:10月1日至7日放假7天。",
            "立冬:11月8日立冬。不放假。",
            "艾滋病日:12月1日\n废除奴隶制国际日:12月2日。"
        ];
        for (var i=0;i < lis.length;i++) {
            lis[i].index=i;
            lis[i].onmouseover=function () {
                for ( var n=0;n < lis.length; n++) {
                    lis[n].className="";
                }
                this.className="current";
                str.innerHTML=this.index + 1;
                pCon.innerHTML=oArray[this.index];
            }
        }
    }
</script>
</body>
</html>

第九节:单一按钮显示/隐藏一播放列表收缩展开

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单一按钮显示/隐藏一播放列表收缩展开</title>
    <link rel="stylesheet" href="../css/common.css">
    <link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <style>
        .wrap{
            width: 250px;
            margin: 10px auto;
            border: 1px solid #ced3d7;
        }
        .nav{
            display: flex;
            justify-content: space-between;
            padding: 0 10px;
            height: 30px;
            line-height: 30px;
            background: #ced3d7;
            cursor: pointer;
            color: #6b7980;
            border: 1px solid white;
        }
        .nav>span{
            flex: 1;
            font-size: 14px;
        }
        ul>li>a{
            display: block;
            font-size: 14px;
            line-height: 30px;
            background: #e9edf2;
            padding: 0 10px;
            color: #6b7980;
        }
        ul>li>a:hover{
            background: white;
        }
    </style>
</head>
<body>
<div class="wrap">
    <h2 class="nav"><span>播放列表</span><i class="fa fa-caret-up"></i><i class="fa fa-caret-down" style="display: none;"></i></h2>
    <ul style="display: none;">
        <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>
    window.onload=function () {
        var hTwo=document.getElementsByTagName("h2")[0];
        var iS=hTwo.getElementsByTagName("i");
        var oUl=document.getElementsByTagName("ul")[0];
        var show=true;
        hTwo.onclick=function () {
            if (show) {
                oUl.style.display="block";
                iS[0].style.display="none";
                iS[1].style.display="block";
            } else {
                oUl.style.display="none";
                iS[0].style.display="block";
                iS[1].style.display="none";
            }
            show=!show;
        }
    }
</script>
</body>
</html>

第十节:提示框效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>提示框效果</title>
    <link rel="stylesheet" href="../css/common.css">
    <style>
        .wrap{
            width: 600px;
            margin: 10px auto;
            padding: 10px;
            border: 1px solid black;
        }
        h2{
            font-size: 14px;
            line-height: 20px;
            text-align: center;
        }
        ul{
            display: flex;
            width: 100%;
            justify-content: space-between;
            flex-wrap: wrap;
        }
        ul>li{
            flex-basis: 100px;
            height: 100px;
            border: 1px solid black;
            background: #f0f0f0;
            padding: 5px;
            margin: 5px 0;
            position: relative;
        }
        ul>li>a{
            font-size: 14px;
            color: #666666;
            text-decoration: none;
        }
        ul>li>img{
            display: none;
            z-index: 10;
            position: absolute;
            left: 50%;
            top: 50%;
            margin: -64px 0 0 -64px;
            border: 1px solid #666666;
        }
    </style>
</head>
<body>
<div class="wrap">
    <h2>名车车标展示-鼠标移过显示车标</h2>
    <ul>
        <li>
            <a href="#" title="BMW 宝马汽车"><strong>BMW</strong><br>马汽车</a>
            <img src="../image/1.jpg" alt="BMW 宝马汽车" />
        </li>
        <li>
            <a href="#" title="Alfa Romeo 阿尔法-罗米欧"><strong>Alfa Romeo</strong><br>阿尔法-罗米欧</a>
            <img src="../image/2.jpg" alt="Alfa Romeo 阿尔法-罗米欧" />
        </li>
        <li>
            <a href="#" title="Skoda 斯柯达"><strong>Skoda</strong><br>斯柯达</a>
            <img src="../image/3.jpg" alt="Skoda 斯柯达" />
        </li>
        <li>
            <a href="#" title="Volkswagen 大众汽车"><strong>Volkswagen</strong><br>大众汽车</a>
            <img src="../image/4.jpg" alt="Volkswagen 大众汽车" />
        </li>
        <li>
            <a href="#" title="Saab 萨布牌轿"><strong>Saab</strong><br>萨布牌轿车</a>
            <img src="../image/5.jpg" alt="Saab 萨布牌轿" />
        </li>
        <li>
            <a href="#" title="Lamborghini 兰博基尼"><strong>Lamborghini</strong><br>兰博基尼</a>
            <img src="../image/6.jpg" alt="Lamborghini 兰博基尼" />
        </li>
        <li>
            <a href="#" title="Porsche 保时捷"><strong>Porsche</strong><br>保时捷</a>
            <img src="../image/7.jpg" alt="Porsche 保时捷" />
        </li>
        <li>
            <a href="#" title="Peugeot 标致"><strong>Peugeot</strong><br>标致</a>
            <img src="../image/8.jpg" alt="Peugeot 标致" />
        </li>
        <li>
            <a href="#" title="Mercedes1 梅赛德斯 奔驰"><strong>Mercedes1</strong><br>梅赛德斯 奔驰</a>
            <img src="../image/9.jpg" alt="Mercedes1 梅赛德斯 奔驰" />
        </li>
        <li>
            <a href="#" title="Buick 别克汽车"><strong>Buick</strong><br>别克汽车</a>
            <img src="../image/10.jpg" alt="Buick 别克汽车" />
        </li>
    </ul>
</div>
<script>
    window.onload=function () {
        var liS=document.getElementsByTagName("li");
        var imgS=document.getElementsByTagName("img");
        for (var i=0; i < liS.length; i++) {
            liS[i].index=i;
            liS[i].onmouseover=function () {
                imgS[this.index].style.display="block";
            };
            liS[i].onmouseout=function () {
                imgS[this.index].style.display="none";
            };
        }
    }
</script>
</body>
</html>

第十一节:鼠标移过,修改图片路径

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>鼠标移过,修改图片路径</title>
    <link rel="stylesheet" href="../css/common.css">
    <style>
        body{
            background: black;
        }
        .wrap{
            width: 222px;
            border: 5px solid white;
            margin: 10px auto;
            background: white;
        }
        ul{
            display: grid;
            grid-template-columns: 1fr 1fr 1fr 1fr;
            background: black;
            padding: 5px;
        }
        ul>li{
            justify-self: center;
            align-self: center;
        }
        .item-1{
            grid-column: 1/4;
            grid-row: 1/4;
        }
        .item-2{
            grid-row: 1/2;
        }
        .item-3{
            grid-row: 2/3;
        }
        .item-4{
            grid-row: 3/4;
        }
        .loading{
            background: url("../image/loading.gif");
        }
    </style>
</head>
<body>
<div class="wrap">
    <ul>
        <li class="item-1"><img src="../image/big_1.jpg"><div class="loading"></div></li>
        <li class="item-2"><a href="#"><img src="../image/small_1.jpg"></a></li>
        <li class="item-3"><a href="#"><img src="../image/small_2.jpg"></a></li>
        <li class="item-4"><a href="#"><img src="../image/small_3.jpg"></a></li>
        <li class="item-5"><a href="#"><img src="../image/small_4.jpg"></a></li>
        <li class="item-6"><a href="#"><img src="../image/small_5.jpg"></a></li>
        <li class="item-7"><a href="#"><img src="../image/small_6.jpg"></a></li>
        <li class="item-8"><a href="#"><img src="../image/small_7.jpg"></a></li>
        <li class="item-9"><a href="#"><img src="../image/small_8.jpg"></a></li>
        <li class="item-10"><a href="#"><img src="../image/small_9.jpg"></a></li>
        <li class="item-11"><a href="#"><img src="../image/small_10.jpg"></a></li>
        <li class="item-12"><a href="#"><img src="../image/small_11.jpg"></a></li>
    </ul>
</div>
<script>
    window.onload=function () {
        var liS=document.getElementsByTagName("li");
        var imgS=document.getElementsByTagName("img");
        var oDiv=document.getElementsByTagName("div")[1];
        for (var i=1; i < imgS.length; i++) {
            imgS[i].index=i;
            imgS[i].onmouseover=function () {
                var img=new Image();
                img.src=imgS[0].src=this.src.replace(/small/, "big");
                oDiv.style.display="block";
                img.complete ? oDiv.style.display="none" : (imgS[0].onload=function() {
                    oDiv.style.display="none"
                });
            }
        }
    }
</script>
</body>
</html>

第十二节:复选框(checkbox)全选/全不选/返选