Showing preview only (436K chars total). Download the full file or copy to clipboard to get everything.
Repository: SUNNERCMS/30daysJavascript
Branch: master
Commit: 445ddc452c5c
Files: 112
Total size: 344.0 KB
Directory structure:
gitextract_hmq1lhif/
├── .gitattributes
├── 01 - JavaScript Drum Kit/
│ ├── README.md
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
├── 02 - JS and CSS Clock/
│ ├── README.md
│ ├── click.css
│ ├── clock.js
│ ├── clock1.js
│ ├── clock2.js
│ ├── index.html
│ └── style.css
├── 03 - CSS Variables/
│ ├── README.md
│ ├── index.html
│ ├── style.css
│ └── variables.js
├── 04 - Array Cardio Day 1/
│ ├── README.md
│ ├── index-FINISHED.html
│ └── index-START.html
├── 05 - Flex Panel Gallery/
│ ├── README.md
│ ├── animation.html
│ ├── index-FINISHED.html
│ └── index-START.html
├── 06 - Fetch、filter、正则表达式实现快速古诗匹配/
│ ├── .vscode/
│ │ └── launch.json
│ ├── README.md
│ └── index.html
├── 07 - Array Cardio Day 2/
│ ├── README.md
│ └── index.html
├── 08 - HTML5 Canvas 实现彩虹画笔绘画板/
│ ├── README.md
│ └── index.html
├── 09 - Console 调试各种姿势指南/
│ ├── README.md
│ └── index-FINISHED.html
├── 10 - JS 实现 Checkbox 中按住 Shift 的多选功能/
│ ├── README.md
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── sunzhaoxiang.html
├── 11 - 自定义视频播放器/
│ ├── README.md
│ ├── index.html
│ └── style.css
├── 12 - 键盘输入序列的验证指南/
│ ├── README.md
│ └── index.html
├── 13 - 图片随屏幕滚动而滑入滑出的效果/
│ ├── README.md
│ ├── index.html
│ ├── index.js
│ └── style.css
├── 14 - JavaScript 引用和值拷贝/
│ ├── README.md
│ ├── index-FINISHED.html
│ └── index-START.html
├── 15 - LocalStorage/
│ ├── README.md
│ ├── demo.html
│ ├── index.html
│ └── style.css
├── 16 - 移动鼠标让字体呈现彩虹效果/
│ ├── README.md
│ └── index.html
├── 17 - 数组排序/
│ ├── README.md
│ └── index.html
├── 18 - Day18 - Reduce、Map混合使用计算时分秒/
│ ├── README.md
│ └── index.html
├── 19 - Webcam Fun/
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── scripts.js
│ └── style.css
├── 20 - Speech Detection/
│ ├── README.md
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── package.json
├── 21 - Geolocation/
│ ├── README.md
│ ├── index.html
│ └── package.json
├── 22 - Follow Along Link Highlighter/
│ ├── README.md
│ ├── index.html
│ └── style.css
├── 23 - Speech Synthesis/
│ ├── 23 - Speech Synthesis/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── style.css
│ └── speak-easy-synthesis/
│ ├── index.html
│ ├── manifest.webapp
│ ├── script.js
│ └── style.css
├── 24 - Sticky Nav/
│ ├── README.md
│ ├── index.html
│ └── style.css
├── 25 - Event Capture, Propagation, Bubbling and Once/
│ ├── README.md
│ └── index.html
├── 26 - Stripe Follow Along Nav/
│ ├── README.md
│ └── index.html
├── 27 - Click and Drag/
│ ├── README.md
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
├── 28 - Video Speed Controller/
│ ├── .vscode/
│ │ └── settings.json
│ ├── README.md
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
├── 29 - Countdown Timer/
│ ├── .vscode/
│ │ └── settings.json
│ ├── README.md
│ ├── index.html
│ ├── scripts-FINISHED.js
│ ├── scripts-START.js
│ └── style.css
├── 30 - Whack A Mole/
│ ├── README.md
│ ├── index-FINISHED.html
│ ├── index-START.html
│ └── style.css
├── 31 - Canvas CountClock/
│ ├── Countdown.html
│ ├── Countdown.js
│ ├── digit.js
│ └── readme.md
├── LICENSE
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.css linguist-language=javascript
*.html linguist-language=javascript
*.java linguist-language=javascript
================================================
FILE: 01 - JavaScript Drum Kit/README.md
================================================
# Day1 JavaScript Drum Kit 中文指南
## 简介
第一天的练习是用JS制作一个爵士鼓的页面,通过敲击键盘上不同的字母,会发出不同的声音,并且页面上会伴随着敲击的动画。
效果如下:

想要实现以上效果,大致思路和解决方案如下:
- 检测到键盘上什么键被按下--监听`keydown`事件
- 在按键被按下的时候,播放音效--`audio.play()`
- 在按键被按下的同时,播放动画--`Element.classList.add('className')`
- 在动画结束后,移除动画,不然之后再点击不会有任何效果--`Element.classList.remove('className')`
## 基础语法
### 一些 ES6 语法
1. ``const`` :声明一个只读的常量,标识符的值只能赋值一次。
2. \`字符串 \${ 变量、属性名 } \`:模板字面量(Template literals)中用于表示模板字符串的标识。特点是字符串首尾用反引号(\`),内部的模板部分用 ${ } 括起来表示,具体请看[MDN文档]( https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings)。简单例子如下:
````javascript
var a = 1;
var b = 2;
//不用模板的写法
console.log("三是" + (a + b) + "不是" + (2 * a + b)); //"三是3不是4"
//使用模板字符串的写法
console.log(`三是${a + b}不是${2 * a + b}`); //"三是3不是4"
````
### ``forEach`` 与箭头函数
使用 ``document.querySelector`` 获取一组符合 CSS 选择符的元素快照,类型为 NodeList(此对象是对于文档的实时运行的动态查询),对其进行遍历时可采用 ``forEach`` 方法。
```javascript
// Code from http://es6-features.org/#StatementBodies
// ES6
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
})
// ES5
nums.forEach(function (v) {
if (v % 5 === 0)
five.push(v);
})
```
## 页面基础布局
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS Drum Kit</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="keys">
<div data-key="65" class="key">
<kbd>A</kbd>
<span class="sound">clap</span>
</div>
<div data-key="83" class="key">
<kbd>S</kbd>
<span class="sound">hihat</span>
</div>
<div data-key="68" class="key">
<kbd>D</kbd>
<span class="sound">kick</span>
</div>
<div data-key="70" class="key">
<kbd>F</kbd>
<span class="sound">openhat</span>
</div>
<div data-key="71" class="key">
<kbd>G</kbd>
<span class="sound">boom</span>
</div>
<div data-key="72" class="key">
<kbd>H</kbd>
<span class="sound">ride</span>
</div>
<div data-key="74" class="key">
<kbd>J</kbd>
<span class="sound">snare</span>
</div>
<div data-key="75" class="key">
<kbd>K</kbd>
<span class="sound">tom</span>
</div>
<div data-key="76" class="key">
<kbd>L</kbd>
<span class="sound">tink</span>
</div>
</div>
<audio data-key="65" src="sounds/clap.wav"></audio>
<audio data-key="83" src="sounds/hihat.wav"></audio>
<audio data-key="68" src="sounds/kick.wav"></audio>
<audio data-key="70" src="sounds/openhat.wav"></audio>
<audio data-key="71" src="sounds/boom.wav"></audio>
<audio data-key="72" src="sounds/ride.wav"></audio>
<audio data-key="74" src="sounds/snare.wav"></audio>
<audio data-key="75" src="sounds/tom.wav"></audio>
<audio data-key="76" src="sounds/tink.wav"></audio>
<script>
</script>
</body>
</html>
```
- <kbd> 标签定义键盘文本
说到技术概念上的特殊样式时,就要提到 <kbd> 标签。正如你已经猜到的,它用来表示文本是从键盘上键入的。
浏览器通常用等宽字体来显示该标签中包含的文本。
<kbd> 标签经常用在于计算机相关的文档和手册中。例如:
```text
键入 <kbd>quit</kbd> 来退出程序,或者键入 <kbd>menu</kbd> 来返回主菜单。
```
- 使用 data-* 属性来嵌入自定义数据
页面里通过data-key将页面展示的内容和audio关联起来。使用方法如下介绍:
```html
<ul>
<li data-animal-type="bird">Owl</li>
<li data-animal-type="fish">Salmon</li>
<li data-animal-type="spider">Tarantula</li>
</ul>
```
① data-* 属性用于存储页面或应用程序的私有自定义数据。
② data-* 属性赋予我们在所有 HTML 元素上嵌入自定义 data 属性的能力。
③ 属性名不应该包含任何大写字母,并且在前缀 "data-" 之后必须有至少一个字符
④ 属性值可以是任意字符串
**语法:**
```text
<element data-*="somevalue">
```
**属性值:**
|值|描述|
|:---------:|:---------:|
|somevalue|规定属性的值(以字符串)。|
- 按键键码对应表https://www.cnblogs.com/yiven/p/7118056.html
## 主要CSS代码
```css
html {
font-size: 10px;
background: url(http://i.imgur.com/b9r5sEL.jpg) bottom center;
background-size: cover;
}
body,
html {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.keys {
display: flex;
flex: 1;
min-height: 100vh;
align-items: center;
justify-content: center;
}
.key {
border: .4rem solid black;
border-radius: .5rem;
margin: 1rem;
font-size: 1.5rem;
padding: 1rem .5rem;
transition: all .07s ease;
width: 10rem;
text-align: center;
color: white;
background: rgba(0, 0, 0, 0.4);
text-shadow: 0 0 .5rem black;
}
.playing {
transform: scale(1.1);
border-color: #ffc600;
box-shadow: 0 0 1rem #ffc600;
}
kbd {
display: block;
font-size: 4rem;
}
.sound {
font-size: 1.2rem;
text-transform: uppercase;
letter-spacing: .1rem;
color: #ffc600;
}
```
主要属性有以下几个:
- `background-size:cover;`属性规定背景图像的尺寸。把背景图像扩展至足够大,以使背景图像完全覆盖背景区域。背景图像的某些部分也许无法显示在背景定位区域中。
- `html`中有一个样式为`font-size: 10px;`,在本案例中,`1rem`就是10px,rem是以html中的`font-size`为参照物,`1.2rem`就是`12px`。
- `transform: scale(1.1);`--该属性在键盘被点击时将该元素缩放至原来的1.1倍。
- `.key{border: .4rem solid black;} .playing{border-color: #ffc600;}`--这两条属性在按键点击的时候改变边框颜色。
- `.key{text-shadow: 0 0 .5rem black;} .playing{box-shadow: 0 0 1rem #ffc600;}`--这两条属性在按键点击的时候改变阴影的效果
- `transition: all .07s ease;`--定义以上动画在0.07秒内完成。
我们注意到我们定义了`.palying`类,在按键按下的时侯为该元素添加`playing`类,在结束后移除`playing`类。
## JS代码
### 按键监听&音效播放&添加动画
```js
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
if (!audio) return;
key.classList.add('playing');
audio.currentTime = 0;
audio.play();
}
/**
* 监听页面的keydown事件,触发playAudio函数。
*/
window.addEventListener('keydown', playSound);
```
* 监听页面的keydown事件,触发playAudio函数。
* 通过KeyCode检测我们按下的键盘按钮是哪个按钮。
* A -> 65
* B -> 66
* C -> 67
* D -> 68
* E -> 69
* F -> 70
* G -> 71
* H -> 72
* I -> 73
* J -> 74
* K -> 75
* L -> 76
* M -> 77
* N -> 78
* O -> 79
* P -> 80
* Q -> 81
* R -> 82
* S -> 83
* T -> 84
* U -> 85
* V -> 86
* W -> 87
* X -> 88
* Y -> 89
* Z -> 90
* 在这里我们用到了ES6的模板字符串,`${e.keyCode}`,可以动态的将按键的`Keycode`传过去,以使`audio`动态的获取每一个按键绑定的`audio`。需要注意的是模板字符串一定要使用"`"(Esc下面那个键)包裹,而不是双引号。
* 我们注意到`audio.play();`前面一行是`audio.currentTime = 0;`,这是因为,如果没有在播放音效前将该音乐重置,会发生以下情况,当我连续点击某一按键的时候,只有第一次点击会响,第二次第三次连续的点击可能没声音。所以在每一次点击之前重置音效是很有必要的。
* `key.classList.add('playing');`可以在按键点击的同时为该元素添加playing类,展示小动画。
* `if(!audio) return; if(!key) return;`因为并不是每一个按键都有音效,当用户点击了非绑定音效按键,及时退出函数是很好的习惯。
### 动画结束后移除动画
```js
function removeTransition(e) {
if (e.propertyName !== 'transform') return;
e.target.classList.remove('playing');
}
const keys = Array.from(document.querySelectorAll('.key'));
keys.forEach(key => key.addEventListener('transitionend',stopTransition));
```
- 监听每一个按键元素的`transitionend`事件,当按键元素的动画结束后会触发`removeTransition`函数。
- 首先在`removeTransition`函数中可以输出事件e的内容,会输出该动画每一步具体的变化,发现其中会有`propertyName`属性,可以通过判断`propertyName`等于其中的一个值(例如'transform'),等于该值就移除`playing`类,也即移除动画。
- 在定位元素的时候,可以使用`this`也可以使用`e.target`,可以简单这么理解,`this`值的是谁出发了这次事件,也就是`key`,就等同于事件的目标(e.target).
## 解决难点
### 如何将键盘按键与页面按钮对应起来?
连接的帮手是 ``keydown`` 事件中的 `keyCode` 属性,`keyCode` 属性的值和 ASCII 编码值相同(对应小写字母)。在[这个网站]( http://keycode.info/ )可以用按键盘来查看对应的键码。
我们能获取到的初始页面中,按钮 `div` 和音频 `audio` 标签中都添加了一个属性 `data-key` 用于存储对应的键码,这样做的目的是,添加键盘事件监听后,触发键盘事件时即可获取事件的 `keyCode` 属性值,以此为线索,操作对应的按钮及音频。
````javascript
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`div[data-key="${e.keyCode}"]`);
````
### 如何保证按键被按住不放时,可以马上响起连续鼓点声?
每次播放音频之前,设置播放时间戳为 0:
````javascript
var audio = document.getElementById("video");
audio.currentTime = 0;
audio.play();
````
### 如何使页面按钮恢复原状?
利用一个叫 [`transitionened`](https://developer.mozilla.org/zh-CN/docs/Web/Events/transitionend) 的事件,它在 CSS transition 结束后会被触发。我们就可以利用这个事件,在每次打鼓的效果(尺寸变大、颜色变化)完成之后,去除相应样式。
在这个页面中,发生 `transition` 的样式属性不止一个(`box-shadow`, `transform`, `border-color`),所以需要添加一个判断语句,使每发生一次按键事件时,只去除一次样式。
````javascript
funciton remove(event) {
if (event.propertyName !== 'border-left-color') return;
this.classList.remove('playing');
// event.target.classList.remove('playing');
}
````
================================================
FILE: 01 - JavaScript Drum Kit/index-FINISHED.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS Drum Kit</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="keys">
<div data-key="65" class="key">
<kbd>A</kbd>
<span class="sound">clap</span>
</div>
<div data-key="83" class="key">
<kbd>S</kbd>
<span class="sound">hihat</span>
</div>
<div data-key="68" class="key">
<kbd>D</kbd>
<span class="sound">kick</span>
</div>
<div data-key="70" class="key">
<kbd>F</kbd>
<span class="sound">openhat</span>
</div>
<div data-key="71" class="key">
<kbd>G</kbd>
<span class="sound">boom</span>
</div>
<div data-key="72" class="key">
<kbd>H</kbd>
<span class="sound">ride</span>
</div>
<div data-key="74" class="key">
<kbd>J</kbd>
<span class="sound">snare</span>
</div>
<div data-key="75" class="key">
<kbd>K</kbd>
<span class="sound">tom</span>
</div>
<div data-key="76" class="key">
<kbd>L</kbd>
<span class="sound">tink</span>
</div>
</div>
<audio data-key="65" src="sounds/clap.wav"></audio>
<audio data-key="83" src="sounds/hihat.wav"></audio>
<audio data-key="68" src="sounds/kick.wav"></audio>
<audio data-key="70" src="sounds/openhat.wav"></audio>
<audio data-key="71" src="sounds/boom.wav"></audio>
<audio data-key="72" src="sounds/ride.wav"></audio>
<audio data-key="74" src="sounds/snare.wav"></audio>
<audio data-key="75" src="sounds/tom.wav"></audio>
<audio data-key="76" src="sounds/tink.wav"></audio>
<script>
function removeTransition(e) {
if (e.propertyName !== 'transform') return;
e.target.classList.remove('playing');
}
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`div[data-key="${e.keyCode}"]`); //获取到的具有指定data-key属性的div元素
if (!audio) return;
key.classList.add('playing');//将获取到的具有指定data-key属性的div元素上添加playing类
audio.currentTime = 0; //currentTime 属性设置或返回音频/视频播放的当前位置(以秒计)。当设置该属性时,播放会跳跃到指定的位置.
audio.play(); //play() 方法开始播放当前的音频或视频。
}
const keys = Array.from(document.querySelectorAll('.key'));//将返回的nodelist类数组转换为真正的数组对象。
keys.forEach(key => key.addEventListener('transitionend', removeTransition));//给所有key元素都加上了过渡完成监听事件。transitionend 事件在 CSS 完成过渡后触发。
/**
* 监听页面的keydown事件,触发playAudio函数。
*/
window.addEventListener('keydown', playSound);
</script>
</body>
</html>
================================================
FILE: 01 - JavaScript Drum Kit/index-START.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS Drum Kit</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="keys">
<div data-key="65" class="key">
<kbd>A</kbd>
<span class="sound">clap</span>
</div>
<div data-key="83" class="key">
<kbd>S</kbd>
<span class="sound">hihat</span>
</div>
<div data-key="68" class="key">
<kbd>D</kbd>
<span class="sound">kick</span>
</div>
<div data-key="70" class="key">
<kbd>F</kbd>
<span class="sound">openhat</span>
</div>
<div data-key="71" class="key">
<kbd>G</kbd>
<span class="sound">boom</span>
</div>
<div data-key="72" class="key">
<kbd>H</kbd>
<span class="sound">ride</span>
</div>
<div data-key="74" class="key">
<kbd>J</kbd>
<span class="sound">snare</span>
</div>
<div data-key="75" class="key">
<kbd>K</kbd>
<span class="sound">tom</span>
</div>
<div data-key="76" class="key">
<kbd>L</kbd>
<span class="sound">tink</span>
</div>
</div>
<audio data-key="65" src="sounds/clap.wav"></audio>
<audio data-key="83" src="sounds/hihat.wav"></audio>
<audio data-key="68" src="sounds/kick.wav"></audio>
<audio data-key="70" src="sounds/openhat.wav"></audio>
<audio data-key="71" src="sounds/boom.wav"></audio>
<audio data-key="72" src="sounds/ride.wav"></audio>
<audio data-key="74" src="sounds/snare.wav"></audio>
<audio data-key="75" src="sounds/tom.wav"></audio>
<audio data-key="76" src="sounds/tink.wav"></audio>
<script>
</script>
</body>
</html>
================================================
FILE: 01 - JavaScript Drum Kit/style.css
================================================
html {
font-size: 10px;
background-size: cover;
background-image: url(http://i.imgur.com/b9r5sEL.jpg);
}
body,
html {
margin: 0;
padding: 0;
font-family: sans-serif;//非称线--字体的开始和结束之处没有特殊修饰
}
.keys {
display: flex;
flex: 1;
min-height: 100vh;
align-items: center;
justify-content: center;
}
.key {
border: .4rem solid black;
border-radius: .5rem;
margin: 1rem;
font-size: 1.5rem;
padding: 1rem .5rem;
transition: all .07s ease;
width: 10rem;
text-align: center;
color: white;
background: rgba(0, 0, 0, 0.4);
text-shadow: 0 0 .5rem black;
}
.playing {
transform: scale(1.1);
border-color: #ffc600;
box-shadow: 0 0 1rem #ffc600;
}
kbd {
display: block;
font-size: 4rem;
}
.sound {
font-size: 1.2rem;
text-transform: uppercase;
letter-spacing: .1rem;
color: #ffc600;
}
================================================
FILE: 02 - JS and CSS Clock/README.md
================================================
# Day02 - JavaScript + CSS Clock
## 简介
第二天的练习是用JS+CSS模拟时钟效果。
效果如下:

实现以上模拟时钟的效果,大致思路和解决方案如下:
* 分别获取到当前时间的时、分、秒。
* 通过时分秒对一圈360度,进行映射,确定每一个指针所需旋转的角度。
* 通过CSS的`transform:rotate(deg)`,来实时的调整指针在键盘中的位置。
### 文件说明:
(1)image:用来存放背景图片
(2)click.js:最终版JS逻辑
(3)click.css:最终版样式表
## 页面布局
```html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RealTimeClock</title>
<link rel="stylesheet" href="click.css">
</head>
<body>
<div class="clock">
<div class="clock-face">
<div class="hand hour-hand"></div>
<div class="hand min-hand"></div>
<div class="hand second-hand"></div>
</div>
</div>
<div class="dateblock">
<div class="date"></div>
<div class="week"></div>
<div class="time"></div>
</div>
<script src="clock.js"></script>
</body>
</html>
```
## CSS样式
```css
/*时分秒指针初始化是垂直的,指针移动没有设置过渡效果和过渡时间,
就是根据角度来定位置*/
html{
/*font-size:625%,默认字体大小都是16px,16*62.5=100px,1rem=100px*/
font-size:625%;
background: #018DED url(./image/picture4.jpg) bottom center ;
background-size: cover;
}
html,body{
margin:0px;
padding:0px;
display: flex;
min-height: 100vh;
justify-content:center;
align-items:center;
}
/*采用的是标准盒模型,即是纯宽高*/
.clock{
position:relative;
width:3rem;
height:3rem;
border:0.2rem solid white;
margin:0.5rem auto;
padding:0.2rem;
background: rgba(0,0,0,0.4);
border-radius:50%;
box-shadow:0 0 2px 4px rgba(0,0,0,0.1),
0 0 10px 3px rgba(0,0,0,0.2),
0 0 1px 2px #EFEFEF inset,
0 0 30px black inset;
}
.clock-face{
position:relative;
width:100%; /*这里的100%是300px,是clock的宽*/
height:100%;
}
/*时钟表表盘中心圆点*/
.clock-face::after{
content:'';
display: block;
width:.1rem;
height:.1rem;
background-color: #a8c5d1;
position: absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
border-radius:50%;
}
/*指针通用样式,在sass中可以封装成一个mixin*/
.hand{
background: #fff;
position:absolute;
bottom:50%;
left:50%;
/*transform:translateX(-50%); 虽说这样可以使指针居中线,但是translate的平移是相对于自身center位置的,那么这样居中处理后,下面的旋转仍旧按的是平移之前的right位置为原点,虽说三个指针通过translateY看似处于一条中线上,实际旋转时仍然是按照各自的right位置进行旋转*/
transform:rotate(0deg);
transform-origin:50% 100%;
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.1),
0 0 8px rgba(0, 0, 0, 0.4),
2px 5px 1px rgba(0, 0, 0, 0.5);
}
/*时针样式*/
.hour-hand{
height:40%;
width:0.1rem;
margin-left:-0.05rem; /*使时针向左移动自身的一半来居中*/
border-bottom-left-radius: .05rem;
border-top-left-radius:.05rem;
}
.min-hand {
height: 45%;
width: .05rem;
margin-left:-0.025rem;
}
.second-hand {
height: 50%;
width: .02rem;
margin-left:-0.01rem;
border-bottom-left-radius: .02rem;
border-top-left-radius: .02rem;
background-color: red;
}
/*日期,时间,星期几的样式*/
.dateblock{
width: 5rem;
position: relative;
font-size:.7rem;
font-family:serif;
font-weight:bold;
text-align: center;
color:white;
}
```
**涉及到的特性:**
- `transform-oragin`
调整指针的初始位置以及旋转的轴点:[transform-oragin](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin)
```css
transform:rotate(0deg);
transform-origin:50% 100%; //这里旋转点是bottom
```
- `transform: rotate()`
## JS代码
```js
//时分表每次移动没有过渡效果,仅仅根据角度来定旋转的位置,
//不用考虑354->0度的一个回旋BUG,若过渡时间过短会出现闪动。
//左边时钟表盘部分
function left(){
const secondHand = document.querySelector(".second-hand");
const minHand = document.querySelector(".min-hand");
const hourHand = document.querySelector(".hour-hand");
function updata(){
const now = new Date();
//秒针的旋转计算
const seconds = now.getSeconds();
const secondsDegrees = seconds*6;
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
//分针旋转的计算
const mins = now.getMinutes();
const minsDegrees = (mins*6)+(seconds/60)*6;
minHand.style.transform = `rotate(${minsDegrees}deg)`;
//时针旋转的计算
const hours = now.getHours();
const hoursDegrees = (hours-12)*30+(mins/60)*30;
hourHand.style.transform = `rotate(${hoursDegrees}deg)`;
}
setInterval(updata,1000);
updata();
}
//右边电子日历部分
function right(){
const DATE = document.querySelector(".date");
const WEEK = document.querySelector(".week");
const TIME = document.querySelector(".time");
function Adate(){
const now = new Date();
const weekList = ["星期一","星期二","星期三","星期四","星期五","星期六","星期日"];
const str = now.getFullYear()+"-"+now.getMonth()+"-"+now.getDay();
DATE.innerHTML = str;
WEEK.innerHTML = weekList[now.getDay()];
}
Adate();
setInterval(Adate,24*3600);
// 分钟,秒,不足两位时,用0进行凑位。
function zero(arg){
if(arg>=10){
return arg;
}else{
return "0"+arg;
}
}
// 显示当前时间的函数
function Atime(){
const now = new Date();
const str = now.getHours()+":"+zero(now.getMinutes())+":"+zero(now.getSeconds());
TIME.innerHTML=str;
}
Atime();
setInterval(Atime,1000);
}
left();
right();
```
- 获取秒针、分钟、小时节点
```js
const secondHand = document.querySelector(".second-hand");
const minHand = document.querySelector(".min-hand");
const hourHand = document.querySelector(".hour-hand");
```
- 获取当前时间秒、分、小时
```js
const now = new Date();
const seconds = now.getSeconds();
const mins = now.getMinutes();
const hours = now.getHours();
```
- 计算秒、分、小时角度
```js
const secondsDegrees = seconds*6;
const minsDegrees = (mins*6)+(seconds/60)*6;
const hoursDegrees = (hours-12)*30+(mins/60)*30;
```
- 根据角度设置样式
```js
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
minHand.style.transform = `rotate(${minsDegrees}deg)`;
hourHand.style.transform = `rotate(${hoursDegrees}deg)`;
```
- 设置定时器,每秒调用一次`setDate`函数
```js
setInterval(updata,1000);
```
================================================
FILE: 02 - JS and CSS Clock/click.css
================================================
/*
* @Author: Administrator
* @Date: 2018-11-27 20:14:35
* @Last Modified by: Administrator
* @Last Modified time: 2018-11-30 11:41:15
*/
/*时分秒指针初始化是垂直的,指针移动没有设置过渡效果和过渡时间,
就是根据角度来定位置*/
html{
/*font-size:625%,默认字体大小都是16px,16*62.5=100px,1rem=100px*/
font-size:625%;
background: #018DED url(./image/picture4.jpg) bottom center ;
background-size: cover;
}
html,body{
margin:0px;
padding:0px;
display: flex;
min-height: 100vh;
justify-content:center;
align-items:center;
}
/*采用的是标准盒模型,即是纯宽高*/
.clock{
position:relative;
width:3rem;
height:3rem;
border:0.2rem solid white;
margin:0.5rem auto;
padding:0.2rem;
background: rgba(0,0,0,0.4);
border-radius:50%;
box-shadow:0 0 2px 4px rgba(0,0,0,0.1),
0 0 10px 3px rgba(0,0,0,0.2),
0 0 1px 2px #EFEFEF inset,
0 0 30px black inset;
}
.clock-face{
position:relative;
width:100%; /*这里的100%是300px,是clock的宽*/
height:100%;
}
/*时钟表表盘中心圆点*/
.clock-face::after{
content:'';
display: block;
width:.1rem;
height:.1rem;
background-color: #a8c5d1;
position: absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
border-radius:50%;
}
/*指针通用样式,在sass中可以封装成一个mixin*/
.hand{
background: #fff;
position:absolute;
bottom:50%;
left:50%;
/*transform:translateX(-50%); 虽说这样可以使指针居中线,但是translate的平移是相对于自身center位置的,那么这样居中处理后,下面的旋转仍旧按的是平移之前的right位置为原点,虽说三个指针通过translateY看似处于一条中线上,实际旋转时仍然是按照各自的right位置进行旋转*/
transform:rotate(0deg);
transform-origin:50% 100%;
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.1),
0 0 8px rgba(0, 0, 0, 0.4),
2px 5px 1px rgba(0, 0, 0, 0.5);
}
/*时针样式*/
.hour-hand{
height:40%;
width:0.1rem;
margin-left:-0.05rem; /*使时针向左移动自身的一半来居中*/
border-bottom-left-radius: .05rem;
border-top-left-radius:.05rem;
}
.min-hand {
height: 45%;
width: .05rem;
margin-left:-0.025rem;
}
.second-hand {
height: 50%;
width: .02rem;
margin-left:-0.01rem;
border-bottom-left-radius: .02rem;
border-top-left-radius: .02rem;
background-color: red;
}
/*日期,时间,星期几的样式*/
.dateblock{
width: 5rem;
position: relative;
font-size:.7rem;
font-family:serif;
font-weight:bold;
text-align: center;
color:white;
}
================================================
FILE: 02 - JS and CSS Clock/clock.js
================================================
/*
* @Author: Administrator
* @Date: 2018-11-27 20:15:18
* @Last Modified by: Administrator
* @Last Modified time: 2018-11-30 11:08:26
*/
//时分表每次移动没有过渡效果,仅仅根据角度来定旋转的位置,
//不用考虑354->0度的一个回旋BUG,若过渡时间过短会出现闪动。
function left(){
const secondHand = document.querySelector(".second-hand");
const minHand = document.querySelector(".min-hand");
const hourHand = document.querySelector(".hour-hand");
function updata(){
const now = new Date();
//秒针的旋转计算
const seconds = now.getSeconds();
const secondsDegrees = seconds*6;
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
//分针旋转的计算
const mins = now.getMinutes();
const minsDegrees = (mins*6)+(seconds/60)*6;
minHand.style.transform = `rotate(${minsDegrees}deg)`;
//时针旋转的计算
const hours = now.getHours();
const hoursDegrees = (hours-12)*30+(mins/60)*30;
hourHand.style.transform = `rotate(${hoursDegrees}deg)`;
}
setInterval(updata,1000);
updata();
}
function right(){
const DATE = document.querySelector(".date");
const WEEK = document.querySelector(".week");
const TIME = document.querySelector(".time");
function Adate(){
const now = new Date();
const weekList = ["星期一","星期二","星期三","星期四","星期五","星期六","星期日"];
const str = now.getFullYear()+"-"+now.getMonth()+"-"+now.getDay();
DATE.innerHTML = str;
WEEK.innerHTML = weekList[now.getDay()];
}
Adate();
setInterval(Adate,24*3600);
// 分钟,秒,不足两位时,用0进行凑位。
function zero(arg){
if(arg>=10){
return arg;
}else{
return "0"+arg;
}
}
// 显示当前时间的函数
function Atime(){
const now = new Date();
const str = now.getHours()+":"+zero(now.getMinutes())+":"+zero(now.getSeconds());
TIME.innerHTML=str;
}
Atime();
setInterval(Atime,1000);
}
left();
right();
================================================
FILE: 02 - JS and CSS Clock/clock1.js
================================================
/*
* @Author: Administrator
* @Date: 2018-11-29 09:54:44
* @Last Modified by: Administrator
* @Last Modified time: 2018-11-30 11:34:30
*/
const secondHand = document.querySelector('.second-hand');
const minsHand = document.querySelector('.min-hand');
const hourHand = document.querySelector('.hour-hand');
let secondDeg = 0;
let minDeg = 0;
let hourDeg = 0;
//初始化函数,用来标定当前时间下时分秒的位置,遵循规则:小单位移动角度的效果累加到大单位上。
function initDate() {
const date = new Date();
const second = date.getSeconds();
secondDeg = second*6; //一秒转动的角度为6°
const min = date.getMinutes();
minDeg = (min + second / 60)*6; //秒转换为分钟,一分钟转动的角度也是6°
const hour = date.getHours();
hourDeg = ((hour-12)+(min/60)+(second/3600)) * 30; //转化为小时,一小时转动的角度的30°
secondHand.style.transform = `rotate(${ secondDeg }deg)`;
minsHand.style.transform = `rotate(${ minDeg }deg)`;
hourHand.style.transform = `rotate(${ hourDeg }deg)`;
}
//跟新函数:每隔一秒计算一次,也即是每次增加1s,(1/60)分钟,(1/3600)小时
function updateDate() {
secondDeg += 1*6;
minDeg += (1 / 60)*6;
hourDeg += (1/3600)*30;
secondHand.style.transform = `rotate(${ secondDeg }deg)`;
minsHand.style.transform = `rotate(${ minDeg }deg)`;
hourHand.style.transform = `rotate(${ hourDeg }deg)`;
}
initDate();
setInterval(updateDate, 1000);
//通过初始化时分秒的位置,然后进行角度的累加
================================================
FILE: 02 - JS and CSS Clock/clock2.js
================================================
/*
* @Author: Administrator
* @Date: 2018-11-30 10:48:55
* @Last Modified by: Administrator
* @Last Modified time: 2018-11-30 11:34:08
*/
/*
* @Author: Administrator
* @Date: 2018-11-27 20:15:18
* @Last Modified by: Administrator
* @Last Modified time: 2018-11-29 13:53:54
*/
//每隔一秒,计算一次时分秒的角度进行定位。
function left(){
const secondHand = document.querySelector(".second-hand");
const minHand = document.querySelector(".min-hand");
const hourHand = document.querySelector(".hour-hand");
function updata(){
const now = new Date();
//秒针的旋转计算,以及闪动消除
const seconds = now.getSeconds();
const secondsDegrees = seconds*6;
if (secondsDegrees === 0) {
secondHand.style.transition = 'all 0s';
} else {
secondHand.style.transition = 'all 0.05s';
}
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
//分针旋转的计算
const mins = now.getMinutes();
const minsDegrees = (mins*6)+(seconds/60)*6;
if (minsDegrees === 0) {
minHand.style.transition = 'all 0s';
} else {
minHand.style.transition = 'all 0.05s';
}
minHand.style.transform = `rotate(${minsDegrees}deg)`;
//时针旋转的计算
const hours = now.getHours();
const hoursDegrees = (hours-12)*30+(mins/60)*30;
hourHand.style.transform = `rotate(${hoursDegrees}deg)`;
}
setInterval(updata,1000);
updata();
}
function right(){
const DATE = document.querySelector(".date");
const WEEK = document.querySelector(".week");
const TIME = document.querySelector(".time");
function Adate(){
const now = new Date();
const weekList = ["星期一","星期二","星期三","星期四","星期五","星期六","星期日"];
const str = now.getFullYear()+"-"+now.getMonth()+"-"+now.getDay();
DATE.innerHTML = str;
WEEK.innerHTML = weekList[now.getDay()];
}
Adate();
setInterval(Adate,24*3600);
// 分钟,秒,不足两位时,用0进行凑位。
function zero(arg){
if(arg>=10){
return arg;
}else{
return "0"+arg;
}
}
// 显示当前时间的函数
function Atime(){
const now = new Date();
const str = now.getHours()+":"+zero(now.getMinutes())+":"+zero(now.getSeconds());
TIME.innerHTML=str;
}
Atime();
setInterval(Atime,1000);
}
left();
right();
================================================
FILE: 02 - JS and CSS Clock/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RealTimeClock</title>
<link rel="stylesheet" href="click.css">
</head>
<body>
<div class="clock">
<div class="clock-face">
<div class="hand hour-hand"></div>
<div class="hand min-hand"></div>
<div class="hand second-hand"></div>
</div>
</div>
<div class="dateblock">
<div class="date"></div>
<div class="week"></div>
<div class="time"></div>
</div>
<script src="clock.js"></script>
</body>
</html>
================================================
FILE: 02 - JS and CSS Clock/style.css
================================================
/*
* @Author: Administrator
* @Date: 2018-11-27 20:14:35
* @Last Modified by: Administrator
* @Last Modified time: 2018-11-29 12:41:57
*/
html{
/*font-size:625%,默认字体大小都是16px,16*62.5=100px,1rem=100px*/
font-size:625%;
background: #018DED url(http://unsplash.it/1500/1000?image=881&blur=50) bottom center ;
background-size: cover;
}
html,body{
margin:0px;
padding:0px;
display: flex;
min-height: 100vh;
justify-content:center;
align-items:center;
}
/*采用的是标准盒模型,即是纯宽高*/
.clock{
position:relative;
width:3rem;
height:3rem;
border:0.2rem solid white;
margin:0.5rem auto;
padding:0.2rem;
background: rgba(0,0,0,0.4);
border-radius:50%;
box-shadow:0 0 2px 4px rgba(0,0,0,0.1),
0 0 10px 3px rgba(0,0,0,0.2),
0 0 1px 2px #EFEFEF inset,
0 0 30px black inset;
}
.clock-face{
position:relative;
width:100%; /*这里的100%是300px,是clock的宽*/
height:100%;
/*transform: translateY(-30px);*/
}
/*时钟表表盘中心圆点*/
.clock-face::after{
content:'';
display: block;
width:.1rem;
height:.1rem;
background-color: #a8c5d1;
position: absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
border-radius:50%;
}
/*指针通用样式,在sass中可以封装成一个mixin*/
.hand{
background: #fff;
position:absolute;
top:50%;
right:50%;
/*transform:translateY(-50%); 虽说这样可以使指针居中线,但是translate的平移是相对于自身center位置的,那么这样居中处理后,下面的旋转仍旧按的是平移之前的right位置为原点,虽说三个指针通过translateY看似处于一条中线上,实际旋转时仍然是按照各自的right位置进行旋转*/
/*transform:rotate(90deg);*/
transform-origin:100% 50%;
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.1),
0 0 8px rgba(0, 0, 0, 0.4),
2px 5px 1px rgba(0, 0, 0, 0.5);
transition-timing-function:linear;
}
/*时针样式*/
.hour-hand{
width:40%;
height:0.1rem;
margin-top:-0.05rem;
border-bottom-left-radius: .05rem;
border-top-left-radius:.05rem;
transition:all 3s;
}
.min-hand {
width: 45%;
height: .05rem;
margin-top:-0.025rem;
transition: all .1s;
}
.second-hand {
width: 50%;
height: .02rem;
margin-top:-0.01rem;
border-bottom-left-radius: .02rem;
border-top-left-radius: .02rem;
transition: all .05s;
background-color: red;
}
/*日期,时间,星期几的样式*/
.dateblock{
width: 5rem;
position: relative;
font-size:.7rem;
font-family:serif;
font-weight:bold;
text-align: center;
color:white;
}
================================================
FILE: 03 - CSS Variables/README.md
================================================
# Day03 - CSS 变量
## 实现效果

## HTML源码
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Variables</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h2>Update <span class="h1">CSS</span> Variables with <span class="h1">JS</span></h2>
<div class="controls">
<div class="wrap">
<label for="spacing">Spacing:</label>
<input type="range" id="spacing" min="10" max="200" value="10" onchange="spacingchange()">
</div>
<div class="wrap">
<label for="blur">Blur:</label>
<input type="range" id="blur" min="0" max="25" value="10" onchange="blurchange()">
</div>
<div class="wrap">
<label for="base">Base Color:</label>
<input type="color" id="base" name="base" value="#ffc600" onchange="basechange">
</div>
</div>
<img id="img" src="http://f.hiphotos.baidu.com/lvpics/h=800/sign=b346032cbe389b5027ffed52b534e5f1/960a304e251f95ca545f8b84ce177f3e6709525d.jpg" alt="演示图片">
<script src="variables.js"> </script>
</body>
</html>
```
## CSS源码
```css
:root{
--spacing:10px;
--blur:10px;
--base:#ffc600;
--fontsize:10px;
}
html,body{
text-align:center;
background: #193549;
margin:0;
padding:0;
min-height: 100vh;
font-size:calc(3*var(--fontsize));
font-family:'helvetica neue', sans-serif; /*Helvetica是一种被广泛使用的的西文字体(铅字体),用于印刷行业,Helvetica是苹果电脑的默认字体,微软常用的Arial字体也来自于它*/
font-weight:900;/*设置字体粗细:100---900,400=normal,700=bolder*/
color:white;
}
.h1{
color:var(--base);
}
.controls{
font-weight:100;
margin-bottom:calc(5*var(--fontsize));
}
.controls .wrap{
display: inline-block;
margin:5px auto;
}
.controls .wrap label{
margin-left:20px;
}
.controls .wrap input{
position:relative;
top:3px;
width:calc(10*var(--fontsize));
}
.controls .wrap #base{
top:-3px;
}
img{
width:calc(60*var(--fontsize));
height:calc(40*var(--fontsize));
padding:var(--spacing);
filter:blur(var(--blur));
background-color:var(--base);/*背景颜色填充内容、内边距、边框,作为打底色*/
}
```
## JS源码
```js
function spacingchange(){
var spacing=document.querySelector("#spacing");
document.body.style.setProperty('--spacing', spacing.value+'px');
// var img=document.querySelector("#img");
// img.style.padding=spacing.value+'px';一个需要改变的元素可以将其取出,改变它的对应项,但有100个需要改变的元素时,改变根变量值最有效。
}
function blurchange(){
var blur=document.querySelector("#blur");
document.body.style.setProperty("--blur",blur.value+'px');
}
function basechange(){
var base=document.querySelector("#base");
document.body.style.setProperty("--base",base.value);
}
```
## 过程指南
### CSS 部分准备
1. 声明全局(`:root`)的 CSS 变量
2. 将变量应用到页面中对应元素 `<img>`
3. 处理标题的 CSS 值
### JS 实时更新 CSS 值
1. 监听input的change改变函数,然后触发各自的事件处理函数
2. 每个事件中,先取出该元素,然后设置CSS的原生变量值,进而下面的所有样式中凡是用到这个变量的值都跟着改变。
改进:利用字符串模板+遍历添加事件的方法,给每一个input元素添加事件处理函数。
## 基础知识
1. NodeList 和 Array 的区别
可以打开 __proto__ 查看它的方法,其中有 `forEach()`、`item()`、`keys()` 等。而 Array 的 prototype 中有 `map()`、`pop()` 等数组才有的方法。
3. HTML5 中的自定义数据属性 `dataset`
HTML5 中可以为元素添加非标准的自定义属性,只需要加上 `data-` 前缀,可以随便添加和命名。添加之后,可以通过元素的 `dataset` 属性来访问这些值,`dataset` 的值是 DOMStringMap 的一个实例化对象,其中包含之前所设定的自定义属性的“名-值”对。
4. [CSS variable](https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_variables)
这是一个 CSS3 的新特性,[IE 和 Edge 目前都还不支持](http://caniuse.com/#feat=css-variables)。命名写法是 `--变量名`,在引用这个变量时写法是 `var(--变量名)`。具体实例见下一条代码。
5. `:root` 伪类
这个伪元素匹配的是文档的根元素,也就是 `<html>` 标签。
所以常用于声明全局的 CSS 变量:
```css
:root {
--color: #fff;
}
```
在使用时:
```css
img {
background: var(--base);
}
```
5. CSS 滤镜 [filter](https://developer.mozilla.org/zh-CN/docs/Web/CSS/filter)
CSS 的滤镜提供了一些图形特效,比如高斯模糊(blur)、锐化、变色等。它带有一些预设的函数,在使用时加上参数调用这些函数即可。[在 Chrome、Firefox 中都支持。](http://caniuse.com/#search=filter)
6. `<input type="range">`HTML5中type属性之range
range 输入类型用于应该包含指定范围值的输入字段。
range 类型显示为滑块。
您也可以设置可接受数字的限制:
`<input type="range" name="points" min="1" max="10" />`
|属性|值|描述|
|:---------:|:---------:|:---------:|
|max|number|规定允许的最大值。|
|min|number|规定允许的最小值。|
|step|number|规定合法数字间隔(如果 step="3",则合法数字是 -3,0,3,6,以此类推)。|
|value|number|规定默认值。|
7. `<input type="color">`HTML5中type属性之color
color输入类型用于规定颜色。
该输入类型允许您从拾色器中选取颜色。
实例:
`Color: <input type="color" name="user_color" />`
8. font-weight 属性设置文本的粗细。
> normal 默认值。定义标准的字符。
bold 定义粗体字符。
bolder 定义更粗的字符。
lighter 定义更细的字符。
100,
200,
300,
400,
500,
600,
700,
800,
900:定义由粗到细的字符。400 等同于 normal,而 700 等同于 bold。
inherit 规定应该从父元素继承字体的粗细。
9. background-color 属性为元素设置一种纯色。
这种颜色会填充元素的内容、内边距和边框区域,但不包括外边距。如果边框有透明部分(如虚线边框dashed),会透过这些透明部分显示出背景色。
## 解决难点
1. **如何处理参数值(一个有 px 、另一个没有)**
运用 `dataset` 储存后缀,有 px 后缀的标签中设置 `<input data-sizing: px>`:
```html
<input type="range" name="blur" min="0" max="25" value="10" data-sizing="px">
<input type="color" name="base" value="#8aa8af">
```
JS 中通过 `dataset.sizing` 来获取后缀值:
```javascript
const suffix = this.dataset.sizing || '';
```
此时 suffix 获取到的值,针对颜色为空,而针对长度类的则为 'px'。
2. **如何用 JavaScript 改变 CSS 属性值?**
在 JavaScript 中 `document.documentElement` 即代表文档根元素。所以要改变全局的 CSS 变量,可以这样写:
```js
document.documentElement.style.setProperty('--base', '#fff');
```
================================================
FILE: 03 - CSS Variables/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Variables</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h2>Update <span class="h1">CSS</span> Variables with <span class="h1">JS</span></h2>
<div class="controls">
<div class="wrap">
<label for="spacing">Spacing:</label>
<input type="range" id="spacing" min="10" max="200" value="10" onchange="spacingchange()">
</div>
<div class="wrap">
<label for="blur">Blur:</label>
<input type="range" id="blur" min="0" max="25" value="10" onchange="blurchange()">
</div>
<div class="wrap">
<label for="base">Base Color:</label>
<input type="color" id="base" name="base" value="#ffc600" onchange="basechange">
</div>
</div>
<img id="img" src="http://f.hiphotos.baidu.com/lvpics/h=800/sign=b346032cbe389b5027ffed52b534e5f1/960a304e251f95ca545f8b84ce177f3e6709525d.jpg" alt="演示图片">
<script src="variables.js"> </script>
</body>
</html>
================================================
FILE: 03 - CSS Variables/style.css
================================================
/*
* @Author: Administrator
* @Date: 2018-11-30 20:09:15
* @Last Modified by: Administrator
* @Last Modified time: 2018-12-03 21:57:37
*/
:root{
--spacing:10px;
--blur:10px;
--base:#ffc600;
--fontsize:10px;
}
html,body{
text-align:center;
background: #193549;
margin:0;
padding:0;
min-height: 100vh;
font-size:calc(3*var(--fontsize));
font-family:'helvetica neue', sans-serif; /*Helvetica是一种被广泛使用的的西文字体(铅字体),用于印刷行业,Helvetica是苹果电脑的默认字体,微软常用的Arial字体也来自于它*/
font-weight:900;/*设置字体粗细:100---900,400=normal,700=bolder*/
color:white;
}
.h1{
color:var(--base);
}
.controls{
font-weight:100;
margin-bottom:calc(5*var(--fontsize));
}
.controls .wrap{
display: inline-block;
margin:5px auto;
}
.controls .wrap label{
margin-left:20px;
}
.controls .wrap input{
position:relative;
top:3px;
width:calc(10*var(--fontsize));
}
.controls .wrap #base{
top:-3px;
}
img{
width:calc(60*var(--fontsize));
height:calc(40*var(--fontsize));
padding:var(--spacing);
filter:blur(var(--blur));
background-color:var(--base);/*背景颜色填充内容、内边距、边框,作为打底色*/
}
================================================
FILE: 03 - CSS Variables/variables.js
================================================
/*
* @Author: Administrator
* @Date: 2018-11-30 20:09:45
* @Last Modified by: Administrator
* @Last Modified time: 2018-12-03 22:17:32
*/
function spacingchange(){
var spacing=document.querySelector("#spacing");
document.body.style.setProperty('--spacing', spacing.value+'px');
// var img=document.querySelector("#img");
// img.style.padding=spacing.value+'px';一个需要改变的元素可以将其取出,改变它的对应项,但有100个需要改变的元素时,改变根变量值最有效。
}
function blurchange(){
var blur=document.querySelector("#blur");
document.body.style.setProperty("--blur",blur.value+'px');
// document.documentElement.style.setProperty("--blur",blur.value+'px');
// 在 JavaScript 中 document.documentElement 即代表文档根元素。所以要改变全局的 CSS 变量,可以这样写
}
function basechange(){
var base=document.querySelector("#base");
document.body.style.setProperty("--base",base.value);
}
================================================
FILE: 04 - Array Cardio Day 1/README.md
================================================
# Day04 - Array Cardio 指南一
## 实现效果
这一部分主要是熟悉 Array 的几个基本方法,其中有两个(filter、map)是 ES6 定义的迭代方法,这些迭代方法都有一个特点,就是对数组的每一项都运行给定函数,根据使用的迭代方法的不同,有不同的返回结果。
文档给出了一个初始操作的 `inventor` 数组,基于这个数组可以练习一下`Array`的各个方法,请用`Google Chrome`浏览器打开 `HTML` 后在`Console`面板中查看输出结果。
## 炫酷的调试技巧
在 Console 中我们常用到的可能是 `console.log()` ,但它还有一个很炫的输出,按照表格来输出,效果如下:
```js
console.table(thing)
```

## 原始数据
本节中我们将围绕如下数据进行相关操作以便快速掌握数组的相关方法的使用。
```js
const inventors = [{
first: 'Albert',
last: 'Einstein',
year: 1879,
passed: 1955
},
{
first: 'Isaac',
last: 'Newton',
year: 1643,
passed: 1727
},
{
first: 'Galileo',
last: 'Galilei',
year: 1564,
passed: 1642
},
{
first: 'Marie',
last: 'Curie',
year: 1867,
passed: 1934
},
{
first: 'Johannes',
last: 'Kepler',
year: 1571,
passed: 1630
},
{
first: 'Nicolaus',
last: 'Copernicus',
year: 1473,
passed: 1543
},
{
first: 'Max',
last: 'Planck',
year: 1858,
passed: 1947
},
{
first: 'Katherine',
last: 'Blodgett',
year: 1898,
passed: 1979
},
{
first: 'Ada',
last: 'Lovelace',
year: 1815,
passed: 1852
},
{
first: 'Sarah E.',
last: 'Goode',
year: 1855,
passed: 1905
},
{
first: 'Lise',
last: 'Meitner',
year: 1878,
passed: 1968
},
{
first: 'Hanna',
last: 'Hammarström',
year: 1829,
passed: 1909
}
];
const people = ['Beck, Glenn', 'Becker, Carl', 'Beckett, Samuel', 'Beddoes, Mick', 'Beecher, Henry',
'Beethoven, Ludwig', 'Begin, Menachem', 'Belloc, Hilaire', 'Bellow, Saul', 'Benchley, Robert',
'Benenson, Peter', 'Ben-Gurion, David', 'Benjamin, Walter', 'Benn, Tony', 'Bennington, Chester',
'Benson, Leana', 'Bent, Silas', 'Bentsen, Lloyd', 'Berger, Ric', 'Bergman, Ingmar', 'Berio, Luciano',
'Berle, Milton', 'Berlin, Irving', 'Berne, Eric', 'Bernhard, Sandra', 'Berra, Yogi', 'Berry, Halle',
'Berry, Wendell', 'Bethea, Erin', 'Bevan, Aneurin', 'Bevel, Ken', 'Biden, Joseph', 'Bierce, Ambrose',
'Biko, Steve', 'Billings, Josh', 'Biondo, Frank', 'Birrell, Augustine', 'Black Elk', 'Blair, Robert',
'Blair, Tony', 'Blake, William'
];
const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car',
'truck', 'pogostick'
];
```
## 筛选 16 世纪出生的发明家
[filter](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
过滤操作,有点像 SQL 里面的 select 语句。筛出运行结果是 true 的组成数组返回。
````js
const __fifteen = inventors.filter(function(inventor) {
if (inventor.year >= 1500 && inventor.year < 1600 ) {
return true;
} else {
return false;
}
});
console.table(__fifteen);
````
前面几篇已经提到过箭头函数,这里可以简化一下,用箭头函数来写,而且由于 if 语句的存在并不是必要的,可以写成下面这样:
````js
const fifteen = inventors.filter(inventor =>(inventor.year >= 1500 && inventor.year < 1600));
console.table(fifteen);
````
控制台效果图:

## 展示他们的姓和名
[map](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
map 形象的理解就是,把数组中的每个元素进行处理后,返回一个新的数组。
例子如下:
````js
// Array.prototype.map()
// 2. 展示他们的姓和名
const fullNames = inventors.map(inventor => `${inventor.first} ${inventor.last}`);
console.log(fullNames);
````
控制台效果图:

## 把他们按照年龄从大到小进行排序
[sort](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
默认情况下,`Array.prototype.sort()` 会将数组以字符串的形式进行升序排列(10 会排在 2 之前),但 sort 也可以接受一个函数作为参数。所以需要对数字大小排序时需要自己设定一个比较函数,例子如下:
````js
// Array.prototype.sort()
// 3. 把他们按照年龄从大到小进行排序
const ordered = inventors.sort((a, b) => {
if(a.year > b.year) {
return 1;
} else {
return -1;
}
});
console.table(ordered);
````
上面的代码可以简写成:
```js
const ordered = inventors.sort((a, b) => a.year > b.year ? 1 : -1);
console.table(ordered);
```
控制台效果图:

## 计算所有的发明家加起来一共活了多少岁
[reduce](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
```js
// Array.prototype.reduce()
// 4. 计算所有的发明家加起来一共活了多少岁
<!--0为total的初始值-->
const totalYears = inventors.reduce((total, inventor) => {
return total + (inventor.passed - inventor.year);
}, 0);
console.log(totalYears);
```
控制台效果图:

## 按照他们活了多久来进行排序
```js
// 5. 按照他们活了多久来进行排序
const oldest = inventors.sort((a, b) => {
const lastInventor = a.passed - a.year;
const nextInventor = b.passed - b.year;
return lastInventor > nextInventor ? -1 : 1;
});
console.table(oldest);
```
控制台效果图:

## `map、filter`结合使用筛选出网页中含有`CSS`标题的数据名称
```js
const category = document.querySelectorAll('.subject-list h2 a');
const links = Array.from(category);
const CSS_BOOK = links
.map(link => link.textContent)
.filter(streetName => streetName.includes('CSS'));
```
由 `querySelectorAll()` 获取到的是一个 `NodeList` ,它并非是 Array 类型的数据,所以并不具有 map 和 filter 这样的方法,所以如果要进行筛选操作则需要把它转化成 Array 类型,使用下面示例之中的 `Array.from()` 来转化。
Google Chrome浏览球操作如下:
- 打开`https://book.douban.com/tag/web`网页。
- 在控制台按如下图操作即可

## 按照姓氏来对发明家进行排序
```js
const alpha = people.sort((lastOne, nextOne) => {
const [aLast, aFirst] = lastOne.split(', ');
const [bLast, bFirst] = nextOne.split(', ');
return aLast > bLast ? 1 : -1;
});
console.log(alpha);
```
控制台效果图:

## 统计给出数组中各个物品的数量
[reduce](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
这是一个归并数组的方法,它接受一个函数作为参数(这个函数可以理解成累加器),它会遍历数组的所有项,然后构建一个最终的返回值,这个值就是这个累加器的第一个参数。第二个参数中的`0`是`previousValue`的初始值,例子如下:
````js
[0,1,2,3,4].reduce((previousValue, currentValue, index, array) => {
return previousValue + currentValue;
},0);
````
而此处我们需要统计一个给定数组中各个项的值,恰好可以用到这个方法,在累加器之中,将统计信息存入一个新的对象,最后返回统计值。
```js
const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car',
'truck', 'pogostick'
];
const transportation = data.reduce( (obj, item) => {
if (!obj[item]) {
obj[item] = 0;
}
obj[item]++;
return obj;
}, {});
console.log(transportation);
```
```JS
var data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car','truck', 'pogostick'
];
data.reduce((acc,cur)=>{
cur in acc ? acc[cur]++ : acc[cur]=1;
return acc;
},{});
```

================================================
FILE: 04 - Array Cardio Day 1/index-FINISHED.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Array Cardio 💪</title>
</head>
<body>
<p><em>打开Google Chrome浏览器,查看JavaScript 控制台。</em> 💁</p>
<script>
// Get your shorts on - this is an array workout!
// ## Array Cardio Day 1
// 构造一些我们需要使用到的数据
const inventors = [{
first: 'Albert',
last: 'Einstein',
year: 1879,
passed: 1955
},
{
first: 'Isaac',
last: 'Newton',
year: 1643,
passed: 1727
},
{
first: 'Galileo',
last: 'Galilei',
year: 1564,
passed: 1642
},
{
first: 'Marie',
last: 'Curie',
year: 1867,
passed: 1934
},
{
first: 'Johannes',
last: 'Kepler',
year: 1571,
passed: 1630
},
{
first: 'Nicolaus',
last: 'Copernicus',
year: 1473,
passed: 1543
},
{
first: 'Max',
last: 'Planck',
year: 1858,
passed: 1947
},
{
first: 'Katherine',
last: 'Blodgett',
year: 1898,
passed: 1979
},
{
first: 'Ada',
last: 'Lovelace',
year: 1815,
passed: 1852
},
{
first: 'Sarah E.',
last: 'Goode',
year: 1855,
passed: 1905
},
{
first: 'Lise',
last: 'Meitner',
year: 1878,
passed: 1968
},
{
first: 'Hanna',
last: 'Hammarström',
year: 1829,
passed: 1909
}
];
// // Array.prototype.filter()
// // 1. 筛选 16 世纪出生的发明家
// const fifteen = inventors.filter(inventor => (inventor.year >= 1500 && inventor.year < 1600));
// console.table(fifteen);
// // Array.prototype.map()
// // 2. 展示他们的姓和名
// const fullNames = inventors.map(inventor => `${inventor.first} ${inventor.last}`);
// console.log(fullNames);
// Array.prototype.sort()
// 3. 把他们按照年龄从大到小进行排序
// const ordered = inventors.sort((a, b) => {
// if(a.year > b.year) {
// return 1;
// } else {
// return -1;
// }
// });
// console.table(ordered);
// const ordered = inventors.sort((a, b) => a.year > b.year ? 1 : -1);
// console.table(ordered);
// // Array.prototype.reduce()
// // 4. 计算所有的发明家加起来一共活了多少岁
// const totalYears = inventors.reduce((total, inventor) => {
// return total + (inventor.passed - inventor.year);
// }, 0);
// console.log(totalYears);
// // 5. 按照他们活了多久来进行排序
// const oldest = inventors.sort((a, b) => {
// const lastInventor = a.passed - a.year;
// const nextInventor = b.passed - b.year;
// return lastInventor > nextInventor ? -1 : 1;
// });
// console.table(oldest);
// 6. 筛选出下面网页里含有某个词语的标题
// https://book.douban.com/tag/web
// const category = document.querySelectorAll('.subject-list h2 a');
// // 由 querySelectorAll() 获取到的是一个 NodeList ,它并非是 Array 类型的数据,所以并不具有 map 和 filter 这样的方法,所以如果要进行筛选操作则需要把它转化成 Array 类型,使用下面示例之中的 Array.from() 来转化。
// const links = Array.from(category);
// const CSS_BOOK = links
// .map(link => link.textContent)
// .filter(streetName => streetName.includes('CSS'));
// // 7. sort 练习
// // 按照姓氏来对发明家进行排序
// const people = ['Beck, Glenn', 'Becker, Carl', 'Beckett, Samuel', 'Beddoes, Mick', 'Beecher, Henry',
// 'Beethoven, Ludwig', 'Begin, Menachem', 'Belloc, Hilaire', 'Bellow, Saul', 'Benchley, Robert',
// 'Benenson, Peter', 'Ben-Gurion, David', 'Benjamin, Walter', 'Benn, Tony', 'Bennington, Chester',
// 'Benson, Leana', 'Bent, Silas', 'Bentsen, Lloyd', 'Berger, Ric', 'Bergman, Ingmar', 'Berio, Luciano',
// 'Berle, Milton', 'Berlin, Irving', 'Berne, Eric', 'Bernhard, Sandra', 'Berra, Yogi', 'Berry, Halle',
// 'Berry, Wendell', 'Bethea, Erin', 'Bevan, Aneurin', 'Bevel, Ken', 'Biden, Joseph', 'Bierce, Ambrose',
// 'Biko, Steve', 'Billings, Josh', 'Biondo, Frank', 'Birrell, Augustine', 'Black Elk', 'Blair, Robert',
// 'Blair, Tony', 'Blake, William'
// ];
// const alpha = people.sort((lastOne, nextOne) => {
// const [aLast, aFirst] = lastOne.split(', ');
// const [bLast, bFirst] = nextOne.split(', ');
// return aLast > bLast ? 1 : -1;
// });
// console.log(alpha);
// 8. Reduce 练习
// 统计给出数组中各个物品的数量
const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car',
'truck', 'pogostick'
];
const transportation = data.reduce( (obj, item) => {
if (!obj[item]) {
obj[item] = 0;
}
obj[item]++;
return obj;
}, {});
console.log(transportation);
</script>
</body>
</html>
================================================
FILE: 04 - Array Cardio Day 1/index-START.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Array Cardio 💪</title>
</head>
<body>
<p><em>打开Google Chrome浏览器,查看JavaScript 控制台。</em> 💁</p>
<script>
// Get your shorts on - this is an array workout!
// ## Array Cardio Day 1
// Some data we can work with
const inventors = [
{ first: 'Albert', last: 'Einstein', year: 1879, passed: 1955 },
{ first: 'Isaac', last: 'Newton', year: 1643, passed: 1727 },
{ first: 'Galileo', last: 'Galilei', year: 1564, passed: 1642 },
{ first: 'Marie', last: 'Curie', year: 1867, passed: 1934 },
{ first: 'Johannes', last: 'Kepler', year: 1571, passed: 1630 },
{ first: 'Nicolaus', last: 'Copernicus', year: 1473, passed: 1543 },
{ first: 'Max', last: 'Planck', year: 1858, passed: 1947 },
{ first: 'Katherine', last: 'Blodgett', year: 1898, passed: 1979 },
{ first: 'Ada', last: 'Lovelace', year: 1815, passed: 1852 },
{ first: 'Sarah E.', last: 'Goode', year: 1855, passed: 1905 },
{ first: 'Lise', last: 'Meitner', year: 1878, passed: 1968 },
{ first: 'Hanna', last: 'Hammarström', year: 1829, passed: 1909 }
];
const people = ['Beck, Glenn', 'Becker, Carl', 'Beckett, Samuel', 'Beddoes, Mick', 'Beecher, Henry', 'Beethoven, Ludwig', 'Begin, Menachem', 'Belloc, Hilaire', 'Bellow, Saul', 'Benchley, Robert', 'Benenson, Peter', 'Ben-Gurion, David', 'Benjamin, Walter', 'Benn, Tony', 'Bennington, Chester', 'Benson, Leana', 'Bent, Silas', 'Bentsen, Lloyd', 'Berger, Ric', 'Bergman, Ingmar', 'Berio, Luciano', 'Berle, Milton', 'Berlin, Irving', 'Berne, Eric', 'Bernhard, Sandra', 'Berra, Yogi', 'Berry, Halle', 'Berry, Wendell', 'Bethea, Erin', 'Bevan, Aneurin', 'Bevel, Ken', 'Biden, Joseph', 'Bierce, Ambrose', 'Biko, Steve', 'Billings, Josh', 'Biondo, Frank', 'Birrell, Augustine', 'Black, Elk', 'Blair, Robert', 'Blair, Tony', 'Blake, William'];
// Array.prototype.filter()
// 1. Filter the list of inventors for those who were born in the 1500's
// Array.prototype.map()
// 2. Give us an array of the inventors' first and last names
// Array.prototype.sort()
// 3. Sort the inventors by birthdate, oldest to youngest
// Array.prototype.reduce()
// 4. How many years did all the inventors live?
// 5. Sort the inventors by years lived
// 6. create a list of Boulevards in Paris that contain 'de' anywhere in the name
// https://en.wikipedia.org/wiki/Category:Boulevards_in_Paris
// 7. sort Exercise
// Sort the people alphabetically by last name
// 8. Reduce Exercise
// Sum up the instances of each of these
const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car', 'truck' ];
</script>
</body>
</html>
================================================
FILE: 05 - Flex Panel Gallery/README.md
================================================
# Day05 - Flex 实现可伸缩的图片墙 中文指南
## 实现效果
点击任意一张图片,图片展开,同时从图片上下两方分别移入文字。点击已经展开的图片后,图片被压缩,同时该图片上下两端的文字被挤走。

## HTML源码
```html
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
```
初始文档的 DOM 结构:以 `.panels` 为父 `div` 之下,有 5 个类名为 `.panel` 的 `div`,这 5 个各含有 3 个子 `p` 标签。而相应的 CSS 样式中,动画时间等特性已经设定好,只需要完成不同状态下的页面布局以及事件监听即可。
## CSS 源码
```css
<style>
html {
box-sizing: border-box;
background: #ffc600;
font-family:'helvetica neue';
font-size: 20px;
font-weight: 200;
}
body {
margin: 0;
}
*, *:before, *:after {
box-sizing: inherit;
}
.panels {
min-height:100vh;
overflow: hidden;
display: flex;
}
.panel {
background:#6B0F9C;
box-shadow:inset 0 0 0 5px rgba(255,255,255,0.1);
color:white;
text-align: center;
align-items:center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition:
font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
background 0.2s;
font-size: 20px;
background-size:cover;
background-position:center;
flex: 1;
justify-content: center;
display: flex;
flex-direction: column;
}
.panel1 { background-image:url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500); }
.panel2 { background-image:url(https://source.unsplash.com/1CD3fd8kHnE/1500x1500); }
.panel3 { background-image:url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); }
.panel4 { background-image:url(https://source.unsplash.com/ITjiVXcwVng/1500x1500); }
.panel5 { background-image:url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500); }
/* Flex Items */
.panel > * {
margin:0;
width: 100%;
transition:transform 0.5s;
flex: 1 0 auto;
display:flex;
justify-content: center;
align-items: center;
}
.panel > *:first-child { transform: translateY(-100%); }
.panel.open-active > *:first-child { transform: translateY(0); }
.panel > *:last-child { transform: translateY(100%); }
.panel.open-active > *:last-child { transform: translateY(0); }
.panel p {
text-transform: uppercase;
font-family: 'Amatic SC', cursive;
text-shadow:0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45);
font-size: 2em;
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
flex: 5;
font-size:40px;
}
</style>
```
CSS 在这个过程中占了重点,运用 `flex` 可以使各个元素按一定比例占据页面。在调试的时候,可以把边框显示出来方便查看效果。(`border: 1px solid #f00;`)
1. 将 `.panels` 设置为 `display:flex`
2. 设定每个子 `panel` 的 `flex` 值为 `1`
3. 针对每个子 `panel`,设为 `display:flex`,设置其 flex 主轴方向
4. 控制 `.panle` 的子元素 `<p>` 中的文字垂直、水平居中(单独看每个 panel,其中的文字也可以用 flex 的思路来使其三等分后居中)
1. 设置为 `display:flex`
2. 设置 `flex` 值
2. 设置其子元素的布局方式:垂直水平居中(沿主轴、侧轴居中)
4. 设定点击图片后文字移动的样式
5. 设定点击图片展开后的图片的 `flex` 值
**重要:不了解CSS和Flex的童鞋必看。**
- [CSS参考手册](http://www.css88.com/book/css/properties/flex/flex.htm)
- [选择器(Selectors)](https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Getting_started/Selectors)
- [CSS选择器笔记](http://www.ruanyifeng.com/blog/2009/03/css_selectors.html)
- [flex布局完全入门教程](http://bbs.kongyixueyuan.com/topic/10/flex布局完全入门教程)
## JS源码
```js
<script>
const panels = document.querySelectorAll('.panel');
function toggleOpen() {
console.log('Hello');
this.classList.toggle('open');
}
function toggleActive(e) {
console.log(e.propertyName);
if (e.propertyName.includes('flex')) {
this.classList.toggle('open-active');
}
}
panels.forEach(panel => panel.addEventListener('click', toggleOpen));
panels.forEach(panel => panel.addEventListener('transitionend', toggleActive));
</script>
```
1. 获取所有类名为 `panel` 的元素
2. 为其添加 `click` 事件监听,编写触发事件调用的函数(给触发的 DOM 元素添加/去掉样式,实现拉伸/压缩的效果)
3. 为其添加 `transitionend` 事件监听,编写调用的函数(添加/去掉样式,实现文字的飞入/飞出效果)
================================================
FILE: 05 - Flex Panel Gallery/animation.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>flexbox gallery</title>
<style>
:root{
--distance:-400px;
}
html,body{
margin:0px;
padding:0px;
}
.show{
width:100%;
display: flex;
/*设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。*/
}
.show .wrap{
flex:1; /*flex-grow:1在这里便没有效果*/
min-height: 100vh;
padding:10px;
text-align: center;
font-size: 80px;
font-family: sans-serif;
font-weight: 700;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items:center;
/*overflow: hidden;*/
word-break: break-all;/*单词换行,可以任意断开换行*/
/*animation:extend 2s ease forwords:处于动画结束处;*/
background-size:cover;
background-repeat: no-repeat;
}
.show .wrap:hover{
box-shadow: 0 0 10px 5px rgba(0,0,0,0.4);
color:orangered;
}
.show .wrap p{
transition:transform 0.5s ease;
}
.wrap p:nth-child(1){
transform: translateY(-500px);
}
.wrap.open-active p:nth-child(1){
transform: translateY(0);
}
.wrap p:nth-child(3){
transform: translateY(500px);
}
.wrap.open-active p:nth-child(3){
transform:translate(0);
}
@keyframes extend{
40%{
flex-grow:0.8;
}
100%{
flex-grow:3;
}
}
@keyframes shink{
0%{
flex-grow:3;
}
40%{
flex-grow:3.2;
}
100%{
flex-grow:1;
}
}
.first {background: url(./image/1.jpg);}
.second {background: url(./image/2.jpg);}
.third {background: url(./image/3.jpg);}
.forth {background: url(./image/4.jpg);}
.fifth {background: url(./image/5.jpg);}
/*媒体查询and后面无空格不生效*/
/*800《=屏幕尺寸《=1250*/
@media screen and (min-width: 800px) and (max-width: 1250px){
.show .wrap{
font-size: 60px;
}
.wrap p:nth-child(2n+1){
font-family: serif;
font-size:50px;
}
}
/*屏幕尺寸《=800px*/
@media screen and (max-width: 800px){
.show .wrap{
font-size:30px;
}
.wrap p:nth-child(2n+1){
font-family: serif;
font-size:20px;
}
}
</style>
</head>
<body>
<div class="show">
<div class="first wrap">
<p>HEY</p>
<p>LET'S</p>
<p>DANCE</p>
</div>
<div class="second wrap">
<p>GIVE</p>
<p>TAKE</p>
<p>RECEIVE</p>
</div>
<div class="third wrap">
<p>WXPERE</p>
<p>IT</p>
<p>TODAY</p>
</div>
<div class="forth wrap">
<p>GIVE</p>
<p>ALL</p>
<p>YOUCAN</p>
</div>
<div class="fifth wrap">
<p>UP</p>
<p>IN</p>
<p>FIGHT</p>
</div>
</div>
<script>
//取出所有的wrap元素,返回的是伪数组,转化为真数组后进行遍历给每个元素添加点击监听事件。
const Wrap = Array.from(document.querySelectorAll(".wrap"));
Wrap.forEach(item=>item.addEventListener("click",handwith,true));
Wrap.forEach(item=>item.addEventListener("animationend",openActive,true));
// 动画效果结束触发事件处理函数
function openActive(e){
console.log("fff");
this.classList.toggle("open-active");
}
function handwith(e){
// var _this=e.target;
if(this.classList.contains("scale")){
this.classList.remove("scale");
this.style.animation="shink 1s ease forwards";
}else{
this.classList.add("scale");
this.style.animation="extend 1s ease forwards";
// document.body.style.setProperty("--distance","-50px");
}
}
</script>
</body>
</html>
================================================
FILE: 05 - Flex Panel Gallery/index-FINISHED.html
================================================
/*这里用了过渡的思路:transition,而我自己做的用了animation+flex-grow的变化以及@media*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flex Panels</title>
<link href='https://fonts.googleapis.com/css?family=Amatic+SC' rel='stylesheet' type='text/css'>
</head>
<body>
```css
<style>
html {
box-sizing: border-box;/*怪异盒模型:width是content+padding+border,实际开发中多采用这种,不用再计算所谓的padding和border来确定整个盒模型大小*/
background:#ffc600;
font-family:'helvetica neue';
font-size: 20px;
font-weight: 200;
}
body {
margin: 0;
}
*, *:before, *:after {
box-sizing: inherit;
}
.panels {
min-height:100vh;
overflow: hidden;
display: flex;/*对该元素下的子元素进行flex弹性布局*/
}
.panel {
background:#6B0F9C;
box-shadow:inset 0 0 0 5px rgba(255,255,255,0.1);
color:white;
text-align: center;
align-items:center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition:
font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
background 0.2s;
font-size: 20px;
background-size:cover;
background-position:center;
flex: 1; /*同级子元素平均布局*/
display: flex; /*对其子元素p, 设置布局方式*/
justify-content: center; /*水平居中*/
flex-direction: column; /*成列排列*/
}
/*设置了5个栅格的背景照片*/
.panel1 { background-image:url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500); }
.panel2 { background-image:url(https://source.unsplash.com/1CD3fd8kHnE/1500x1500); }
.panel3 { background-image:url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d); }
.panel4 { background-image:url(https://source.unsplash.com/ITjiVXcwVng/1500x1500); }
.panel5 { background-image:url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500); }
/* Flex Items */
.panel > * {
margin:0;
width: 100%;
transition:transform 0.5s;
flex: 1 0 auto;
display:flex;
justify-content: center;
align-items: center;
}
.panel > *:first-child { transform: translateY(-100%); }
.panel.open-active > *:first-child { transform: translateY(0); }
.panel > *:last-child { transform: translateY(100%); }
.panel.open-active > *:last-child { transform: translateY(0); }
.panel p {
text-transform: uppercase; /*文本转化为大写*/
font-family: 'Amatic SC', cursive;
text-shadow:0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45);
font-size: 2em;
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
flex: 5;
font-size:40px;
}
.panel1panel1 {
color: red;
}
</style>
```
```html
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
```
```js
<script>
const panels = Array.form(document.querySelectorAll('.panel'));
function toggleOpen() {
console.log('Hello');
this.classList.toggle('open');
}
function toggleActive(e) {
console.log(e.propertyName);
if (e.propertyName.includes('flex')) {
this.classList.toggle('open-active');
}
}
panels.forEach(panel => panel.addEventListener('click', toggleOpen));
panels.forEach(panel => panel.addEventListener('transitionend', toggleActive)); //过渡效果完成之后触发的时间toggleActive。
</script>
```
</body>
</html>
================================================
FILE: 05 - Flex Panel Gallery/index-START.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flex Panels 💪</title>
<link href='https://fonts.googleapis.com/css?family=Amatic+SC' rel='stylesheet' type='text/css'>
</head>
<body>
<style>
html {
/*
*content-box:padding和border不被包含在定义的width和height之内。
*对象的实际宽度等于设置的width值和border、padding之和,
*即 ( Element width = width + border + padding )此属性表现为标准模式下的盒模型。
*
*border-box:padding和border被包含在定义的width和height之内。
*对象的实际宽度就等于设置的width值,即使定义有border和padding也不会改变对象的实际宽度,
*即 ( Element width = width )此属性表现为怪异模式下的盒模型。
*/
box-sizing: border-box;
background: #ffc600;
font-family: 'helvetica neue';
font-size: 20px;
font-weight: 200;
}
body {
margin: 0;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.panels {
min-height: : 100vh;
}
.panel {
background: #6B0F9C;
box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.1);
color: white;
text-align: center;
align-items: center;
transition: font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
background 0.2s;
font-size: 20px;
background-size: cover;
background-position: center;
}
.panel1 {
background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500);
}
.panel2 {
background-image: url(https://source.unsplash.com/1CD3fd8kHnE/1500x1500);
}
.panel3 {
background-image: url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d);
}
.panel4 {
background-image: url(https://source.unsplash.com/ITjiVXcwVng/1500x1500);
}
.panel5 {
background-image: url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500);
}
.panel>* {
margin: 0;
width: 100%;
transition: transform 0.5s;
}
.panel p {
text-transform: uppercase;
font-family: 'Amatic SC', cursive;
text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45);
font-size: 2em;
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
font-size: 40px;
}
</style>
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
<script>
</script>
</body>
</html>
================================================
FILE: 06 - Fetch、filter、正则表达式实现快速古诗匹配/.vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:8080",
"webRoot": "${workspaceRoot}"
},
{
"type": "chrome",
"request": "attach",
"name": "Attach to Chrome",
"port": 9222,
"webRoot": "${workspaceRoot}"
}
]
}
================================================
FILE: 06 - Fetch、filter、正则表达式实现快速古诗匹配/README.md
================================================
# Day06 - Fetch、filter、正则表达式实现快速古诗匹配
## 效果图
在输入框中搜索`字或者某个词`快速匹配含有这个字或者是词的诗句。

## 涉及特性
- flex布局
- `nth-child`奇偶匹配
- Fetch获取数据以及异常处理
- Array
- `filter()`
- `push()`
- `...`
- JavaScript RegExp 对象
- 字面量语法
- 创建 RegExp 对象的语法
- 修饰符`i`、`g`
- `test()`
- `replace()`
- 防抖处理
- 匹配文本的高亮显示
- "瓦楞纸效果"的实现:transform:perspective(xxxpx) rotateX(xdeg)
## 实现步骤
- UI布局
- 通过Fetch下载数据
- 数据处理并保存
- 事件监听
- 数据匹配操作
- 新数据替换展示
## 布局篇
- HTML代码
```html
<form class="search-form">
<input type="text" class="search" placeholder="诗人名字,关键字">
<ul class="suggestions">
<li>输入诗人名字</li>
<li>输入关键字,找一首诗</li>
</ul>
</form>
```
- CSS代码
```css
<style>
html{
box-sizing:border-box;
margin:0px;
background-color:burlywood;
font-family: "Kaiti","SimHei","Hiragino Sans GB","Helvetica neue";
font-size:625%;
font-weight:200;
}
*,*:before,*:after{
box-sizing:inherit;
}
body{
display: flex;
justify-content: center;
/*text-align:center;*/
}
.search-form{
display: flex;
flex-direction:column; /*规定元素项目垂直显示,此时主轴为垂直方向*/
align-items: center; /*在主轴垂直时,设置align-items可以使元素水平居中*/
}
.search{
width:8rem;
border:0.1rem solid #f7f7f7;
padding:0.2rem;
border-radius: 0.05rem;
font-size:0.2rem;
text-align:center;
box-shadow:0 0 5px 0 rgba(0,0,0,0.12),
0 0 3px 0 rgba(0,0,0,.19) inset;
margin-top:0.4rem;
outline:none; /*去掉input框的默认样式*/
}
.suggestions{
list-style: none; /*去掉序列修饰符号*/
width:6rem;
margin:0px;
padding: 0px;
display: flex;
flex-direction: column;
align-items: center;
font-size:0.2rem;
}
.suggestions li{
text-align: center;
width:100%;
background: white;
border-bottom: 0.01rem solid #D8D8D8;
padding:0.15rem;
box-shadow:0 0 10px rgba(0,0,0,0.14);
/*transition:transform 0.5s ease;*/
}
.suggestions li:hover{
box-shadow: 0 0 10px 0 rgba(0,0,0,0.4);
}
.suggestions p{
text-align:right;
margin:0;
}
/*实现奇偶栏的折叠效果*/
.suggestions li:nth-child(odd){
transform: perspective(1rem) rotateX(-3deg) ;
}
.suggestions li:nth-child(even){
transform: perspective(1rem) rotateX(3deg) translateY(-2px) ;
}
</style>
```
- CSS布局相关参考文档
- [CSS参考手册](http://www.css88.com/book/css/properties/flex/flex.htm)
- [CSS选择器笔记](http://www.ruanyifeng.com/blog/2009/03/css_selectors.html)
- [flex布局完全入门教程](http://bbs.kongyixueyuan.com/topic/10/flex布局完全入门教程)
- [使用HTML5里的classList操作CSS类](http://www.webhek.com/post/html5-classlist-api.html)
- [position](http://zh.learnlayout.com/position.html)
## 通过Fetch下载数据解析并且保存
```js
//通过fetch来获取后台数据,并进行json化以及请求异常处理。
fetch(url)
.then(response=>{
if(response.ok){
return response.json();
}else{
return Promise.reject({
status:response.status,
statusText:response.statusText
});
}
})
.then(data => { poetrys.push(...data); //concat会创建一个新数组,push会修改原来的数组,所以可以直接拿过来用。
// console.log(poetrys);
})
.catch(e=>{console.log("status:",e.status);
console.log("statusText:",e.statusText);
});
```
[Fetch详细使用文档,详见本人博客](https://blog.csdn.net/qq_39207948/article/details/85050687)
## 事件监听
```js
const search = document.querySelector(".search");
const suggestions = document.querySelector(".suggestions");
search.addEventListener("change",debounce(findMatches,500)); //当输入框中文本改变时会触发事件处理函数
search.addEventListener("keyup",debounce(findMatches,500)); //当按键up时会触发事件,最好有防抖操作。
```
获取`search`和`suggestions'`节点分别对`change`、`keyup`事件进行监听,当输入框中的内容发生变化或者键盘弹起时触发`debounce`函数进行防抖处理。
## 数据匹配操作
- RegExp使用基础
[RegExp参考文档](http://www.w3school.com.cn/jsref/jsref_obj_regexp.asp)
- 项目源码分析
```js
//关键字匹配函数,在里面又调用了内容加载函数
function findMatches(){
//有搜索内容时,进行关键字匹配,没有的话显示两行提示
if(this.value){
let regexp = new RegExp(this.value,"gi");
let matched= poetrys.filter(item=>{ //根据标题、作者名、文本中的是否有关键字,将该数组项取出
return regexp.test(item.title)||regexp.test(item.detail_author)||regexp.test(item.detail_text);
});
if(matched.length > 0){ //如果匹配到数组项,将匹配到的内容加载出来
createDom(matched);
}else{ //如果没有匹配项,那么显示提示信息。
suggestions.innerHTML='';
suggestions.innerHTML=`<li>抱歉,没有查找到匹配项!</li>`
}
}else{
suggestions.innerHTML=`<li>输入诗人名字</li>
<li>输入关键字,找一首诗</li>`;
}
}
//将匹配到的内容加载出来
function createDom(matched){
let frag = document.createDocumentFragment(); //用文本片段的形式进行一次性添加,减少回流重绘。
matched.forEach(item=>{
let li = document.createElement("li");
let p= document.createElement("p");
let regexp = new RegExp(search.value,"gi");
//将匹配到的关键词用带样式的形式进行替换。
let detailText = item.detail_text.replace(regexp,`<span style="color:green">${search.value}</span>`);
let title = item.title.replace(regexp,`<span style="color:green">${search.value}</span>`);
let detailAuthor = item.detail_author[0].replace(regexp,`<span style="color:green">${search.value}</span>`);
li.innerHTML = detailText;
p.innerHTML = title + "-" +detailAuthor;
// p.setAttribute("style","text-align:right;margin:0px");
li.appendChild(p);
frag.appendChild(li);
});
suggestions.innerHTML='';
suggestions.appendChild(frag);
}
```
================================================
FILE: 06 - Fetch、filter、正则表达式实现快速古诗匹配/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Fetch,filter,Regexp实现快速古诗匹配</title>
<style>
html{
box-sizing:border-box;
margin:0px;
background-color:burlywood;
font-family: "Kaiti","SimHei","Hiragino Sans GB","Helvetica neue";
font-size:625%;
font-weight:200;
}
*,*:before,*:after{
box-sizing:inherit;
}
body{
display: flex;
justify-content: center;
/*text-align:center;*/
}
.search-form{
display: flex;
flex-direction:column; /*规定元素项目垂直显示,此时主轴为垂直方向*/
align-items: center; /*在主轴垂直时,设置align-items可以使元素水平居中*/
}
.search{
width:8rem;
border:0.1rem solid #f7f7f7;
padding:0.2rem;
border-radius: 0.05rem;
font-size:0.2rem;
text-align:center;
box-shadow:0 0 5px 0 rgba(0,0,0,0.12),
0 0 3px 0 rgba(0,0,0,.19) inset;
margin-top:0.4rem;
outline:none; /*去掉input框的默认样式*/
}
.suggestions{
list-style: none; /*去掉序列修饰符号*/
width:6rem;
margin:0px;
padding: 0px;
display: flex;
flex-direction: column;
align-items: center;
font-size:0.2rem;
}
.suggestions li{
text-align: center;
width:100%;
background: white;
border-bottom: 0.01rem solid #D8D8D8;
padding:0.15rem;
box-shadow:0 0 10px rgba(0,0,0,0.14);
/*transition:transform 0.5s ease;*/
}
.suggestions li:hover{
box-shadow: 0 0 10px 0 rgba(0,0,0,0.4);
}
.suggestions p{
text-align:right;
margin:0;
}
/*实现奇偶栏的折叠效果*/
.suggestions li:nth-child(odd){
transform: perspective(1rem) rotateX(-3deg) ;
}
.suggestions li:nth-child(even){
transform: perspective(1rem) rotateX(3deg) translateY(-2px) ;
}
</style>
</head>
<body>
<form class="search-form">
<input type="text" class="search" placeholder="诗人名字,关键字">
<ul class="suggestions">
<li>输入诗人名字</li>
<li>输入关键字,找一首诗</li>
</ul>
</form>
<script>
const url = 'https://gist.githubusercontent.com/liyuechun/f00bb31fb8f46ee0a283a4d182f691b4/raw/3ea4b427917048cdc596b38b67b5ed664605b76d/TangPoetry.json';
const poetrys=[];
//通过fetch来获取后台数据,并进行json化以及请求异常处理。
fetch(url)
.then(response=>{
if(response.ok){
return response.json();
}else{
return Promise.reject({
status:response.status,
statusText:response.statusText
});
}
})
.then(data => { poetrys.push(...data); //concat会创建一个新数组,push会修改原来的数组,所以可以直接拿过来用。
// console.log(poetrys);
})
.catch(e=>{console.log("status:",e.status);
console.log("statusText:",e.statusText);
});
const search = document.querySelector(".search");
const suggestions = document.querySelector(".suggestions");
search.addEventListener("change",debounce(findMatches,500)); //当输入框中文本改变时会触发事件处理函数
search.addEventListener("keyup",debounce(findMatches,500)); //当按键up时会触发事件,最好有防抖操作。
//防抖函数:避免搜索内容更改过快
function debounce(func,wait){
let timeout;
return function(){
let _this=this;
let args=arguments;
clearTimeout(timeout);
timeout=setTimeout(func.bind(_this,args),wait);
}
}
//关键字匹配函数,在里面又调用了内容加载函数
function findMatches(){
//有搜索内容时,进行关键字匹配,没有的话显示两行提示
if(this.value){
let regexp = new RegExp(this.value,"gi");
let matched= poetrys.filter(item=>{ //根据标题、作者名、文本中的是否有关键字,将该数组项取出
return regexp.test(item.title)||regexp.test(item.detail_author)||regexp.test(item.detail_text);
});
if(matched.length > 0){ //如果匹配到数组项,将匹配到的内容加载出来
createDom(matched);
}else{ //如果没有匹配项,那么显示提示信息。
suggestions.innerHTML='';
suggestions.innerHTML=`<li>抱歉,没有查找到匹配项!</li>`
}
}else{
suggestions.innerHTML=`<li>输入诗人名字</li>
<li>输入关键字,找一首诗</li>`;
}
}
//将匹配到的内容加载出来
function createDom(matched){
let frag = document.createDocumentFragment(); //用文本片段的形式进行一次性添加,减少回流重绘。
matched.forEach(item=>{
let li = document.createElement("li");
let p= document.createElement("p");
let regexp = new RegExp(search.value,"gi");
//将匹配到的关键词用带样式的形式进行替换。
let detailText = item.detail_text.replace(regexp,`<span style="color:green">${search.value}</span>`);
let title = item.title.replace(regexp,`<span style="color:green">${search.value}</span>`);
let detailAuthor = item.detail_author[0].replace(regexp,`<span style="color:green">${search.value}</span>`);
li.innerHTML = detailText;
p.innerHTML = title + "-" +detailAuthor;
// p.setAttribute("style","text-align:right;margin:0px");
li.appendChild(p);
frag.appendChild(li);
});
suggestions.innerHTML='';
suggestions.appendChild(frag);
}
</script>
</body>
</html>
================================================
FILE: 07 - Array Cardio Day 2/README.md
================================================
# Day07 - Array Cardio 中文指南二
第七天的练习是接着之前[Day04 - Array Cardio 中文指南一](http://bbs.kongyixueyuan.com/topic/40/day04-array-cardio-%E6%8C%87%E5%8D%97%E4%B8%80)的练习,继续熟练数组的方法,依旧没有页面显示效果,所以请打开浏览器的Console面板进行调试运行。


## 任务表
网站给了两个数组,分别为`people`数组和`comments`数组,如下:
```JavaScript
const people = [
{ name: 'Wes', year: 1988 },
{ name: 'Kait', year: 1986 },
{ name: 'Irv', year: 1970 },
{ name: 'Lux', year: 2015 }
];
const comments = [
{ text: 'Love this!', id: 523423 },
{ text: 'Super good', id: 823423 },
{ text: 'You are the best', id: 2039842 },
{ text: 'Ramen is my fav food ever', id: 123523 },
{ text: 'Nice Nice Nice!', id: 542328 }
];
```
**在此两数组的基础上实现一下几个操作:**
1. 是否至少有一人年满`19`周岁?
2. 是否每一个人都年满`19`周岁?
3. 是否存在`id=823423`的评论?
4. 找到`id=823423`的评论的序列号(下标)。
5. 删除`id=823423`的评论。
## 是否至少有一人年满19周岁?
### `Array.prototype.some()`
> [some参考文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
- CASE
```js
let isBiggerThan10 = (element, index, array) => {
return element > 10;
}
[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
```
- Syntax
```js
arr.some(callback[, thisArg])
```
- Parameters
- element:当前在操作的对象。
- index:当前操作对象的索引。
- array:在操作的数组指针。
- Return value
返回`true`或者`false`,返回true,说明数组中有满足条件的数据存在,返回false,说明数组里面没有满足条件的数组存在。
### 项目源码
- 版本一:
```js
const isAdult = people.some(function(person){
const currentYear = new Date().getFullYear();
if(currentYear - person.year >= 19){
return true;
}
});
console.log(isAdult);
```
- 版本二:
```JavaScript
const isAdult = people.some((person) => {
const currentYear = new Date().getFullYear();
if(currentYear - person.year >= 19){
return true;
}
});
console.log(isAdult);
```
- 版本三:
```JavaScript
const isAdult = people.some(person => (new Date().getFullYear() - person.year) >= 19 );
console.log(isAdult);
```
## 是否每一个人都年满`19`周岁?
### `Array.prototype.every()`
> [every参考文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
- CASE
```js
let isBigEnough = (element, index, array) => {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
```
- Syntax
```js
arr.every(callback)
```
- Parameters
- Parameters
- element:当前在操作的对象。
- index:当前操作对象的索引。
- array:在操作的数组指针。
- Return value
返回`true`或者`false`,返回true,代表数组中所有数据都满足条件,否则,至少有一条数据不满足条件。
### 项目源码
```JavaScript
const everyAdult = people.every(person => (new Date().getFullYear() - person.year) >= 19);
console.log({everyAdult});
```
## 是否存在`id=823423`的评论?
### `Array.prototype.find(callback)`
> [find参考文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
- CASE
```js
let isBigEnough = (element) => {
return element >= 15;
}
[12, 5, 8, 130, 44].find(isBigEnough); // 130
```
- Syntax
```js
arr.find(callback)
```
- Parameters
- element:当前在操作的对象。
- index:当前操作对象的索引。
- array:在操作的数组指针。
- Return value
如果有满足条件对象,返回该对象,否则返回`undefined `。
### 项目源码
```JavaScript
const findComment = comments.find(comment => comment.id === 823423);
console.log(findComment);
}
```
## 找到`id=823423`的评论的序列号(下标)
### `Array.prototype.findIndex()`
> [findIndex参考文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)
- CASE
```js
let isBigEnough = (element) => {
return element >= 15;
}
[12, 5, 8, 130, 44].findIndex(isBigEnough);
// index of 4th element in the Array is returned,
// so this will result in '3'
```
- Syntax
arr.findIndex(callback)
- Parameters
- element:当前在操作的对象。
- index:当前操作对象的索引。
- array:在操作的数组指针。
- Return value
返回满足条件的当前对象在数组中的索引,如果找不到满足条件的对象,返回`-1`。
### 项目源码
```JavaScript
const findCommentIndex = comments.findIndex(comment => comment.id === 823423);
console.log(findCommentIndex);
```
## 删除`id=823423`的评论
> [splice参考文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)
> [slice参考文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)
### `Array.prototype.splice()`
- CASE
在索引2的位置移除0个元素,并且插入"drum"
```js
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(2, 0, 'drum');
// myFish 是 ["angel", "clown", "drum", "mandarin", "sturgeon"]
// removed is [], 没有元素被移除。
```
从索引3开始移除1个元素。
```js
var myFish = ['angel', 'clown', 'drum', 'mandarin', 'sturgeon'];
var removed = myFish.splice(3, 1);
// 移除的原色是 ["mandarin"]
// myFish 为 ["angel", "clown", "drum", "sturgeon"]
```
从索引2移除一个元素,并且插入"trumpet"
```js
var myFish = ['angel', 'clown', 'drum', 'sturgeon'];
var removed = myFish.splice(2, 1, 'trumpet');
// myFish 为 ["angel", "clown", "trumpet", "sturgeon"]
// 移除的元素为 ["drum"]
```
从索引0开始移除2个元素,并且插入"parrot", "anemone" 和 "blue"。
```js
var myFish = ['angel', 'clown', 'trumpet', 'sturgeon'];
var removed = myFish.splice(0, 2, 'parrot', 'anemone', 'blue');
// myFish为 ["parrot", "anemone", "blue", "trumpet", "sturgeon"]
// 移除的元素是 ["angel", "clown"]
```
从索引2开始移除所有元素
```js
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(2);
// myFish 为 ["angel", "clown"]
// 移除的原色为 ["mandarin", "sturgeon"]
```
- Syntax
```js
array.splice(start)
array.splice(start, deleteCount)
array.splice(start, deleteCount, item1, item2, ...)
```
**array.splice(start):** 从索引`start`开始移除后面所有的元素。
**array.splice(start, deleteCount):** 从索引`start`元素删除`deleteCount`个元素。
**array.splice(start, deleteCount, item1, item2, ...):**从`start`索引开始,删除`deleteCount`个元素,然后插入`item1`,`item2`,...
### `Array.prototype.slice()`
- CASE
```js
var a = ['zero', 'one', 'two', 'three'];
var sliced = a.slice(1, 3);
console.log(a); // ['zero', 'one', 'two', 'three']
console.log(sliced); // ['one', 'two']
```
- Syntax
```js
arr.slice()
arr.slice(begin)
arr.slice(begin, end)
```
**arr.slice()**等价于**arr.slice(0,arr.length)**
**arr.slice(begin)**等价于**arr.slice(begin,arr.length)**
`arr.slice(begin, end)`:创建一个新数组,将索引`begin`-`end`(不包含end)的元素放到新数组中并返回新数组,原数组不被修改。
### 项目源码 - 删除`id=823423`的评论
```
const comments = [
{ text: 'Love this!', id: 523423 },
{ text: 'Super good', id: 823423 },
{ text: 'You are the best', id: 2039842 },
{ text: 'Ramen is my fav food ever', id: 123523 },
{ text: 'Nice Nice Nice!', id: 542328 }
];
const findCommentIndex = comments.findIndex(comment => comment.id === 823423);
// delete the comment with the ID of 823423
//comments.splice(findCommentIndex,1);
const newComments = [
...comments.slice(0,findCommentIndex),
...comments.slice(findCommentIndex+1)
];
```
`splice`会修改原数组,`slice`不会改变原数组的值。
================================================
FILE: 07 - Array Cardio Day 2/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Array Cardio 💪💪</title>
</head>
<body>
<p><em>Psst: have a look at the JavaScript Console</em> 💁</p>
<script>
// ## Array Cardio Day 2
const people = [
{ name: 'Wes', year: 1988 },
{ name: 'Kait', year: 1986 },
{ name: 'Irv', year: 1970 },
{ name: 'Lux', year: 2015 }
];
const comments = [
{ text: 'Love this!', id: 523423 },
{ text: 'Super good', id: 823423 },
{ text: 'You are the best', id: 2039842 },
{ text: 'Ramen is my fav food ever', id: 123523 },
{ text: 'Nice Nice Nice!', id: 542328 }
];
// Some and Every Checks
// Array.prototype.some() // is at least one person 19 or older?
// const isAdult = people.some(function(person){
// const currentYear = new Date().getFullYear();
// if(currentYear - person.year >= 19){
// return true;
// }
// });
const isAdult = people.some(person => (new Date().getFullYear() - person.year) >= 19 );
// Array.prototype.every() // is everyone 19 or older?
const everyAdult = people.every(person => (new Date().getFullYear() - person.year) >= 19);
// Array.prototype.find()
// Find is like filter, but instead returns just the one you are looking for
// find the comment with the ID of 823423
const findComment = comments.find(comment => comment.id === 823423);
// Array.prototype.findIndex()
// Find the comment with this ID
const findCommentIndex = comments.findIndex(comment => comment.id === 823423);
// delete the comment with the ID of 823423
//comments.splice(findCommentIndex,1);
const newComments = [
...comments.slice(0,findCommentIndex),
...comments.slice(findCommentIndex+1)
];
</script>
</body>
</html>
================================================
FILE: 08 - HTML5 Canvas 实现彩虹画笔绘画板/README.md
================================================
# Day08 - HTML5 Canvas 实现彩虹画笔绘画板指南
## 项目效果

> 用 HTML5 中的 Canvas 的路径绘制实现一个绘画板,可供鼠标画画,颜色呈彩虹色渐变,画笔大小同样呈渐变效果。这部分不涉及 CSS 内容,全部由 JS 来实现。
## 涉及特性
Canvas:
- 模板骨架
- 基本属性
- `getContext()`
- `strokeStyle`
- `lineCap`
- `lineJoin`
- 路径绘制
- `beginPath()`
- `lineTo()`
- `moveTo()`
鼠标事件处理:
- `mousemove`
- `mousedown`
- `mouseup`
- `mouseout`
## 过程指南
1. 获取 HTML 中的 `<canvas>` 元素,并设定宽度和高度
2. `.getContext('2d')` 获取上下文,下面以 ctx 表示
3. 设定 ctx 基本属性
- 描边和线条颜色
- 线条宽度
- 线条末端形状
4. 绘画效果
1. 设定一个用于标记绘画状态的变量
2. 鼠标事件监听,不同类型的事件将标记变量设为不同值
3. 编写发生绘制时触发的函数,设定绘制路径起点、终点
5. 线条彩虹渐变效果(运用 hsl 的 `h` 值的变化,累加)
6. 线条粗细渐变效果(设定一个范围,当超出这个范围时,线条粗细进行逆向改变,利用[撞墙反弹程序](https://blog.csdn.net/qq_39207948/article/details/85252068)
## Canvas相关知识
[Canvas_API](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API)
### 代码部分
```JS
//全局变量和初始值设置部分
let drawflag=false; //用于区分鼠标点击事件和鼠标移动事件。
let beginX=0; //设置为全局变量,初始点要传到移动处理事件中。
let beginY=0;
let hue=0; //hsl的色调初始值
let context='';
let lineWidth=60;
let direction=true; //定义变量增加方向
// 页面加载函数,在DOM结构解析完成后运行
window.onload=function(){
let canvas = document.querySelector("#tutorial");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context=canvas.getContext("2d");
canvas.addEventListener("mousedown",beginlocation);
canvas.addEventListener("mousemove",drawing);
canvas.addEventListener("mouseup",()=>drawflag=false);
canvas.addEventListener("mouseout",()=>drawflag=false);
}
```
- 页面加载函数中用的两个事件处理函数
```JS
//设定初始点坐标,并开启绘图flag
function beginlocation(e){
beginX=e.offsetX;
beginY=e.offsetY;
drawflag=true;
}
//绘图函数:实际上一段一段的直线连接而成,鼠标每移动一点就将该时刻的坐标转换成下一次的起始坐标,而鼠标移动后的位置作为该段直线结束的坐标。
function drawing(e){
if(drawflag){
let moveX=e.offsetX;
let moveY=e.offsetY;
//色相值改变
if(hue<=360){ //hue要设置初始值
hue++;
}else{
hue=0;
}
context.strokeStyle=`hsl(${hue},100%,50%)`;
//“撞墙反弹程序”
if(lineWidth>100||lineWidth<10){
direction = !direction;
}
if(direction){
lineWidth++;
}else{
lineWidth--;
}
context.lineWidth=lineWidth;
context.lineCap="round";
context.lineJoin="round";
context.beginPath();
context.moveTo(beginX,beginY);
context.lineTo(moveX,moveY);
context.closePath();
[beginX,beginY]=[moveX,moveY]; //es6解构赋值
context.stroke();
}else{
return;
}
}
```
- canvas 元素
```js
<canvas id="tutorial"></canvas>
```
`canvas` 看起来和 `img` 元素很相像,唯一的不同就是它并没有 `src` 和`alt` 属性。实际上,`canvas` 标签只有两个属性——`width`和`height`。这些都是可选的,并且同样利用 `DOM properties` 来设置。当没有设置宽度和高度的时候,`canvas`会初始化宽度为`300`像素和高度为`150`像素。该元素可以使用CSS来定义大小,但在绘制时图像会伸缩以适应它的框架尺寸:如果CSS的尺寸与初始画布的比例不一致,它会出现扭曲。
- 渲染上下文(The rendering context)
```js
var canvas = document.getElementById('tutorial');
var ctx = canvas.getContext('2d');
```
`canvas`元素创造了一个固定大小的画布,它公开了一个或多个渲染上下文,其可以用来绘制和处理要展示的内容。
`canvas`起初是空白的。为了展示,首先脚本需要找到渲染上下文,然后在它的上面绘制。`canvas`元素有一个叫做 `getContext()` 的方法,这个方法是用来获得渲染上下文和它的绘画功能。`getContext()`只有一个参数,上下文的格式。对于2D图像而言,基本教程,你可以使用[CanvasRenderingContext2D](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D)
- 检查支持性
替换内容是用于在不支持 `canvas` 标签的浏览器中展示的。通过简单的测试`getContext()`方法的存在,脚本可以检查编程支持性。
```js
var canvas = document.getElementById('tutorial');
if (canvas.getContext){
//支持
var ctx = canvas.getContext('2d');
// drawing code here
} else {
//不支持
// canvas-unsupported code here
}
```
### Canvas的简单实例
- [canvas 倒计时特效](https://blog.csdn.net/qq_39207948/article/details/85252925)
- [canvas 躁动的小球](https://blog.csdn.net/qq_39207948/article/details/85252947)
- [canvas 单个小球运动实验](https://blog.csdn.net/qq_39207948/article/details/85252849)
### 涉及知识点
[Canvas](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API)
#### canvas宽高设置
```js
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
```
#### 属性
- `lineCap`:笔触的形状,有 round | butt | square 圆、平、方三种。
- `lineJoin`:线条相交的样式,有 round | bevel | miter 圆交、斜交、斜接三种。
- `lineWidth`:线条的宽度
- `strokeStyle`:线条描边的颜色
#### 方法
- `beginPath()`:新建一条路径
- `stroke()`:绘制轮廓
- `moveTo()`:(此次)绘制操作的起点
- `lineTo()`:路径的终点
### 彩虹渐变颜色——HSL
在这个挑战中,涉及到改变线条的颜色,如何实现彩虹的渐变效果?我们需要利用 HSL 色彩模式,首先可以去这个网站 [http://mothereffinghsl.com](http://mothereffinghsl.com/) 感受一下 HSL 不同色彩值对应的效果。
- H(hue) 代表色调,取值为 0~360,专业术语叫色相
- S 是饱和度,可以理解为掺杂进去的灰度值,取值为 0~1
- L 则是亮度,取值也是 0~1,或者百分比。
这之中 H 值从 0 到 360 的变化代表了色相的角度的值域变化,利用这一点就可以实现绘制时线条颜色的渐变了,只需要在它的值超过 360 时恢复到 0 重新累加即可。
```js
//色相值改变
if(hue<=360){ //hue要设置初始值
hue++;
}else{
hue=0;
}
context.strokeStyle=`hsl(${hue},100%,50%)`;
```
除此之外,如果想实现黑白水墨的颜色,可以将颜色设置为黑色,通过透明度的改变来实现深浅不一的颜色。
### 控制笔触大小
```js
//“撞墙反弹程序”
if(lineWidth>100||lineWidth<10){
direction = !direction;
}
if(direction){
lineWidth++;
}else{
lineWidth--;
}
context.lineWidth=lineWidth;
```
上面的代码中,根据线条的宽度的变化来控制`direction`的值,根据`direction`的值来控制线宽是增加还是减少。
### 控制线条路径
```js
context.beginPath();
context.moveTo(beginX,beginY);
context.lineTo(moveX,moveY);
context.closePath();
[beginX,beginY]=[moveX,moveY]; //es6解构赋值
```
### 事件监听代码逻辑分析
```js
canvas.addEventListener("mousedown",beginlocation);
canvas.addEventListener("mousemove",drawing);
canvas.addEventListener("mouseup",()=>drawflag=false);
canvas.addEventListener("mouseout",()=>drawflag=false);
```
#### 需要整理知识点
- 1、鼠标事件有哪些,具体使用方法。
- 2、获取窗口的高度与宽度(不包含工具条与滚动条):
var w=window.innerWidth;
var h=window.innerHeight;浏览器中地址导航栏下面中的部分
和clientWidth以及clientHeight的区别。
clientX和offsetX的区别
clientX检索与窗口客户区域有关的鼠标光标的X坐标,
offsetX 检索与触发事件的对象相关的鼠标位置的水平坐标
因为canvas的宽高均设置为了window.innerHtml和window.innerWidth,那么当点击鼠标时,实际上得到的是相对于canvas元素的位置,也即是e.offsetX/Y,这里offset中的set是小写。
- 3、lineCap 属性设置或返回线条末端线帽的样式。
butt 默认。向线条的每个末端添加平直的边缘。
round 向线条的每个末端添加圆形线帽。
square 向线条的每个末端添加正方形线帽。
- 4、lineJoin 属性设置或返回所创建边角的类型,当两条线交汇时。
bevel 创建斜角。
round 创建圆角。
miter 默认。创建尖角。
================================================
FILE: 08 - HTML5 Canvas 实现彩虹画笔绘画板/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Rainbowbrush</title>
<style>
html,body{
margin:0;
padding:0;
overflow:hidden;
}
#tutorial{
border:1px solid black;
}
</style>
</head>
<body>
<canvas id="tutorial">
<p>抱歉!您的浏览器暂不支持Canvas标签属性!</p>
</canvas>
<script>
//全局变量和初始值设置部分
let drawflag=false; //用于区分鼠标点击事件和鼠标移动事件。
let beginX=0; //设置为全局变量,初始点要传到移动处理事件中。
let beginY=0;
let hue=0; //hsl的色调初始值
let context='';
let lineWidth=60;
let direction=true; //定义变量增加方向
// 页面加载函数,在DOM结构解析完成后运行
window.onload=function(){
let canvas = document.querySelector("#tutorial");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context=canvas.getContext("2d");
canvas.addEventListener("mousedown",beginlocation);
canvas.addEventListener("mousemove",drawing);
canvas.addEventListener("mouseup",()=>drawflag=false);
canvas.addEventListener("mouseout",()=>drawflag=false);
}
//设定初始点坐标,并开启绘图flag
function beginlocation(e){
beginX=e.offsetX;
beginY=e.offsetY;
drawflag=true;
}
//绘图函数:实际上一段一段的直线连接而成,鼠标每移动一点就将该时刻的坐标转换成下一次的起始坐标,而鼠标移动后的位置作为该段直线结束的坐标。
function drawing(e){
if(drawflag){
let moveX=e.offsetX;
let moveY=e.offsetY;
//色相值改变
if(hue<=360){ //hue要设置初始值
hue++;
}else{
hue=0;
}
context.strokeStyle=`hsl(${hue},100%,50%)`;
//“撞墙反弹程序”
if(lineWidth>100||lineWidth<10){
direction = !direction;
}
if(direction){
lineWidth++;
}else{
lineWidth--;
}
context.lineWidth=lineWidth;
context.lineCap="round";
context.lineJoin="round";
context.beginPath();
context.moveTo(beginX,beginY);
context.lineTo(moveX,moveY);
context.closePath();
[beginX,beginY]=[moveX,moveY]; //es6解构赋值
context.stroke();
}else{
return;
}
}
</script>
</body>
</html>
<!--
用canvas实现彩虹画笔的思路
1、判断鼠标按下和抬起的状态
2、鼠标按下时开始绘制,鼠标移动时需要得到鼠标的位置信息,根据位置信息添加新的绘图路径,进行render,抬起鼠标绘图结束。
3、点击之后开始绘图,需要一个开关的flag,只有flag打开时才能在鼠标移动时绘图。
4、可以结合input的color的拾色器来改变线条颜色。
-->
<!-- 需要整理知识点
1、鼠标事件有哪些,具体使用方法。
2、获取窗口的高度与宽度(不包含工具条与滚动条):
var w=window.innerWidth;
var h=window.innerHeight;浏览器中地址导航栏下面中的部分
和clientWidth以及clientHeight的区别。
clientX和offsetX的区别
clientX检索与窗口客户区域有关的鼠标光标的X坐标,
offsetX 检索与触发事件的对象相关的鼠标位置的水平坐标
因为canvas的宽高均设置为了window.innerHtml和window.innerWidth,那么当点击鼠标时,实际上得到的是相对于canvas元素的位置,也即是e.offsetX/Y,这里offset中的set是小写。
3、lineCap 属性设置或返回线条末端线帽的样式。
butt 默认。向线条的每个末端添加平直的边缘。
round 向线条的每个末端添加圆形线帽。
square 向线条的每个末端添加正方形线帽。
4、lineJoin 属性设置或返回所创建边角的类型,当两条线交汇时。
bevel 创建斜角。
round 创建圆角。
miter 默认。创建尖角。
-->
================================================
FILE: 09 - Console 调试各种姿势指南/README.md
================================================
# Day09 - Console 调试各种姿势指南
## 项目效果
[控制台打印结果,请猛戳我!!!](https://blog.csdn.net/qq_39207948/article/details/85261675)
## 各种调试正确姿势
### `.log` 的更多用法
这个是最常用的,但它还有一些更多功能:比如参数支持类似 C 语言的字符串替换模式。
- `%s` 字符串
- `%d` 整数
- `%f` 浮点值
- `%o` Object
- `%c` 设定输出的样式,在之后的文字将按照第二个参数里的值进行显示
```js
console.log("I am a String: %s ", "log"); //log
console.log("I am a int number: %d ", 1); //1
console.log("I am a float number: %d ", 1.23); //1.23
let dog = {name: "Lucky",age: "5"};
console.log("%o",dog);
console.log("%c3D Text"," text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9,0 3px 0 #bbb,0 4px 0 #b9b9b9,0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1),0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15);font-size:5em");
```

### 清空 console 面板输出内容
要清空已经打印输出的内容,有两种方式,一种是 JavaScript 语句: `console.clear()`。另一个是快捷键 `Ctrl + L`。

### 不同样式的输出
除了常规的 `log` 之外,还有一些其他已设定好的样式,区别在于图标或者颜色不一样:
```js
// warning!
console.warn("用于输出警示信息");
// Error :|
console.error("用于输出错误信息");
// Info
console.info("用于输出提示性信息");
//debug
console.debug("用于输出调试信息");
```

### 打印DOM节点
获取 DOM 元素之后,可以直接打印输出。
```js
const p = document.querySelector('p');
console.log(p);
console.dir(p);
```
* .log 输出这个 DOM 的 HTML 标签。
* .dir 则会输出这个 DOM 元素的属性列表。

### 断点调试
`console.asset(arg1,arg2)`方法接受一个表达式作为参数,如果参数返回值是`false`,则会输出第二个参数中的内容。
```js
const p = document.querySelector('p');
console.assert(p.classList.contains('ouch'), 'That is wrong!');
```

### 打印表格
`console.table()`方法,可以将数组、对象以表格的形式打印输出,如果只输出其中的某一列,可以加上第二个参数,如下所示:
```Javascript
console.table(dogs);
console.table(dogs, ["age"]);
```

### 分组打印
```Javascript
const dogs = [{ name: 'Snickers', age: 2 }, { name: 'hugo', age: 8 }];
dogs.forEach(dog => {
console.group(`${dog.name}`);
// console.groupCollapsed(`${dog.name}`); // 列表默认叠起状态
console.log(`${dog.name}`);
console.log(`${dog.age}`);
console.log(`${dog.name} 有 ${dog.age} 岁了`);
console.groupEnd();
});
```
`group()`方法中可以传入这个分组的名称。`group()/groupCollapsed() `与 `groupEnd()` 之间的内容会自动分组,区别在于是否能自动折叠。


### console.count() 计数
通过`console.count()`可以对输出的对象进行计数。但需要注意的是这里的计数对象仅限于由 `count()` 输出的内容,并非所有 `console` 中的输出。

### `time` 计时
用 `time("name")` 和 `timeEnd("name")` 分别控制开始点和结束点,它们两的参数表示当前计时的名称,可以自定义但需要保持相同。所以如果想看异步获取数据花了多场时间,可以这样写:
````js
console.time('fetch my data');
fetch("https://api.github.com/users/soyaine")
.then(data => data.json())
.then(data => {
console.timeEnd('fetch my data');
console.log(data);
});
````

如果 timeEnd 中的名称如果和上面不一样,得到的数据是系统当前时间换算后的毫秒值。
================================================
FILE: 09 - Console 调试各种姿势指南/index-FINISHED.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Console Tricks!</title>
</head>
<body>
<p style="color:red;font-size:40px" onClick="makeGreen()">全栈部落的小伙伴,请点击我!</p>
<script>
const dogs = [{
name: 'Snickers',
age: 2
}, {
name: 'hugo',
age: 8
}];
function makeGreen() {
const p = document.querySelector('p');
p.style.color = '#BADA55';
p.style.fontSize = '50px';
p.innerText = "打开控制台,查看调试效果。";
}
// Regular
console.log('hello');
// Interpolated
console.log("I am a String: %s ", "log"); //log
console.log("I am a int number: %d ", 1); //1
console.log("I am a float number: %d ", 1.23); //1.23
let dog = {
name: "Lucky",
age: "5"
};
console.log("%o", dog);
console.log("%c3D Text",
" text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9,0 3px 0 #bbb,0 4px 0 #b9b9b9,0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1),0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15);font-size:5em"
);
// warning!
console.warn('OH NOOO');
// Error :|
console.error('Shit!');
// Info
console.info('Crocodiles eat 3-4 people per year');
//debug
console.debug("用于输出调试信息");
// Testing
const p = document.querySelector('p');
console.assert(p.classList.contains('ouch'), 'That is wrong!');
// clearing
console.clear();
// Viewing DOM Elements
console.log(p);
console.dir(p);
console.clear();
console.table(dogs);
console.table(dogs, ["age"]);
// Grouping together
dogs.forEach(dog => {
console.groupCollapsed(`${dog.name}`);
console.log(`This is ${dog.name}`);
console.log(`${dog.name} is ${dog.age} years old`);
console.log(`${dog.name} is ${dog.age * 7} dog years old`);
console.groupEnd(`${dog.name}`);
});
// counting
console.count('liyuechun');
console.count('chunge');
console.count('黎跃春');
console.count('黎跃春');
console.count('黎跃春');
console.count('黎跃春');
console.count('黎跃春');
console.count('全栈部落');
console.count('黎跃春');
console.count('黎跃春');
console.count('黎跃春');
console.count('全栈部落');
// timing
console.time('fetch my data');
fetch("https://gist.githubusercontent.com/liyuechun/f00bb31fb8f46ee0a283a4d182f691b4/raw/3ea4b427917048cdc596b38b67b5ed664605b76d/TangPoetry.json")
.then(data => data.json())
.then(data => {
console.timeEnd('fetch my data');
console.log(data);
});
</script>
</body>
</html>
================================================
FILE: 10 - JS 实现 Checkbox 中按住 Shift 的多选功能/README.md
================================================
# Day10 - JS 实现 Checkbox 中按住 Shift 的多选功能
## 项目效果

初始文档中提供了一组 checkbox 类型的 input 元素,选中某个复选框时,其 <p> 标签中的文字会显示删除线。最终效果是,提供按下 Shift 键后进行多选操作的功能。readme.md文档中的说明对应的是xunzhoaxinag.html。
## 操作方法
1. 选中 A 项
2. 按下 Shift
3. 再选中 B 项
4. A-B 之间的所有项都被选中或者取消
## 实现方法
### 方法一
Wes Bos 在文档里提供了一种解决办法:用一个变量,来标记这个范围。
变量初始值为 `false`,当按下 Shift 键且同时选中了某个元素的时候,遍历所有项,遍历过程中,若遇到 A 或 B,则将标记值取反。同时,将所有标记为 `true` 的项设置为选中。
```js
let startChecked;
// 处理方法一:用变量 inBetween 对需要选中的元素进行标记
function handleCheck0(e) {
let inBetween = false;
if(e.shiftKey && this.checked){
boxs.forEach(input => {
console.log(input);
if(input === startChecked || input ===this) {
inBetween = !inBetween;
}
if(inBetween) {
console.log("on");
input.checked = true;
}
});
}
startChecked = this;
}
```
> 上面会出现一个问题,初次加载页面时,按住 Shift 再点击某一项,此项之后的元素都会被选中。此外,对于取消选中,无法批量操作。所以我参照了 Stack Overflow 的一个答案: How can I shift-select multiple checkboxes like GMail? 改进得到第二种解决方案。
### 方法二
方法一中的 inBetween 仅仅表示此项是否在被选中的范围中,此处会赋给它更多的意义,用它来表示此项是选中还是未选中,而范围划定则由数组来解决。
首先将获取到的 `<input>` 组转化为数组,针对每次操作,获取 A 和 B,利用 `indexOf()` 来获得 A 和 B 在数组中的索引值,由此即可确定范围,并能通过 `slice()` 来直接截取 A-B 的所有 DOM 元素,并进行状态改变的操作,而变量 onOff 表示 A-B 范围内的状态,true 表示选中,false 表示取消选中。
```js
const boxs = document.querySelectorAll('.inbox input[type="checkbox"]');
const boxArr = Array.from(boxs);
boxArr.forEach(box => box.addEventListener('click', handleCheck1));
// 处理方法二:利用数组索引获取需要选中的范围
let lastinput;//用来保存上一次的点击元素
let onoff; //用来保存上一次的点击状态,已提供给截取范围内CheckBox的状态
function handleCheck1(e){
if(e.shiftKey){ //若果按下shift按键再点击时进入该程序,主要用来处理索引值
let cur=boxArr.indexOf(this); //用来获取当前点击元素是第几个input
let last=boxArr.indexOf(lastinput); //用来获取上一次点击元素是第几个input
boxArr.slice(Math.min(cur,last),Math.max(cur,last)+1) //slice返回一个子数组
.forEach(item=>item.checked = onoff); //将截取出来的各项状态设置的和上下点击元素的状态一致
}
lastinput = this; //用来存放第一次或者上一次的点击元素(将当前点击元素作为上次元素,然后在有点击时和下次又组成一个范围)
onoff = lastinput.checked ? true : false; //识别第一次或者上一次点击元素的状态值
}
}
```
> 学习用全局变量来存放(由当前状态,转换而成的)上次状态。
设置两个变量(全局变量)来分别保存上一次的点击元素和其状态,接下里选中范围的元素状态以此为依据。
在shift被按下时,需要得到上次点击元素的索引,和当前点击元素(this)的索引,
然后将该段内input元素的状态全部统一于上次点击后的状态。
然后将当前点击元素作为上次元素,进行一个接龙。
### 涉及知识点
- 1.shiftKey:检测 SHIFT 键是否被按住。
事件属性可返回一个布尔值,指示当事件发生时,“SHIFT”键是否被按下并保持住。
语法:event.shiftKey=true|false|1|0,这里的event用this无效,用e.target无效。
- 2.document.querySelectorAll('.inbox input[type="checkbox"]')
通过[]和属性名称来选取指定元素
================================================
FILE: 10 - JS 实现 Checkbox 中按住 Shift 的多选功能/index-FINISHED.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<style>
html {
font-family: sans-serif;
background: #ffc600;
}
.inbox {
max-width: 400px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.1);
}
.item {
display: flex;
align-items: center;
border-bottom: 1px solid #F1F1F1;
}
.item:last-child {
border-bottom: 0;
}
input:checked+p {
background: #F9F9F9;
text-decoration: line-through;
}
input[type="checkbox"] {
margin: 20px;
}
p {
margin: 0;
padding: 20px;
transition: background 0.2s;
flex: 1;
font-family: 'helvetica neue';
font-size: 20px;
font-weight: 200;
border-left: 1px solid #D1E2FF;
}
.details {
text-align: center;
font-size: 15px;
}
</style>
<!--
The following is a common layout you would see in an email client.
When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down, all the checkboxes inbetween those two checkboxes should be checked.
-->
<div class="inbox">
<div class="item">
<input type="checkbox">
<p>This is an inbox layout.</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check one item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Hold down your Shift key</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check a lower item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Everything inbetween should also be set to checked</p>
</div>
<div class="item">
<input type="checkbox">
<p>Try do it with out any libraries</p>
</div>
<div class="item">
<input type="checkbox">
<p>Just regular JavaScript</p>
</div>
<div class="item">
<input type="checkbox">
<p>Good Luck!</p>
</div>
<div class="item">
<input type="checkbox">
<p>Don't forget to tweet your result!</p>
</div>
</div>
<script>
const checkboxes = document.querySelectorAll('.inbox input[type="checkbox"]');
let lastChecked;
function handleCheck(e) {
// Check if they had the shift key down
// AND check that they are checking it
let inBetween = false;
if (e.shiftKey && this.checked) {
// go ahead and do what we please
// loop over every single checkbox
checkboxes.forEach(checkbox => {
console.log(checkbox);
if (checkbox === this || checkbox === lastChecked) {
inBetween = !inBetween;
console.log('STarting to check them inbetween!');
}
if (inBetween) {
checkbox.checked = true;
}
});
}
lastChecked = this;
}
checkboxes.forEach(checkbox => checkbox.addEventListener('click', handleCheck));
console.log("ss1");
</script>
</body>
</html>
================================================
FILE: 10 - JS 实现 Checkbox 中按住 Shift 的多选功能/index-START.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<style>
html {
font-family: sans-serif;
background: #ffc600;
}
.inbox {
max-width: 400px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.1);
}
.item {
display: flex;
align-items: center;
border-bottom: 1px solid #F1F1F1;
}
.item:last-child {
border-bottom: 0;
}
input:checked+p {
background: #F9F9F9;
text-decoration: line-through;
}
input[type="checkbox"] {
margin: 20px;
}
p {
margin: 0;
padding: 20px;
transition: background 0.2s;
flex: 1;
font-family: 'helvetica neue';
font-size: 20px;
font-weight: 200;
border-left: 1px solid #D1E2FF;
}
.details {
text-align: center;
font-size: 15px;
}
</style>
<!--
The following is a common layout you would see in an email client.
When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down, all the checkboxes inbetween those two checkboxes should be checked.
-->
<div class="inbox">
<div class="item">
<input type="checkbox">
<p>This is an inbox layout.</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check one item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Hold down your Shift key</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check a lower item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Everything inbetween should also be set to checked</p>
</div>
<div class="item">
<input type="checkbox">
<p>Try do it with out any libraries</p>
</div>
<div class="item">
<input type="checkbox">
<p>Just regular JavaScript</p>
</div>
<div class="item">
<input type="checkbox">
<p>Good Luck!</p>
</div>
<div class="item">
<input type="checkbox">
<p>Don't forget to tweet your result!</p>
</div>
</div>
<script>
</script>
</body>
</html>
================================================
FILE: 10 - JS 实现 Checkbox 中按住 Shift 的多选功能/sunzhaoxiang.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>checkbox的shift多选功能</title>
<style>
</style>
</head>
<body>
<style>
html {
font-family: sans-serif;
background: #123456;
}
.inbox {
max-width: 400px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.1);
}
.item {
display: flex;
align-items: center;
border-bottom: 1px solid #F1F1F1;
}
.item:last-child {
border-bottom: 0;
}
input:checked+p {
background: #F9F9F9;
text-decoration: line-through;
}
input[type="checkbox"] {
margin: 20px;
}
p {
margin: 0;
padding: 20px;
transition: background 0.2s;
flex: 1;
font-family: 'Microsoft Yahei', 'helvetica neue';
font-size: 20px;
font-weight: 200;
border-left: 1px solid #D1E2FF;
}
.details {
text-align: center;
font-size: 15px;
}
</style>
<!--
The following is a common layout you would see in an email client.
When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down, all the checkboxes inbetween those two checkboxes should be checked.
-->
<div class="inbox">
<div class="item">
<input type="checkbox" class="N0">
<p>无双看过吗?</p>
</div>
<div class="item">
<input type="checkbox" class="N1">
<p>从零到壹全栈部落</p>
</div>
<div class="item">
<input type="checkbox" class="N2">
<p>输出是最好的学习方式</p>
</div>
<div class="item">
<input type="checkbox" class="N3">
<p>赠人玫瑰,手留余香</p>
</div>
<div class="item">
<input type="checkbox" class="N4">
<p>祥哥的说,CSDN博客 </p>
</div>
<div class="item">
<input type="checkbox" class="N5">
<p>全栈部落</p>
</div>
<div class="item">
<input type="checkbox" class="N6">
<p>你会玩魔方吗?</p>
</div>
<div class="item">
<input type="checkbox" class="N7">
<p>你会留下联系方式吗?</p>
</div>
<div class="item">
<input type="checkbox" class="N8">
<p>你会选择下载吗?</p>
</div>
</div>
<script>
const boxs = document.querySelectorAll('.inbox input[type="checkbox"]');
const boxArr = Array.from(boxs);
boxArr.forEach(box => box.addEventListener('click', handleCheck1));
// 处理方法一:用变量 inBetween 对需要选中的元素进行标记
function handleCheck0(e) {
let inBetween = false;
if(e.shiftKey && this.checked){
boxs.forEach(input => {
console.log("input的值是:",input);
console.log("this值是:",this);
if(input === lastChecked || input ===this) {
inBetween = !inBetween;
}
if(inBetween) {
console.log("on");
input.checked = true;
}
});
}
lastChecked = this;
}
// 处理方法二:利用数组索引获取需要选中的范围
let lastinput;//用来保存上一次的点击元素
let onoff; //用来保存上一次的点击状态,已提供给截取范围内CheckBox的状态
function handleCheck1(e){
if(e.shiftKey){ //若果按下shift按键再点击时进入该程序,主要用来处理索引值
let cur=boxArr.indexOf(this); //用来获取当前点击元素是第几个input
let last=boxArr.indexOf(lastinput); //用来获取上一次点击元素是第几个input
boxArr.slice(Math.min(cur,last),Math.max(cur,last)+1) //slice返回一个子数组
.forEach(item=>item.checked = onoff); //将截取出来的各项状态设置的和上下点击元素的状态一致
}
lastinput = this; //用来存放第一次或者上一次的点击元素(将当前点击元素作为上次元素,然后在有点击时和下次又组成一个范围)
onoff = lastinput.checked ? true : false; //识别第一次或者上一次点击元素的状态值
}
// 学习用全局变量来存放(由当前状态,转换而成的)上次状态。
// 设置两个变量(全局变量)来分别保存上一次的点击元素和其状态,接下里选中范围的元素状态以此为依据。
// 在shift被按下时,需要得到上次点击元素的索引,和当前点击元素(this)的索引,
// 然后将该段内input元素的状态全部统一于上次点击后的状态。
// 然后将当前点击元素作为上次元素,进行一个接龙。
</script>
</body>
</html>
<!--
1.shiftKey:检测 SHIFT 键是否被按住。
事件属性可返回一个布尔值,指示当事件发生时,“SHIFT”键是否被按下并保持住。
语法:event.shiftKey=true|false|1|0,这里的event用this无效,用e.target无效。
2.document.querySelectorAll('.inbox input[type="checkbox"]')
通过[]和属性名称来选取指定元素
-->
================================================
FILE: 11 - 自定义视频播放器/README.md
================================================
# Day11 - 自定义视频播放器
## 效果展示

第十一天是要做一个自定义的视频播放器,在具有基本样式的前提下,实现视频的播放,暂停,进度条拖拽,音量加减,播放速度加减,快进快退的功能。
## 实现思路
1. 首先需要分别将变量绑定至页面上的元素
2. 分别实现播放,暂停,声音加减,播放速度加减,拖拽快进,点击快进等函数
3. 事件绑定,将页面元素绑定相应触发事件
## CSS部分解读
- 视频播放器控制台隐藏弹出的设置
```CSS
/*控制台的样式设置*/
.player__controls {
display:flex; /*弹性布局,子元素生效*/
position: absolute;
bottom:0;
width: 100%;
transform: translateY(100%) translateY(-5px);
transition:all .3s;
flex-wrap:wrap; /*运行flex的子元素进行灵活的换行布局*/
background:rgba(0,0,0,0.1);
}
.player:hover .player__controls {
transform: translateY(0); /*这里将控制台显示出来*/
}
/*当有鼠标悬停视频播放器时,控制台弹出,此时设置进度条高度为15px*/
.player:hover .progress {
height:15px;
}
```
> 隐藏露头功能(天猫界面趴着那只猫,只露了个眼睛,悬停会跳出来,就可以据此来模拟实现)
transform: translateY(100%) translateY(-5px);
这里的控制台有一定的高度,translateY(100%),是为了将控制台通过位移结合overflow:hidden进行完全隐藏, 然后translateY(-5px),是为了将控制台顶部高度为5px的控制条,向上移动然后将其显示出来。
- input的range类型:[滑动条的自定义样式设置](https://blog.csdn.net/qq_39207948/article/details/85880391)
## 变量绑定
HTML 元素中,`video` 标签是我们的视频,而下面的 `player__controls` 就是我们自己的控制面板
```html
<div class="player">
<video class="player__video viewer" src="./mp4/demo.mp4" loop>您的浏览器不支持播放该视频!</video>
<div class="player__controls">
<div class="progress">
<div class="progress__filled"></div>
</div>
<button class="player__button toggle" title="Toggle Play">►</button>
<input type="range" name="volume" class="player__slider" min=0 max="1" step="0.05" value="1" title="音量">
<input type="range" name="playbackRate" class="player__slider" min="0.5" max="2" step="0.1" value="1" title="播放速度">
<button data-skip="-5" class="player__button">« 5s</button>
<button data-skip="5" class="player__button">5s »</button>
</div>
</div>
```
开始之前我们先把所有需要用到的元素节点先取到:
```javascript
// 获取视频播放器中的各个元素
const player = document.querySelector('.player');
const video = player.querySelector('.viewer');
const progress = player.querySelector('.progress');
const progressBar = player.querySelector('.progress__filled');
const toggle = player.querySelector('.toggle'); //播放/停止图标按钮
const ranges = Array.from(player.querySelectorAll('.player__slider')); //音量和播放速度按钮
const skipButtons = Array.from(player.querySelectorAll('[data-skip]')); //快进快退按钮
```
### 源代码功能函数的实现:
#### N0.1 点击视频播放器或者暂停/播放按钮控制视频的停-播,并且暂停/播放按钮图标随着改变
```HTML
video.addEventListener("click",togglePlay); //视频播放器监听点击事件,控制停-播
toggle.addEventListener("click",togglePlay); //toggle按钮监听点击事件,控制停-播
```
```javascript
// 使用video的两个方法使得动画暂停和运行,而判断的依据就是video.paused属性。
function togglePlay(){
video.paused ? video.play() : video.pause();
}
```
接下来需要实现,toggle图标随播放状态而改变
```HTML
video.addEventListener("play",updateButton); //根据监听video执行暂停和播放的方法来改变按钮标志
video.addEventListener("pause",updateButton); //根据监听video执行暂停和播放的方法来改变按钮标志
```
```JS
//改变toggle的图标
function updateButton(){
toggle.textContent = video.paused ? "►":"II" ;
}
```
#### NO.2 音量和播放速度滑动条功能的实现
```HTML
<input type="range" name="volume" class="player__slider" min=0 max="1" step="0.05" value="1" title="音量">
<input type="range" name="playbackRate" class="player__slider" min="0.5" max="2" step="0.1" value="1" title="播放速度">
```JS
const ranges = Array.from(player.querySelectorAll('.player__slider'));
ranges[0].addEventListener("change",handle1);
ranges[1].addEventListener("change",handle2);
// 音量改变函数
function handle1(){
console.log("yinliang:",this.value);
video.volume=this.value;
}
//播放速度改变函数
function handle2(){
console.log("shudu:",this.value);
video.playbackRate=this.value;
}
```
> 该视频播放器有两个滑动条,前者控制音量的大小,后者控制播放速度,由于input类型相同,但是绑定的事件处理函数又不相同,可以利用name值做到一个函数来处理不同的回调函数功能(设定name值和对象的属性值一致),具体代码如下:
```JS
ranges.forEach(item=>item.addEventListener('change',rangeHandle));
// 音量和播放速度控制函数
function rangeHandle(){
video[this.name]=this.value; //这里动态的进行了对象的属性值设置
}
```
> 其中需要注意的是,他们分别有一个 volume 和 playbackRate 的 name 属性,我们起这两个名字是因为他们是 video 对象里对应音量和播放速度的两个属性名。这样起名并不是必须的,但可以让我们后面 js 的操作更精简。
因为我们上面说过,input 的 name 值和 video 对象中的属性名是一样的,可以看到在 handleRangeUpdate 函数中我们利用了 this.name 的写法来代表属性,,这里的 this 一样是 addEventListener 的调用者,即 range。
#### NO.3 视频快退和快进
```HTML
<button data-skip="-5" class="player__button">« 5s</button>
<button data-skip="5" class="player__button">5s »</button>
```
```JS
const skipButtons = Array.from(player.querySelectorAll('[data-skip]')); //快进快退按钮,将伪数组转换为真正的数组
skipButtons.forEach(item=>item.addEventListener("click",skip));//给快进快退按钮添加点击事件监听函数
// 视频快退和快进控制函数
function skip(){
video.currentTime+=(+this.dataset.skip);
}
```
> video 有一个属性叫 currentTime,可以用来设置视频当前的时间。我们只要修改这个属性就可以了
要注意的是,这里就不能用 this 来访问 video 对象了,因为在这里面,this 指向的是遍历得到的每一个 button,而我们是要修改 video 的 currentTime 属性。
data-** 这样的属性以前提到过了,在 JavaScript 中需要通过 .dataset.** 来访问。因为我们获取到的是字符串,所以要通过"+"来转换成数值。
#### NO.4 进度条随着视频播放而改变,进度条拖动控制视频播放功能的实现,
- 进度条随着视频播放而改变
我们的进度条需要能在鼠标点击和拖动的时候改变视频播放的进度。我们先实现进度条随着视频播放更新进度的功能。
进度条显示进度的原理很简单,progress__filled 这个元素是一个 flex 定位的元素,我们改变其 flex-basis 的百分比值就可以调节它所占父元素的宽度。flex-basis 值代表 flex 元素在主轴方向上的初始尺寸。progressBar.style.flexBasis对其进行设值便可以动态改变进度条的填充长度。
```html
<div class="progress">
<div class="progress__filled"></div>
</div>
```
```js
const progress = player.querySelector('.progress');
const progressBar = player.querySelector('.progress__filled');
video.addEventListener("timeupdate",progressFilled);
//进度条跟随视频播放改变,那么需要实时自动来执行这个函数。
function progressFilled(){
progressBar.style.flexBasis=((video.currentTime/video.duration)*100).toFixed(2)+"%";
}
```
> 现在只要运行 progressFilled 这个函数就能够更新对应的进度条,但我们需要的是自动执行这个操作。也许你会想到利用 setInterval 设置一个定时器,其实 video 元素给我们提供了更好的方法—— timeupdate 事件。这个事件会在媒体文件的 currentTime 属性改变的时触发.
- 进度条控制视频播放功能的实现(包括点击改变和拖动改变)
实现思路:根据进度条填充部分占据整个进度条的百分比,然后结合整个video的duration,便可得出当前的video.currentTime
```JS
progress.addEventListener("click",progressClickHandle); //点击改变进度条位置来跟新视频播放进度
// 拖动事件拆分为:mousedown,mousemove,mouseup
progress.addEventListener("mousedown",()=>mousedownFlag=true); //鼠标在进度条上按下不放将标志位置为true,然后拖动即触发mousemove事件
progress.addEventListener("mousemove",progressMoveHandle); //拖动改变进度条位置来跟新视频播放进度
progress.addEventListener("mouseup",()=>mousedownFlag=false); //鼠标抬起后,标志位为false,即便触发mousemove,也不会更新视频进度
//点击进度条来改变视频播放(手动更新视频播放进度的函数,点击和拖动都要使用这个函数)
function progressClickHandle(e){
video.currentTime = (e.offsetX/progress.offsetWidth)*video.duration;
}
// 鼠标按下拖动进度条时,更新视频播放进度,从而触发timeupdate事件来校正进度条长度。
let mousedownFlag = false;
function progressMoveHandle(e){
if(mousedownFlag){
progressClickHandle(e);
}
}
```
### 涉及知识点
1.title 属性规定关于元素的额外信息。
  这些信息通常会在鼠标移到元素上时显示一段工具提示文本(tooltip text)。
2.:focus 伪类在元素获得焦点时向元素添加特殊的样式。
3.flex-wrap 属性规定flex容器是单行或者多行,同时横轴的方向决定了新行堆叠的方向。
  注释:IE 浏览器不支持此属性。
4.光标cursor的常见样式pointer|e-resize
5.flex-basis属性用于设置或检索弹性盒伸缩基准值
6.[flex属性的全面总结](https://blog.csdn.net/qq_39207948/article/details/85956861)
7.video 对象有一个叫 paused 的属性来判断视频是否在播放
  .play() 方法可以播放视频,.pause() 方法暂停播放
8.textContent 属性设置或返回指定节点的文本内容,以及它的所有后代。[结合demo理解记忆](https://blog.csdn.net/qq_39207948/article/details/86099905)
  如果您设置了 textContent 属性,会删除所有子节点,并被替换为包含指定字符串的一个单独的文本节点。
9.playbackRate 属性设置或返回音频/视频的当前播放速度。
只有 Google Chrome 和 Safari 支持 playbackRate 属性。
  1.0 正常速度
  0.5 半速(更慢)
  2.0 倍速(更快)
  -1.0 向后,正常速度
  -0.5 向后,半速
10.video.playbackRate:设置或返回音频/视频播放的速度
video.volume:设置或返回音频/视频的音量
11. 鼠标事件进行整理,具体都有哪些方法和属性。
12. currentTime 属性设置或返回音频/视频播放的当前位置(以秒计)。
  当设置该属性时,播放会跳跃到指定的位置。
13.duration 返回当前音频/视频的长度(以秒计)
14. timeupdate事件,当目前的播放位置已更改时触发。可用来实时更新进度条部分。
================================================
FILE: 11 - 自定义视频播放器/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义视频播放器</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="player">
<video class="player__video viewer" src="./mp4/demo.mp4" loop>您的浏览器不支持播放该视频!</video>
<div class="player__controls">
<div class="progress">
<div class="progress__filled"></div>
</div>
<button class="player__button toggle" title="Toggle Play">►</button>
<input type="range" name="volume" class="player__slider" min=0 max="1" step="0.05" value="1" title="音量">
<input type="range" name="playbackRate" class="player__slider" min="0.5" max="2" step="0.1" value="1" title="播放速度">
<button data-skip="-5" class="player__button">« 5s</button>
<button data-skip="5" class="player__button">5s »</button>
</div>
</div>
<script>
// 获取视频播放器中的各个元素
const player = document.querySelector('.player');
const video = player.querySelector('.viewer');
const progress = player.querySelector('.progress');
const progressBar = player.querySelector('.progress__filled');
const toggle = player.querySelector('.toggle'); //播放/停止图标按钮
const ranges = Array.from(player.querySelectorAll('.player__slider')); //音量和播放速度按钮
const skipButtons = Array.from(player.querySelectorAll('[data-skip]')); //快进快退按钮
video.addEventListener("click",togglePlay); //监听点击事件,然后调用play方法或者pause方法
toggle.addEventListener("click",togglePlay);
video.addEventListener("play",updateButton); //根据监听video执行暂停和播放的方法来改变按钮标志
video.addEventListener("pause",updateButton); //根据监听video执行暂停和播放的方法来改变按钮标志
ranges.forEach(item=>item.addEventListener('change',rangeHandle)); //只有change事件只有在blur失焦时才触发。
ranges.forEach(range => range.addEventListener('mousemove', rangeHandle)); //鼠标移动时触发事件。
skipButtons.forEach(item=>item.addEventListener("click",skip));
video.addEventListener("timeupdate",progressFilled);
progress.addEventListener("click",progressClickHandle); //点击改变进度条位置来跟新视频播放进度
// 拖动事件拆分为:mousedown,mousemove,mouseup
progress.addEventListener("mousedown",()=>mousedownFlag=true); //鼠标在进度条上按下不放将标志位置为true,然后拖动即触发mousemove事件
progress.addEventListener("mousemove",progressMoveHandle); //拖动改变进度条位置来跟新视频播放进度
progress.addEventListener("mouseup",()=>mousedownFlag=false); //鼠标抬起后,标志位为false,即便触发mousemove,也不会更新视频进度
// 使用video的两个方法使得动画暂停和运行,而判断的依据就是video.paused属性。
function togglePlay(){
video.paused ? video.play() : video.pause();
}
//改变toggle的图标
function updateButton(){
toggle.textContent = video.paused ? "►":"II" ;
}
// 音量和播放速度控制函数
function rangeHandle(){
video[this.name]=this.value; //这里动态的进行了对象的属性值设置
}
// 视频快退和快进,这里我演示视频长度仅仅为21秒
function skip(){
video.currentTime+=(+this.dataset.skip);
}
//进度条跟随视频播放改变,那么需要实时自动来执行这个函数。
function progressFilled(){
progressBar.style.flexBasis=((video.currentTime/video.duration)*100).toFixed(2)+"%";
}
//点击进度条来改变视频播放(手动更新视频播放进度的函数,点击和拖动都要使用这个函数)
function progressClickHandle(e){
video.currentTime = (e.offsetX/progress.offsetWidth)*video.duration;
}
// 鼠标按下拖动进度条时,更新视频播放进度,从而触发timeupdate事件来校正进度条长度。
let mousedownFlag = false;
function progressMoveHandle(e){
if(mousedownFlag){
progressClickHandle(e);
}
}
</script>
</body>
</html>
<!--
background-size属性
video 对象有一个叫 paused 的属性来判断视频是否在播放,播放返回true,暂停返回false
.play() 方法可以播放视频,.pause() 方法暂停播放
playbackRate 属性设置或返回音频/视频的当前播放速度。
只有 Google Chrome 和 Safari 支持 playbackRate 属性。
-->
================================================
FILE: 11 - 自定义视频播放器/style.css
================================================
* @Author: Administrator
* @Date: 2018-12-29 15:53:10
* @Last Modified by: Administrator
* @Last Modified time: 2019-01-05 19:52:35
*/
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
padding: 0;
display:flex;
background:#7A419B;
min-height:100vh;
background: linear-gradient(135deg, #7c1599 0%,#921099 48%,#7e4ae8 100%);
background-size:cover;
align-items: center;
justify-content: center;
}
/*整个视频播放器的样式设置*/
.player {
max-width:750px;
border:5px solid rgba(0,0,0,0.2);
box-shadow:0 0 20px rgba(0,0,0,0.2);
position: relative;
font-size: 0;
overflow: hidden;
}
.player__video {
width: 100%;
}
/*按钮选项的通用配置,去除浏览器默认配置*/
.player__button {
background:none;
border:0;
line-height:1;
color:white;
text-align: center;
outline:0;
padding: 0;
cursor:pointer;
max-width:50px;
}
.player__button:focus {
border-color: #ffc600;
}
/*滑动条的设置*/
.player__slider {
width:10px;
height:30px;
}
/*控制台的样式设置*/
.player__controls {
display:flex; /*弹性布局,子元素生效*/
position: absolute;
bottom:0;
width: 100%;
transform: translateY(100%) translateY(-5px);
transition:all .3s;
flex-wrap:wrap; /*运行flex的子元素进行灵活的换行布局*/
background:rgba(0,0,0,0.1);
}
/*设置控制台鼠标悬停的效果*/
.player:hover .player__controls {
transform: translateY(0);
}
/*当有鼠标悬停视频播放器时,控制台弹出,此时设置进度条高度为15px*/
.player:hover .progress {
height:15px;
}
.player__controls > * {
flex:1;
}
/*进度条样式设置*/
.progress {
flex:10;
position: relative;
display:flex;
flex-basis:100%; /*初始长度占据100%*/
height:5px;
transition:height 0.3s; /*当鼠标位于视频播放器,进度条由5px变成15PX*/
background:rgba(0,0,0,0.5);
cursor:e-resize; /*定义鼠标的样式是左右箭头滑动样式*/
}
/*进度条填充部分*/
.progress__filled {
width:50%;
background:#ffc600; /*橘黄色的进度条填充部分*/
flex:0;
flex-basis:0%;
/*transition: flex-basis 0.22s linear;*/
}
/* unholy css to style input type="range" */
/*主要是滑动条的自定义样式设置*/
/*去除系统默认的滑动条样式,主要针对基于webkit的浏览器*/
input[type=range] {
-webkit-appearance: none;
background:transparent;
width: 100%;
margin: 0 5px;
}
/*原始的控件获取到焦点时,会显示包裹整个控件的边框,所以还需要把边框取消。*/
input[type=range]:focus {
outline: none;
}
/*开始自定义滑动条控件的样式*/
/*自定义滑动控件的轨道*/
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 8.4px;
cursor: pointer;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0);
background: rgba(255,255,255,0.8); /*滑动条轨道背景颜色*/
border-radius: 3.3px;
border: 0.2px solid rgba(1, 1, 1, 0);
}
/*自定义滑动控件的滑块*/
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 15px;
width: 15px;
box-shadow: 0 0 0 rgba(0, 0, 0, 0), 0 0 0 rgba(13, 13, 13, 0);
border-radius: 50px;
background: #ffc600; /*和进度条的填充部分一个颜色*/
cursor: pointer;
margin-top: -3.5px;
box-shadow:0 0 2px rgba(0,0,0,0.2);
}
/*自定义滑动条获得焦点时的背景颜色*/
input[type=range]:focus::-webkit-slider-runnable-track {
background: #bada55;
}
/*兼容Firefox浏览器*/
input[type=range]::-moz-range-track {
width: 100%;
height: 8.4px;
cursor: pointer;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0);
background: #ffffff;
border-radius: 3.3px;
border: 0.2px solid rgba(1, 1, 1, 0);
}
input[type=range]::-moz-range-thumb {
box-shadow: 0 0 0 rgba(0, 0, 0, 0), 0 0 0 rgba(13, 13, 13, 0);
height: 15px;
width: 15px;
border-radius: 50px;
background: #ffc600;
cursor: pointer;
}
/*flex-basis 属性用于设置或检索弹性盒伸缩基准值
一个长度单位或者一个百分比,规定灵活项目的初始长度。
auto 默认值。长度等于灵活项目的长度。如果该项目未指定长度,则长度将根据内容决定。
================================================
FILE: 12 - 键盘输入序列的验证指南/README.md
================================================
### Day12 - 键盘输入序列的验证指南
#### 项目效果

文档里提供了一个 `script` 标签,供我们从 [Cornify.com](https://www.cornify.com/) 加载一个 JS 文件,调用其中的 `cornify_add()` 方法时,会在页面中追加 `p` 标签,并在 DOM 中插入一个图标。Cornify 的具体效果如上所示。
#### 实现功能:
- 可以在输入区输入内容,且内容长度为5,在匹配区中显示输入内容,长度为4,当匹配到暗码字段时,会调用`cornify_add()`,在页面中添加一个独角兽或者彩虹特效。
#### 解决思路
1. 指定可激发特效的字符串
2. 监测字符串变化
3. 事件监听
4. 正则表达式判断字符串输入
5. 处理输入,在符合条件时,调用 `cornify_add()`
#### 代码分析
1、获取元素和事件监听
```
let input=document.querySelector(".text");
let show= document.querySelector(".show");
input.addEventListener('keyup',debounce(handle,300));
```
2、对键盘输入事件进行防抖处理,避免过度调用回调函数
```
// 防抖处理
function debounce(func,wait){
let timeflag;
return function(){
clearTimeout(timeflag); //清除300ms之内之前触发的定时器。
let arg=arguments;
let _this = this;
timeflag = setTimeout(func.bind(_this,arg),wait);
}
}
```
> 防抖的主要思路:用一个函数对回调函数和等待时间进行包装,这个包装函数需要在等待时间到达后返回这个回调函数,这里可以使用setTimeout函数实现,需要注意的是需要设置一个时间变量来保存定时器,在wait等待时间之内再次触发监听事件,说明上一个的定时器还存在,需要将它清除。这里的防抖代码解决了this指向和event队象的问题,如果对这块不了解,详解见https://github.com/mqyqingfeng/Blog/issues/22
3、回调函数部分
```
//回调函数
function handle(){
input.value=input.value.slice(-5);
show.textContent=input.value.slice(-4);
let regexp = /love/gi;
if(regexp.test(show.textContent)){
cornify_add();//这个方法是由通过script引入的cornify.com中的cornify.js提供的。
}
}
```
================================================
FILE: 12 - 键盘输入序列的验证指南/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>key sequence detection</title>
<style>
html,body{
margin:0;
padding:0;
}
.contain{
width:200px;
height: 200px;
font-size:25px;
text-align: center;
position: absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
z-index:30;
}
.alertInfo,.resultInfo{
color:green;
font-weight: bold;
}
.text{
width:100px;
outline:none;
text-align: center;
font-size:25px;
color:blue;
/*border:none;*/
}
.show{
font-size:25px;
color:red;
}
</style>
</head>
<body>
<div class="contain">
<p class="alertInfo">暗码:love</p>
<input type="text" class="text" placeholder="输入区">
<p class="resultInfo">匹配显示区:</p>
<p class="show"></p>
</div>
<script type="text/javascript" src="http://www.cornify.com/js/cornify.js"></script>
<script>
let input=document.querySelector(".text");
let show= document.querySelector(".show");
input.addEventListener('keyup',debounce(handle,300));
// 防抖处理
function debounce(func,wait){
let timeflag;
return function(){
clearTimeout(timeflag); //清除300ms之内之前触发的定时器。
let arg=arguments;
let _this = this;
timeflag = setTimeout(func.bind(_this,arg),wait);
}
}
//回调函数
function handle(){
input.value=input.value.slice(-5);
show.textContent=input.value.slice(-4);
let regexp = /love/gi;
if(regexp.test(show.textContent)){
cornify_add();//这个方法是由通过script引入的cornify.com中的cornify.js提供的。
}
}
</script>
</body>
</html>
================================================
FILE: 13 - 图片随屏幕滚动而滑入滑出的效果/README.md
================================================
### Day13 - 图片随屏幕滚动而滑入滑出的效果
#### 项目效果

实现页面内伴随着鼠标滚动,到每个图片时图片出现,并伴随着动画出现。
#### 实现思路
- 1、判断图片的滑出和滑入的位置(JS实现)
- 2、根据位置来决定图片的过渡效果(CSS实现)
### 代码解析
#### JS部分
```JS
let imgs=Array.from(document.querySelectorAll("img"));//转换为真正的数组
window.addEventListener("scroll",throttle(handle,100));
// 节流函数(定时器实现方式)
function throttle(func,wait){
let timeflag;
return function(){
let content=this;
let args=arguments;
if(!timeflag){
timeflag=setTimeout(function(){
timeflag=null;
func.apply(content,args)
},wait);
}
}
}
// 事件处理函数,每2秒执行一次,停止后也会执行一次。(无首有尾)
// 判断图片位置的函数,遍历每一个图片,判断每一个图片是否出现在视图中,来决定是否加类。
function handle(){
imgs.forEach(img=>{
let imgHalfBoolean=(window.scrollY+window.innerHeight)>img.offsetTop+img.height/2;//图片划过一半的判断条件
let imgFullBoolean=window.scrollY<img.offsetTop+img.height;//图片没有完全滑入顶部的判断条件
// 图片滑出一半且没有完全滑出时,加上过渡类
if(imgHalfBoolean&&imgFullBoolean){
img.classList.add("active");
}else{
img.classList.remove("active");
}
})
}
```
##### 代码知识点分析
- 1、节流处理: 此外由于每次滚动都触发监听事件,会降低 JavaScript 运行性能,所以用 throttle 函数来降低触发的次数。
- 2、图片位置的确定:主要通过四个距离值
(1)window.scrollY:页面滚动过的距离。
(2)window.innerHeight:浏览器的视距,不包括上下工具栏。
(3)img.offsetTop:图片相对于父元素的距离。
(4)img.height:图片自身的高度值。
详细的解释如下图:

```JS
let imgHalfBoolean=(window.scrollY+window.innerHeight)>img.offsetTop+img.height/2;//图片划过一半的判断条件
let imgFullBoolean=window.scrollY<img.offsetTop+img.height;//图片没有完全滑入顶部的判断条件
```
其中橙色半透明部分指可滚动页面整体,橙色标注部分是指会随着页面滚动而变化的尺寸,黑色标注的尺寸是固定不变的。 页面的滑动过程经过了两个临界点,一个是下滑到图片的一半处,另一个是完全滑过图片使图片已不再视窗之内,分别决定了图片的显示和隐藏。
#### CSS部分
```CSS
html{
box-sizing:border-box;
background: linear-gradient(silver,#ffc600);
font-family: 'helvetica neue';
font-size:20px;
font-weight: 200;
}
body{
padding:0;
margin:0;
}
*,*:before,*:after{
box-sizing:inherit;
}
h1{
margin-top:0;
}
.site-wrap{
max-width:700px;
margin:100px auto;
background: whitesmoke;
padding:40px;
/*text-align:justify;text-align 属性规定元素中的文本的水平对齐方式。*/
}
p{
text-indent:2em; /*text-indent 属性规定文本块中首行文本的缩进。*/
word-break:break-all;
}
/* 图片的位置设置 */
.align-left{
float:left;
margin-right:30px;
}
.align-right{
float:right;
margin-left:30px;
}
/* 所有的图片处于隐藏状态,并将所有的属性改变设置为0.5s的过渡效果 */
.slide-in {
opacity:0;
transition:all .1s;
}
/* 位于左侧和右侧图片的额初始位置和大小状态 */
.align-left.slide-in{
transform:translateX(-30%) scale(0.9);
}
.align-right.slide-in{
transform: translate(30%) scale(0.9);
}
/* 具有active类的图片效果 */
.slide-in.active{
opacity: 1;
transform: translate(0%) scale(1);
}
```
================================================
FILE: 13 - 图片随屏幕滚动而滑入滑出的效果/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="site-wrap">
<h1>Slide In On Scroll</h1>
<p>Consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui libero ipsum delectus quidem
dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas laborum nam! Fuga ad tempora
in aspernatur pariaturlores sunt esse magni, ut, dignissimos.</p>
<p>Lorem ipsum cupiditate, corporis a qui libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut
asperiores omnis blanditiis quod quas laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt
esse magni, ut, dignissimos.</p>
<p>Adipisicing elit. Tempore tempora rerum..</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui
libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas
laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui
libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas
laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore tempora rerum, est autem cupiditate, corporis a qui
libero ipsum delectus quidem dolor at nulla, adipisci veniam in reiciendis aut asperiores omnis blanditiis quod quas
laborum nam! Fuga ad tempora in aspernatur pariatur fugit quibusdam dolores sunt esse magni, ut, dignissimos.</p>
<img src="http://unsplash.it/400/400" class="align-left slide-in">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptates, deserunt facilis et iste corrupti omnis tenetur
est. Iste ut est dicta dolor itaque adipisci, dolorum minima, veritatis earum provident error molestias. Ratione magni
illo sint vel velit ut excepturi consectetur suscipit, earum modi accusamus voluptatem nostrum, praesentium numquam,
reiciendis voluptas sit id quisquam. Consequatur in quis reprehenderit modi perspiciatis necessitatibus saepe, quidem,
suscipit iure natus dignissimos ipsam, eligendi deleniti accusantium, rerum quibusdam fugit perferendis et optio recusandae
sed ratione. Culpa, dolorum reprehenderit harum ab voluptas fuga, nisi eligendi natus maiores illum quas quos et aperiam
aut doloremque optio maxime fugiat doloribus. Eum dolorum expedita quam, nesciunt</p>
<img src="http://unsplash.it/400/401" class="align-right slide-in">
<p> at provident praesentium atque quas rerum optio dignissimos repudiandae ullam illum quibusdam. Vel ad error quibusdam,
illo ex totam placeat. Quos excepturi fuga, molestiae ea quisquam minus, ratione dicta consectetur officia omnis, doloribus
voluptatibus? Veniam ipsum veritatis architecto, provident quas consequatur doloremque quam quidem earum expedita,
ad delectus voluptatum, omnis praesentium nostrum qui aspernatur ea eaque adipisci et cumque ab? Ea voluptatum dolore
itaque odio. Eius minima distinctio harum, officia ab nihil exercitationem. Tempora rem nemo nam temporibus molestias
facilis minus ipsam quam doloribus consequatur debitis nesciunt tempore officiis aperiam quisquam, molestiae voluptates
cum, fuga culpa. Distinctio accusamus quibusdam, tempore perspiciatis dolorum optio facere consequatur quidem ullam
beatae architecto, ipsam sequi officiis dignissimos amet impedit natus necessitatibus tenetur repellendus dolor rem!
Dicta dolorem, iure, facilis illo ex nihil ipsa amet officia, optio temporibus eum autem odit repellendus nisi. Possimus
modi, corrupti error debitis doloribus dicta libero earum, sequi porro ut excepturi nostrum ea voluptatem nihil culpa?
Ullam expedita eligendi obcaecati reiciendis velit provident omnis quas qui in corrupti est dolore facere ad hic, animi
soluta assumenda consequuntur reprehenderit! Voluptate dolor nihil veniam laborum voluptas nisi pariatur sed optio
accusantium quam consectetur, corrupti, sequi et consequuntur, excepturi doloremque. Tempore quis velit corporis neque
fugit non sequi eaque rem hic. Facere, inventore, aspernatur. Accusantium modi atque, asperiores qui nobis soluta cumque
suscipit excepturi possimus doloremque odit saepe perferendis temporibus molestiae nostrum voluptatum quis id sint
quidem nesciunt culpa. Rerum labore dolor beatae blanditiis praesentium explicabo velit optio esse aperiam similique,
voluptatem cum, maiores ipsa tempore. Reiciendis sed culpa atque inventore, nam ullam enim expedita consectetur id
velit iusto alias vitae explicabo nemo neque odio reprehenderit soluta sint eaque. Aperiam, qui ut tenetur, voluptate
doloremque officiis dicta quaerat voluptatem rerum natus magni. Eum amet autem dolor ullam.</p>
<img src="http://unsplash.it/200/500" class="align-left slide-in">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero
placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero
perferendis, deserunt et incidunt eveniet <img src="http://unsplash.it/200/200" class="align-right slide-in"> temporibus
doloremque possimus facilis. Possimus labore, officia dolore! Eaque ratione saepe, alias harum laboriosam deserunt
laudantium blanditiis eum explicabo placeat reiciendis labore iste sint. Consectetur expedita dignissimos, non quos
distinctio, eos rerum facilis eligendi. Asperiores laudantium, rerum ratione consequatur, culpa consectetur possimus
atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel non incidunt, quod doloremque dignissimos necessitatibus
aliquid laboriosam architecto at cupiditate commodi expedita in, quae blanditiis. Deserunt labore sequi, repellat laboriosam
est, doloremque culpa reiciendis tempore excepturi. Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui
rem quam error sint, libero. Laboriosam rem, ratione. Autem blanditiis</p>
<p>laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita, laborum reprehenderit
ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda natus cupiditate hic
quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos dolore culpa
debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro saepe iure sunt
eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo, ratione eveniet,
provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus quia facilis
iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores quis necessitatibus
distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam, quasi aspernatur quas
odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus, odit excepturi voluptate
saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore non magnam, amet, facere
ducimus accusantium eos veritatis neque.</p>
<img src="http://unsplash.it/400/400" class="align-right slide-in">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio maiores adipisci quibusdam repudiandae dolor vero
placeat esse sit! Quibusdam saepe aperiam explicabo placeat optio, consequuntur nihil voluptatibus expedita quia vero
perferendis, deserunt et incidunt eveniet temporibus doloremque possimus facilis. Possimus labore, officia dolore!
Eaque ratione saepe, alias harum laboriosam deserunt laudantium blanditiis eum explicabo placeat reiciendis labore
iste sint. Consectetur expedita dignissimos, non quos distinctio, eos rerum facilis eligendi. Asperiores laudantium,
rerum ratione consequatur, culpa consectetur possimus atque ab tempore illum non dolor nesciunt. Neque, rerum. A vel
non incidunt, quod doloremque dignissimos necessitatibus aliquid laboriosam architecto at cupiditate commodi expedita
in, quae blanditiis. Deserunt labore sequi, repellat laboriosam est, doloremque culpa reiciendis tempore excepturi.
Enim nostrum fugit itaque vel corporis ullam sed tenetur ipsa qui rem quam error sint, libero. Laboriosam rem, ratione.
Autem blanditiis laborum neque repudiandae quam, cumque, voluptate veritatis itaque, placeat veniam ad nisi. Expedita,
laborum reprehenderit ratione soluta velit natus, odit mollitia. Corporis rerum minima fugiat in nostrum. Assumenda
natus cupiditate hic quidem ex, quas, amet ipsum esse dolore facilis beatae maxime qui inventore, iste? Maiores dignissimos
dolore culpa debitis voluptatem harum, excepturi enim reiciendis, tempora ab ipsam illum aspernatur quasi qui porro
saepe iure sunt eligendi tenetur quaerat ducimus quas sequi omnis aperiam suscipit! Molestiae obcaecati officiis quo,
ratione eveniet, provident pariatur. Veniam quasi expedita distinctio, itaque molestiae sequi, dolorum nisi repellendus
quia facilis iusto dignissimos nam? Tenetur fugit quos autem nihil, perspiciatis expedita enim tempore, alias ab maiores
quis necessitatibus distinctio molestias eum, quidem. Delectus impedit quidem laborum, fugit vel neque quo, ipsam,
quasi aspernatur quas odio nihil? Veniam amet reiciendis blanditiis quis reprehenderit repudiandae neque, ab ducimus,
odit excepturi voluptate saepe ipsam. Voluptatem eum error voluptas porro officiis, amet! Molestias, fugit, ut! Tempore
non magnam, amet, facere ducimus accusantium eos veritatis neque.</p>
<script src="index.js"></script>
</body>
</html>
================================================
FILE: 13 - 图片随屏幕滚动而滑入滑出的效果/index.js
================================================
let imgs=Array.from(document.querySelectorAll("img"));//转换为真正的数组
// imgs.forEach(item=>item.addEventListener("scroll",handle));
window.addEventListener("scroll",throttle(handle,100));
// 节流函数(定时器实现方式)
function throttle(func,wait){
let timeflag;
return function(){
let content=this;
let args=arguments;
if(!timeflag){
timeflag=setTimeout(function(){
timeflag=null;
func.apply(content,args)
},wait);
}
}
}
// 事件处理函数,每2秒执行一次,停止后也会执行一次。(无首有尾)
// 判断图片位置的函数,遍历每一个图片,判断每一个图片是否出现在视图中,来决定是否加类。
function handle(){
imgs.forEach(img=>{
let imgHalfBoolean=(window.scrollY+window.innerHeight)>img.offsetTop+img.height/2;//图片划过一半的判断条件
let imgFullBoolean=window.scrollY<img.offsetTop+img.height;//图片没有完全滑入顶部的判断条件
// 图片滑出一半且没有完全滑出时,加上过渡类
if(imgHalfBoolean&&imgFullBoolean){
img.classList.add("active");
}else{
img.classList.remove("active");
}
})
}
================================================
FILE: 13 - 图片随屏幕滚动而滑入滑出的效果/style.css
================================================
html{
box-sizing:border-box;
background: linear-gradient(silver,#ffc600);
font-family: 'helvetica neue';
font-size:20px;
font-weight: 200;
}
body{
padding:0;
margin:0;
}
*,*:before,*:after{
box-sizing:inherit;
}
h1{
margin-top:0;
}
.site-wrap{
max-width:700px;
margin:100px auto;
background: whitesmoke;
padding:40px;
/*text-align:justify;text-align 属性规定元素中的文本的水平对齐方式。*/
}
p{
text-indent:2em; /*text-indent 属性规定文本块中首行文本的缩进。*/
word-break:break-all;
}
/* 图片的位置设置 */
.align-left{
float:left;
margin-right:30px;
}
.align-right{
float:right;
margin-left:30px;
}
/* 所有的图片处于隐藏状态,并将所有的属性改变设置为0.5s的过渡效果 */
.slide-in {
opacity:0;
transition:all .1s;
}
/* 位于左侧和右侧图片的额初始位置和大小状态 */
.align-left.slide-in{
transform:translateX(-30%) scale(0.9);
}
.align-right.slide-in{
transform: translate(30%) scale(0.9);
}
/* 具有active类的图片效果 */
.slide-in.active{
opacity: 1;
transform: translate(0%) scale(1);
}
================================================
FILE: 14 - JavaScript 引用和值拷贝/README.md
================================================
### Day14 - JavaScript 引用和值拷贝
#### 项目效果
效果展示如下面的图片,另外也可以自行在浏览器的控制台中打印显示进行理解。
#### 按值操作(深拷贝)
基本类型由值操作。以下类型在JavaScript中被视为基本类型:
- `String`
- `Number`
- `Boolean`
- `Null`
- `Undefined`
基本数据类型赋值你可以理解成值拷贝,从深拷贝和浅拷贝的角度去思考的话,你可以理解成`深拷贝`,当你修改一个变量的值时,不会影响其他变量的值。
##### 实例
```Javascript
let age1 = 100; //number类型
let age2 = age1; //值的赋值属于深拷贝
console.log(age1, age2); //100 100
age1 = 200;
console.log(age1, age2); //200 100
```
> 值的复制:是给另外一个变量创建了一个存储空间,二者彼此独立,修改数据互不影响。
由此可见,基本类型,按值操作,新建的变量会将值复制给新的变量,各自的改变不会互相影响。
#### 通过引用操作 (浅拷贝)
对象`Object`类型是按引用操作的,如果它不是基本类型中的一个,那么它就是对象,这里如果我们细究的话,JavaScript中每一个东西都可以当做对象,甚至是基本的类型(不包括`null`和`undefined`),但我们尽量不要钻这个牛角尖。
一些JavaScript中的对象:
`Object`
`Function`
`Array`
`Set`
`Map`
那对于数组来说,情况是否一样呢?延续上面的思路,下面我们来看看数组。
```JS
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team = players;
console.log(players, team); // ["Wes", "Sarah", "Ryan", "Poppy"] ["Wes", "Sarah", "Ryan", "Poppy"]
team[3] = 'Lux';
console.log(players, team); // ["Wes", "Sarah", "Ryan", "Lux"] ["Wes", "Sarah", "Ryan", "Lux"]
```
> 结果显示原数组 plaryers 也被修改了。为什么会这样?因为 team 只是这个数组的引用,并不是它的复制。team 和 players 这两个变量指向的是同一个数组,也即是仅仅浅拷贝了指针,这个变量存储的指向原来数组存储空间的方向,两个变量指向的内容是一样的,这样修改其中一个另一个也会跟着改变。
#### 数组复制的解决方法 (数组深拷贝):
- 方法一 Array.prototype.slice()
由于运行 slice ,原数组不会被修改。所以如果修改这两个数组中任意 一个,另一个都不会受到影响。
```JS
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team2 = players.slice();
team2[3] = 'Lux2';
console.log(players, team2); // ["Wes", "Sarah", "Ryan", "Poppy"] ["Wes", "Sarah", "Ryan", "Lux2"]
```
- 方法二 Array.prototype.concat()
concat() 方法是用来合并数组的,它也不会更改原有的数组,而是返回一个新数组,所以可以将 players 数组与一个空数组合并,得到的结果就符合预期了。
```JS
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team3 = [].concat(players); //或者写成 team3 = players.concat();
team3[3] = 'Lux3';
console.log(players, team3); // ["Wes", "Sarah", "Ryan", "Poppy"] ["Wes", "Sarah", "Ryan", "Lux3"]
```
- 方法三 ES6 扩展运算符
扩展语法可以像扩展参数列表一样来扩展数组,效果与上述方法类似,但比较简洁。
```JS
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team4 = [...players]; //扩展运算符这里实际上是“打散操作”
team4[3] = 'Lux4';
console.log(players, team4); //["Wes", "Sarah", "Ryan", "Poppy"] ["Wes", "Sarah", "Ryan", "Lux4"]
```
- 方法四 Array.from()
Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。
```JS
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team5 = Array.from(players);
team4[3] = 'Lux5';
console.log(players, team4); //["Wes", "Sarah", "Ryan", "Poppy"] ["Wes", "Sarah", "Ryan", "Lux5"]
```
#### object对象的复制方法(对象深拷贝)
对于 Object 数据,我们用一个 person 对象来试试。
先声明对象:
```JS
const person = {
name: 'Web sun',
age: 25
};
```
然后思考一下如何可以取得它的复制,试试想当然的做法:
```JS
const captain = person;
captain.number = 99;
console.log(person, captain);
// Object {name: "Web sun", age: 25, number: 99}
// Object {name: "Web sun", age: 25, number: 99}
```
这样好像行不通,person 的值也被更改了,那该如何才能真正复制呢,来达到两个变量之间互不影响?
- 方法一 ES6的Object.assign()
`Object.assign(target, ...sources)`用于对象的合并,将源对象中的所有可枚举属性,复制到目标对象中,并返回合并后的目标对象。后来的源对象的属性值,将会覆盖它之前的对象的属性。
```JS
var obj={
name:'sun',
height:'180cm'
}
var copyobj=Object.assign({},obj);
copyobj.name='zhao';
console.log(obj); //{name: "sun", height: "180cm"}
console.log(copyobj); //{name: "zhao", height: "180cm"}
```
- 方法二 ES6的扩展运算符
用于取出参数对象的所有可遍历属性,拷贝到当前属性中。
```JS
var obj={
name:'sun',
height:180cm
}
var copyobj={...obj};
copyobj.name='zhao';
console.log(obj); //{name: "sun", height: "180cm"}
console.log(copyobj); //{name: "zhao", height: "180cm"}
```
#### 注意:上述对数组深拷贝的方法仅仅适用第一层级的值是基本数据类型的情况。若第一层级的值为对象或者数组等引用类型时,则上述方法失效。对象中若仍嵌套有对象,同样失效。
## 拷贝所有层级
##### 1.不仅拷贝第一层级,还能拷贝数组或对象所有层级的各项值。
##### 2.不是单独针对数组或对象,而是能够通用与数组、对象和其他复杂的JSON形式的对象。
- 方法一 `JSON.parse(JSON.stringify(xxx))`
```JS
var array=[{number:1},{number:2},{number:3}];
var copyArray=JSON.parse(JSON.stringify(array));
copyArray[0].number=100;
console.log(array);//[{number:1},{number:2},{number:3}];
console.log(copyArray); //[{number:100},{number:2},{number:3}];
```
> 序列化:将一个JavaScript值(数组或对象)转换为一个JSON字符串;JSON.stringify()
反序列化:将一个JSON字符串转换为对象;JSON.parse()
- 方法二 深拷贝递归函数
```JS
function deepcopy(obj){
if(typeof obj == 'object') { //说明参数是一个对象类型,但是数组也是对象,需要具体判断是不是数组类型。
var result = obj.constructor==Array ? [] : {};
for(let i in obj){ //遍历obj的每一项
result[i]=typeof obj[i]=="object" ? deepcopy(obj[i]) : obj[i];
}
}else{
var result = obj;
}
return result;
}
```
================================================
FILE: 14 - JavaScript 引用和值拷贝/index-FINISHED.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS Reference VS Copy</title>
</head>
<body>
<script>
/*
基本数据类型:
1.String
2.Number
3.Boolean
4.Null
5.Undefined
*/
let age = 100;
let age2 = age;
console.log(age, age2);
age = 200;
console.log(age, age2);
let name = 'liyuechun';
let name2 = name;
console.log(name, name2);
name = 'liyc';
console.log(name, name2);
/*
引用
1.Object
2.Function
3.Array
4.Set
5.Map
*/
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
// 引用拷贝
const team = players;
console.log(`players: ${players}`, `team:${team}`);
// 我们做如下操作:
team[3] = 'Lux';
console.log(`players: ${players}`, `team:${team}`);
const team2 = players.slice();
console.log(`players: ${players}`, `team:${team}`, `team2:${team2}`);
/*
深拷贝:
*/
// 创建新数组并且将原来的数组拼接到新数组中
const team3 = [].concat(players);
// ES6 Spread语法
const team4 = [...players];
team4[3] = 'heeee hawww';
console.log(`team4:${team4}`);
const team5 = Array.from(players);
console.log(`team5:${team5}`);
//创建object对象
const person = {
name: '黎跃春',
age: 29
};
// 浅拷贝
console.log(`person:${person}`);
const captain = person;
captain.number = 99;
console.log(`person:${person}`);
console.log(`captain:${captain}`);
// 深拷贝
const cap2 = Object.assign({}, person, {
number: 99,
age: 12
});
console.log(`cap2:${cap2}`);
// 对象的嵌套
const liyc = {
name: '黎跃春',
age: 100,
social: {
sina: '黎跃春-追时间的人',
facebook: '黎跃春'
}
};
console.log(`liyc:${liyc}`);
const dev = Object.assign({}, liyc);
console.log(`dev:${dev}`);
const dev2 = JSON.stringify(liyc);
console.log(`dev2:${dev2}`);
const dev3 = JSON.parse(JSON.stringify(liyc));
console.log(`dev3:${dev3}`);
</script>
</body>
</html>
================================================
FILE: 14 - JavaScript 引用和值拷贝/index-START.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS Reference VS Copy</title>
</head>
<body>
<script>
// start with strings, numbers and booleans
// Let's say we have an array
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
// and we want to make a copy of it.
// You might think we can just do something like this:
// however what happens when we update that array?
// now here is the problem!
// oh no - we have edited the original array too!
// Why? It's because that is an array reference, not an array copy. They both point to the same array!
// So, how do we fix this? We take a copy instead!
// one way
// or create a new array and concat the old one in
// or use the new ES6 Spread
// now when we update it, the original one isn't changed
// The same thing goes for objects, let's say we have a person object
// with Objects
// and think we make a copy:
// how do we take a copy instead?
// We will hopefully soon see the object ...spread
// Things to note - this is only 1 level deep - both for Arrays and Objects. lodash has a cloneDeep method, but you should think twice before using it.
</script>
</body>
</html>
================================================
FILE: 15 - LocalStorage/README.md
================================================
## 效果图

第十五天主要是练习`LocalStorage`(本地存储)以及事件委托的使用,使用场景是一个简单的`todo list`的应用,实现基本的添加`item`,切换完成状态,将所有`todo`项存储在`localstorage`中,保证刷新浏览器后数据不丢失。
## 主要思路
* 提前预定义好所有用到的变量;
```Javascript
// 添加item的按钮
const addItems = document.querySelector('.add-items');
// todolist列表
const itemsList = document.querySelector('.plates');
// 本地缓存的所有todoitem
const items = JSON.parse(localStorage.getItem('items')) || [];
```
* 为`addItems`按钮添加事件函数,添加一个新的`todo item`并存储到本地缓存;
* 监听`checkbox`的点击事件,切换是否完成的状态,并更新本地存储,保证刷新本页面是数据不会丢失;
* 分别设置两个监听器,监听`addItems`的`submit`事件,和`itemsList`的点击事件;
## 添加item事件
* 添加item的主要代码如下
```Javascript
function addItem(e) {
// 阻止默认事件的触发,防止在提交后页面自己刷新
e.preventDefault();
const text = this.querySelector('[name=item]').value;
const item = {
// ES6的简写形式 => text: text;
text,
done: false
};
items.push(item);
localStorage.setItem('items', JSON.stringify(items));
populateList(items, itemsList);
// 添加完数据后,重置输入框
this.reset();
}
addItems.addEventListener('submit', addItem);
```
* 监听`addItems`的`submit`事件,当用户点击`enter`或者点击右侧的`submit`按钮的时候触发;
* `text,`是ES6的缩写形式,即代表`text: text;`
*`localStorage`的常用API:
* `localStorage.setItem(‘key’,value); ->` 设置本地缓存,以`key-value`的形式
* `localStorage.getItem(‘key’); ->` 根据参数key取得本地缓存中对应的值
* `localStorage.clear(); ->` 清空本地的缓存
* `localStorage.removeItem(‘key’); ->` 删除key所对应的那一条本地缓存
* `localStorage`中只能存储字符串,所以我们经常会用到: `JSON.stringify(object)`将一个对象转换为字符串,再使用`JSON.parse(objSting)`将一个对象字符串转换为对象
* `this.reset();`代表将表单重置,清空表单中的值,在我们进行了一次submit之后,如果不重置表单的话,表单中的值将不会消失,这将大大影响用户体验
## 切换完成状态事件
```Javascript
function toggleDone(e) {
// if(!e.target.nodeName.match('INPUT')) return;
// 跳过所有的input,只处理label
if (!e.target.matches('input')) return;
const node = e.target;
const index = node.dataset.index;
items[index].done = !items[index].done;
localStorage.setItem('items', JSON.stringify(items));
populateList(items, itemsList);
}
itemsList.addEventListener('click', toggleDone);
```
* 此处使用到了事件委托,所谓事件委托,我是这么理解的:
* 假设我们队一个input列表进行了事件监听,但我们如法保证,此列表在接下来的状态下是否进行了更新,刷新等改变原来节点的操作,如果有这样的操作出现,那么我们之前的事件监听器就无法再起到监听的作用;
* 但我们可以对input列表的父元素进行事件监听,让它们的父元素处于监听状态,当我们所点击的元素是其子元素的话,就告诉它的子元素,执行相应的事件;
* 相当于委托父元素帮我们监听所有子元素,这样无论子元素列表进行怎么样的更新,改变,只要父元素节点不发生改变就可以持续起到监听的 作用。
* 通过`e.target.matches('input')`可以判断所点击的元素是不是input元素,`e.target`返回所点击的宿主元素。
* 通过获取到所点击的列表的序号,更改其`done`属性,更新后进行存储,就可以实现完成状态的事件。
## 列表显示函数
```Javascript
// 设置默认值,防止传参数出错的时候crash
function populateList(populates = [], place {
place.innerHTML = populates.map((populate, index) => {
//之所以用‘’空字符是因为如果用null的话,会出现在html中
return `
<li>
<input type="checkbox" id=item${index} data-index=${index} ${populate.done ? 'checked' : ''}>
<label for="item${index}">${(populate.text)}</label>
</li>
`;
// join()之后一定要加'',表示空字符,否则会加入逗号,造成错误
}).join('');
}
```
* 将所有的列表项转化为`li`传入页面的`html`中
* 将此函数抽象出来,以方便以后实现同样类似的操作,将一个数组中的元素动态添加到页面的一个节点中
## 清除缓存
```Javascript
// 在关闭浏览器时或者刷新页面时清除缓存
window.onbeforeunload = function (e) {
localStorage.removeItem('items');
e.returnValue=true; //弹出提示框阻止onunload事件的运行
};
```
> onbeforeunload 事件在即将离开当前页面(刷新或关闭)时触发。该事件可用于弹出对话框,提示用户是继续浏览页面还是离开当前页面。
注意:当该事件返回的字符串(事前设置好的event.returnValue的值)不为null或者undefined时,弹出确认窗口让用户自行选择是否关闭当前页面。(换句话说就是使用event.returnValue可以阻止onunload卸载页面,因为它会出现一个提示窗口,让用户自己判断是否离开当前页)一些浏览器将该事件返回的字符串显示在弹出窗上。从Firefox 4、 Chrome 51、Opera 38 和Safari 9.1开始,通用确认信息代替事件返回的字符串。
* 有些时候,我们仅仅是为了练习`localStorage`的使用,并不想在浏览器中留下过多的缓存,那么这个方法就派上了用场。
* 当页面重新刷新或者关闭之前,执行`localStorage.removeItem('items’);`清除页面的缓存。
* **慎用**,尤其在生产环境中。
## 整体代码架构
```Javascript
const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
const items = JSON.parse(localStorage.getItem('items')) || [];
function addItem(e) {
e.preventDefault();
const text = this.querySelector('[name=item]').value;
const item = {
text, // ES6的简写形式 => text = text;
done: false
};
items.push(item);
localStorage.setItem('items', JSON.stringify(items));
populateList(items, itemsList);
this.reset(); // 添加完数据后,重置输入框
}
function populateList(populates = [], place) { // 设置默认值,防止传参数出错的时候crash
place.innerHTML = populates.map((populate, index) => {
return `
<li>
<input type="checkbox" id=item${index} data-index=${index} ${populate.done ? 'checked' : ''}>
<label for="item${index}">${(populate.text)}</label>
</li>
`; //之所以用‘’空字符是因为如果用null的话,会出现在html中
}).join(''); // join()之后一定要加'',表示空字符,否则会加入逗号,造成错误
}
function toggleDone(e) {
// if(!e.target.nodeName.match('INPUT')) return;
if (!e.target.matches('input')) return; // 跳过所有的input,只处理label
const node = e.target;
const index = node.dataset.index;
items[index].done = !items[index].done;
localStorage.setItem('items', JSON.stringify(items));
populateList(items, itemsList);
}
addItems.addEventListener('submit', addItem);
itemsList.addEventListener('click', toggleDone);
populateList(items, itemsList);
```
* 在页面加载的时候,先获取本地缓存的`items`,若存在就传给变量`items`,若第一次登录或者无`item`,初始化为空数组;
* 在页面加载的时候先加载页面的所有`todolist`,执行一遍`populateList(items, itemsList);`函数即可。
================================================
FILE: 15 - LocalStorage/demo.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="wrapper">
<h2>LOCAL TAPAS</h2>
<p></p>
<ul class="plates">
<li>Loading Tapasss...</li>
</ul>
<form class="add-items">
<input type="text" name="item" placeholder="Item Name" required>
<input type="submit" value="+ Add Item">
</form>
</div>
<script>
let Form=document.querySelector(".add-items");
let List=document.querySelector(".plates");
let itemsLocalStorage = JSON.parse(localStorage.getItem("items")) || [];
Form.addEventListener("submit",addItems);
function addItems(e){
e.preventDefault();
let item=this.querySelector("[name=item]").value;
let contain={
text:item,
done:false
}
itemsLocalStorage.push(contain);
localStorage.setItem("items",JSON.stringify(itemsLocalStorage));
addList(itemsLocalStorage,List);
this.reset();
// 将localStorage中保存条目渲染到展示列表中。
function addList(arr,place){
place.innerHTML = arr.map((item,index)=>{
return `
<li>
<input type="checkbox" id=${index} ${item.done ? checked : false}></input>
<label for=${index}>${item.text}</label>
</li>
`
}).join("");
}
}
</script>
</body>
</html>
================================================
FILE: 15 - LocalStorage/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LocalStorage</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!--
Fish SVG Cred:
https://thenounproject.com/search/?q=fish&i=589236
-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 512 512"
enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<path d="M495.9,425.3H16.1c-5.2,0-10.1,2.9-12.5,7.6c-2.4,4.7-2.1,10.3,0.9,14.6l39,56.4c2.6,3.8,7,6.1,11.6,6.1h401.7 c4.6,0,9-2.3,11.6-6.1l39-56.4c3-4.3,3.3-9.9,0.9-14.6C506,428.2,501.1,425.3,495.9,425.3z M449.4,481.8H62.6L43,453.6H469 L449.4,481.8z"
/>
<path d="M158.3,122c7.8,0,14.1-6.3,14.1-14.1V43.4c0-7.8-6.3-14.1-14.1-14.1c-7.8,0-14.1,6.3-14.1,14.1v64.5 C144.2,115.7,150.5,122,158.3,122z"
/>
<path d="M245.1,94.7c7.8,0,14.1-6.3,14.1-14.1V16.1c0-7.8-6.3-14.1-14.1-14.1C237.3,2,231,8.3,231,16.1v64.5 C231,88.4,237.3,94.7,245.1,94.7z"
/>
<path d="M331.9,122c7.8,0,14.1-6.3,14.1-14.1V43.4c0-7.8-6.3-14.1-14.1-14.1s-14.1,6.3-14.1,14.1v64.5 C317.8,115.7,324.1,122,331.9,122z"
/>
<path d="M9.6,385.2c5.3,2.8,11.8,1.9,16.2-2.2l50.6-47.7c56.7,46.5,126.6,71.9,198.3,71.9c0,0,0,0,0,0 c87.5,0,169.7-36.6,231.4-103.2c5-5.4,5-13.8,0-19.2c-61.8-66.5-144-103.2-231.4-103.2c-72,0-142.2,25.6-199,72.5l-50-47.1 c-4.4-4.1-10.9-5-16.2-2.2c-5.3,2.8-8.3,8.7-7.4,14.6l11.6,75L2.2,370.6C1.3,376.5,4.2,382.4,9.6,385.2z M380.9,230.8 c34.9,14.3,67.2,35.7,95.3,63.6c-10.1,10-20.8,19.2-31.9,27.5c-22.4-3.3-29.6-8.8-30.7-9.7c-4-5.7-11.8-7.7-18.1-4.4 c-6.9,3.6-9.5,12.2-5.9,19.1c1.9,3.5,7.3,10.3,22.4,16c-10.1,5.7-20.5,10.7-31.1,15.1C352.4,320.2,352.4,268.6,380.9,230.8z M36.3,255.6l29.4,27.7c5.3,5,13.6,5.1,19.1,0.3c53.2-47.6,120.7-73.7,190-73.7c26.9,0,53.2,3.9,78.5,11.3 c-29.3,44.6-29.3,102,0,146.6c-25.3,7.4-51.6,11.3-78.5,11.3c-69,0-136.3-26-189.4-73.2c-2.7-2.4-13.4-6.3-19.1,0.3l-30.1,28.3 l5.7-40C42.2,293,36.3,255.6,36.3,255.6z"
/>
<circle cx="398.8" cy="273.8" r="14.1" />
</g>
</svg>
<div class="wrapper">
<h2>LOCAL TAPAS</h2>
<p></p>
<ul class="plates">
<li>Loading Tapasss...</li>
</ul>
<form class="add-items">
<input type="text" name="item" placeholder="Item Name" autocomplete="off" required>
<input type="submit" value="+ Add Item">
</form>
</div>
<script>
// 在关闭浏览器之后清除缓存
window.onbeforeunload = function (e) {
localStorage.removeItem('items');
e.returnValue=false;
};
// window.onbeforeunload = function (event) {
// var message = 'Important: Please click on \'Save\' button to leave this page.';
// if (typeof event == 'undefined') {
// event = window.event;
// }
// if (event) {
// event.returnValue = message;
// }
// return message;
// };
const addItems = document.querySelector('.add-items'); //获取的是form表单
const itemsList = document.querySelector('.plates'); //获取ul无序列表
const items = JSON.parse(localStorage.getItem('items')) || []; //从localStorage中获取键名为items的值,并转换为json格式,若果取到则进行赋值,若果没有则赋值[];
//增加列表项,然后添加到ul列表中,并更新到localStorage
function addItem(e) {
e.preventDefault(); //阻止submit默认的页面刷新行为。
const text = this.querySelector('[name=item]').value; //获取输入框中的内容
const item = { //对象容器存储输入框内容
text, // ES6的简写形式 => text = text;
done: false
};
items.push(item);
localStorage.setItem('items', JSON.stringify(items));
populateList(items, itemsList);
this.reset(); // 添加完数据后,重置输入框
}
// 将获取到的输入框内容添加到ul列表中
function populateList(populates = [], place) { // 设置默认值,防止传参数出错的时候crash
place.innerHTML = populates.map((populate, index) => {
return `
<li>
<input type="checkbox" id=item${index} data-index=${index} ${populate.done ? 'checked' : ''}>
<label for="item${index}">${(populate.text)}</label>
</li>
`; //之所以用‘’空字符是因为如果用null的话,会出现在html中
}).join(''); // join()之后一定要加'',表示空字符,否则会加入逗号,造成错误
}
// 更改列表项选中状态,更新到localStorage和界面显示
function toggleDone(e) {
// if(!e.target.nodeName.match('INPUT')) return;
if (e.target.matches('input')) return; // 跳过所有的input,只处理label
const node = e.target;
const index = node.dataset.index; //访问自定义属性
items[index].done = !items[index].done;
localStorage.setItem('items', JSON.stringify(items));
populateList(items, itemsList);
}
// 主函数
addItems.addEventListener('submit', addItem); //form表单监听是否有submit提交发生
itemsList.addEventListener('click', toggleDone); //点击列表项触发toggleDone函数
populateList(items, itemsList); //每次加载页面时,先将localStorage中保存的items内容加载到列表中显示出来。
</script>
</body>
</html>
================================================
FILE: 15 - LocalStorage/style.css
================================================
html {
box-sizing: border-box;
background:url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat;
background-size:cover;
min-height:100vh;
display:flex;
justify-content: center;
align-items: center;
text-align: center;
font-family: Futura,"Trebuchet MS",Arial,sans-serif
}
*, *:before, *:after {box-sizing: inherit; }
svg {
fill:white;
background: rgba(0,0,0,0.1);
padding: 20px;
border-radius: 50%;
width:200px;
margin-bottom: 50px;
}
.wrapper {
padding: 20px;
max-width: 350px;
background: rgba(255,255,255,0.95);
box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
}
h2 {
text-align: center;
margin: 0;
font-weight: 200;
}
.plates {
margin: 0;
padding: 0;
text-align: left;
list-style: none;
}
.plates li {
border-bottom: 1px solid rgba(0,0,0,0.2);
padding: 10px 0;
font-weight: 100;
display: flex;
}
.plates label {
flex:1;
cursor: pointer;
}
.plates input {
display: none;
}
.plates input + label:before {
content: '⬜️';
margin-right: 10px;
}
.plates input:checked + label:before {
content: '☑';
}
.add-items {
margin-top: 20px;
}
.add-items input {
padding:10px;
outline:0;
border:1px solid rgba(0,0,0,0.1);
}
================================================
FILE: 16 - 移动鼠标让字体呈现彩虹效果/README.md
================================================
# Day16 - 鼠标移动让文字出现🌈效果中文指南
## 效果图

鼠标移动时,元素的字体阴影随着鼠标移动的方向发生改变,达到字体阴影随着鼠标一起走的效果。
## 基础知识
#### text-shadow
`text-shadow: h-shadow v-shadow blur color; `
`none`:无阴影
`<length>`①:第1个长度值用来设置对象的阴影水平偏移值。可以为负值
`<length>`②:第2个长度值用来设置对象的阴影垂直偏移值。可以为负值
`<length>`③:如果提供了第3个长度值则用来设置对象的阴影模糊值。不允许负值
`<color>`:设置对象的阴影的颜色。
#### 解构赋值
> [参考文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
解构赋值(destructuring assignment)语法是一个Javascript表达式,它使得从数组或者对象中提取数据赋值给不同的变量成为可能。
```js
let a, b, rest;
/* array 解构赋值 */
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
[a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 1
console.log(b); // 2
console.log(rest); // [3, 4, 5]
/* object 解构赋值:这里的小括号不能掉 */
({a, b} = {a:1, b:2});
console.log(a); // 1
console.log(b); // 2
/* object & ...rest 解构赋值 */
({a, b, ...rest} = {a:1, b:2, c:3, d:4});
// {a: 1, b: 2, c: 3, d: 4}
rest;
// {c: 3, d: 4}
```
#### MouseEvent

[https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
[clientX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX) 设置或获取鼠标指针位置相对于当前窗口的 x 坐标,其中客户区域不包括窗口自身的控件和滚动条。
[clientY](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientY) 设置或获取鼠标指针位置相对于当前窗口的 y 坐标,其中客户区域不包括窗口自身的控件和滚动条。
[offsetX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX) 设置或获取鼠标指针位置相对于触发事件的对象的 x 坐标。
[offsetY](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetY) 设置或获取鼠标指针位置相对于触发事件的对象的 y 坐标。
[screenX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX) 设置或获取获取鼠标指针位置相对于用户屏幕的 x 坐标。
[screenY](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY) 设置或获取鼠标指针位置相对于用户屏幕的 y 坐标。
[x](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/x) 设置或获取鼠标指针位置相对于父文档的 x 像素坐标(亦即相对于当前窗口)。
[y](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/y) 设置或获取鼠标指针位置相对于父文档的 y 像素坐标(亦即相对于当前窗口)。
[pageX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX) 设置或获取指针位置相对于整个文档的x坐标
[pageY](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageY) 设置或获取指针位置相对于整个文档的y坐标
#### 页面元素offset的几个属性示例
* HTMLElement.offsetParent:是一个只读属性,指向最近的包含该元素的定位元素.如果没有定位的元素,则 offsetParent 为最近的 table 元素对象或根元素(标准模式下为 html;quirks 模式下为 body)。当元素的 style.display 设置为 "none" 时,offsetParent 返回 null。
offsetParent 很有用,因为 _offsetTop_ 和 _offsetLeft_ 都是相对于其内边距边界的。
* HTMLElement.offsetTop:指的是当前元素到其offsetParent指向元素的__上边距__的距离。
* HTMLElement.offsetLeft:指的是当前元素到其offsetParent指向元素的__左边距__的距离。
* HTMLElement.offsetHeight:指的是当前元素的__高度__,包含__content,padding,border__的高度值,但不包括__margin__的值。
* HTMLElement.offsetWidth:指的是当前元素的__宽度__,包含__content,padding,border__的高度值,但不包括__margin__的值。
## js代码
```javascript
const hero = document.querySelector('.hero');
const text = hero.querySelector('h1');
const walk = 40; // 鼠标左右移动共移动的距离
function draw(e){
const { offsetWidth: width, offsetHeight: height} = hero;
let { offsetX: x, offsetY: y} = e;
// 使鼠标移动到中间元素上,x、y的值连续变化
if(e.target !== this){
// if(e.target == text){
x = x + e.target.offsetLeft;
y = y + e.target.offsetTop;
}
// const xaisx = (x/width*walk)-(walk/2);
// const yaisx = (y/height*walk)-(walk/2);
const xaisx = Math.floor((x/width*walk)-(walk/2));
const yaisx = Math.floor((y/height*walk)-(walk/2));
text.style.textShadow = `
${xaisx}px ${yaisx * -1}px 2px rgba(0,255,0,0.7),
${xaisx * -1}px ${yaisx}px 2px rgba(255,0,0,0.7),
${yaisx}px ${xaisx * -1}px 2px rgba(188,188,188,0.7),
${yaisx * -1}px ${xaisx}px 2px rgba(0,0,255,0.7)
`; // 多写几个就有了霓虹灯的效果
}
hero.addEventListener('mousemove',draw);
```
* 分别获取到鼠标所在位置相对于页面左侧和顶端的距离,将这两个距离映射为自己想要移动的距离上(`walk`);
* 其中当鼠标移动中间的文字上的时候,由于`e.target`变化了,所以造成x的值不连续,因此需要监测`e.target`的值,判断是否指在了文字上;
* 为元素设置字体阴影,text-shadow样式,也可以设置多个,达到类似霓虹灯的效果;
* 对元素添加`mousemove`事件。
================================================
FILE: 16 - 移动鼠标让字体呈现彩虹效果/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mouse Shadow</title>
</head>
<body>
<div class="hero">
<h1 contenteditable>🔥liyc1215</h1>
</div>
<style>
html {
color: black;
font-family: sans-serif;
}
.hero {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: orange;
}
h1 {
text-shadow: 10px 10px 0 rgba(0, 0, 0, 1);
font-size: 100px;
}
</style>
<script>
const hero = document.querySelector('.hero');
const text = hero.querySelector('h1');
const walk = 40; // 鼠标左右移动共移动的距离
function draw(e) {
const {
offsetWidth: width,
offsetHeight: height
} = hero;
let {
offsetX: x,
offsetY: y
} = e;
// 使鼠标移动到中间元素上,x、y的值连续变化
if (e.target !== this) {
// if(e.target == text){
x = x + e.target.offsetLeft;
y = y + e.target.offsetTop;
}
// const xaisx = (x/width*walk)-(walk/2);
// const yaisx = (y/height*walk)-(walk/2);
const xaisx = Math.floor((x / width * walk) - (walk / 2));
const yaisx = Math.floor((y / height * walk) - (walk / 2));
text.style.textShadow =
`
${xaisx}px ${yaisx * -1}px 2px rgba(0,255,0,0.7),
${xaisx * -1}px ${yaisx}px 2px rgba(255,0,0,0.7),
${yaisx}px ${xaisx * -1}px 2px rgba(188,188,188,0.7),
${yaisx * -1}px ${xaisx}px 2px rgba(0,0,255,0.7)
`; // 多写几个就有了霓虹灯的效果
}
hero.addEventListener('mousemove', draw);
</script>
</body>
</html>
================================================
FILE: 17 - 数组排序/README.md
================================================
## Day17 - Sort Without Articles(无冠词排序)
### 效果图

##### 实现效果:对列表中列表项内容进行无冠词排序,在排序时不考虑a,an,the(大小写)的影响,点击ascending按钮进行升序,按下descending进行降序。
### 源码分析
```CSS
body{
background: url("https://source.unsplash.com/nDqA4d5NL0k/2000x2000");
background-size:cover;
padding:50px;
display:flex;
flex-direction: column; /*这里是将ul,和div进行垂直居中排列,二者相对位置是上下,不加的话相对位置水平*/
align-items:center;
min-height:100vh;
}
```
```HTML
<ul id="bands"></ul>
<div class="button">
<button name="ascending">升序</button>
<button name="descengding">降序</button>
</div>
```
> 这里给两个按钮进行命名,用以区分两个按钮,在进行CSS布局时使用`flex-direction:column`将ul和div处于竖直布局。
```JS
const button=[...document.querySelectorAll("button")];//使用扩展运算符,这里是打散,将伪数组转化为真正的数组
button.forEach(item=>item.addEventListener("click",paixu));
```
> 这里取得所有的按钮元素,并给每个按钮添加一个鼠标点击事件,注意项:伪元素转换为真正元素,可以使用扩展运算符,也可以使用`Array.form()`.
排序函数:
```JS
//用sort比较函数以及del函数将每一项去除完冠词后进行比较,然后根据比较条件的正负,对原来项进行排序,sort会改变原数组
function paixu(e){
let flag=this.getAttribute("name");//使用name值对相同元素进行区别
if(flag=="ascending"){
bands.sort((a,b)=>del(a)>del(b)?1:-1);
}else{
bands.sort((a,b)=>del(a)>del(b)?-1:1);
}
show(bands,bandsele);
}
```
> 这里的思路:刚开始使用的是两个函数,点击不同的按钮跳转到各自的事件处理函数上去。但是写完发现这两个函数及其相似,过于冗余,使用了name属性,然后当点击不同的按钮时,获取各自的name值,然后根据name值来判断点击的是哪一个按钮,来处理升序还是降序。
这里涉及的知识点:
- 1.使用`get.Attribute()`来获取属性值,但是具体获取那个元素的,这里使用了事件项event,这里的this就是e.target:表示当前元素。
- 2.sort数组的排序函数,根据判断程序来判断条件的正负,正的升序,负的降序,会改变原来的数组,所以,在最后有个显示的语句show(bands,bandsele);
删去冠词的函数:
```JS
// 取出冠词a,an,the的函数,返回一个新的字符串
function del(item){
return item.replace(/^(a|an|the)\s{1}/ig,'');
}
```
> \s{1}表示有一个空格,replace返回的事一个新字符串。
列表项显示函数:
```JS
//将数组中的元素进行显示在指定列表中的函数,遍历整个数组元素,然后将返回的元素进行拼接整体显示在innerHTML
function show(arr,place){
place.innerHTML=arr.map(item=>{return `<li>${item}</li>`}).join('');
}
```
> 这个函数相当实用,map函数返回一个新的数组。
================================================
FILE: 17 - 数组排序/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>无冠词排序</title>
<style>
body{
background: url("https://source.unsplash.com/nDqA4d5NL0k/2000x2000");
background-size:cover;
padding:50px;
display:flex;
flex-direction: column; /*这里是将ul,和div进行垂直居中排列,二者相对位置是上下,不加的话相对位置水平*/
/* justify-content: center; */
align-items:center;
min-height:100vh;
}
#bands{
list-style: inside square;
font-size:20px;
background: white;
width:500px;
margin:auto;
padding:0px;
box-shadow: 0 0 0 20px rgba(0,0,0,0.25);
}
#bands li{
border-bottom:1px solid #efefef;
padding:20px;
}
#bands li:last-chid{
border-bottom:none;
}
.button{
margin-top:25px;
margin-bottom: 50px;
}
button{
font-size: 25px;
width:100px;
padding:3px;
border-radius: 5px;
margin-left: 10px;
box-shadow: 0 0 5px 0 rgba(0,0,0,0.25);
}
</style>
</head>
<body>
<ul id="bands"></ul>
<div class="button">
<button name="ascending">升序</button>
<button name="descengding">降序</button>
</div>
<script>
const bands = ['The Plot in You', 'The Devil Wears Prada', 'Pierce the Veil', 'Norma Jean', 'The Bled',
'Say Anything', 'The Midway State', 'We Came as Romans', 'Counterparts', 'Oh, Sleeper', 'A Skylit Drive',
'Anywhere But Here', 'An Old Dog'
];
const bandsele=document.querySelector("#bands");
show(bands,bandsele);
const button=[...document.querySelectorAll("button")];//使用扩展运算符,这里是打散,将伪数组转化为真正的数组
button.forEach(item=>item.addEventListener("click",paixu));
// 取出冠词a,an,the的函数,返回一个新的字符串
function del(item){
return item.replace(/^(a|an|the)\s{1}/ig,'');
}
//用sort比较函数以及del函数将每一项去除完冠词后进行比较,然后根据比较条件的正负,对原来项进行排序,sort会改变原数组
function paixu(e){
let flag=this.getAttribute("name");//使用name值对相同元素进行区别
if(flag=="ascending"){
bands.sort((a,b)=>del(a)>del(b)?1:-1);
}else{
bands.sort((a,b)=>del(a)>del(b)?-1:1);
}
show(bands,bandsele);
}
//将数组中的元素进行显示在指定列表中的函数,遍历整个数组元素,然后将返回的元素进行拼接整体显示在innerHTML
function show(arr,place){
place.innerHTML=arr.map(item=>{return `<li>${item}</li>`}).join('');
}
</script>
</body>
</html>
================================================
FILE: 18 - Day18 - Reduce、Map混合使用计算时分秒/README.md
================================================
## Day18 - Reduce、Map混合使用计算时分秒
### 效果图

第18天挑战的内容主要是如何将一系列的`data-time`加起来,最终计算总时间,总时间用时分秒显示。
挑战升级:点击显示时长按钮将每个video的时间显示出来,将选中的videos进行总时长的计算。
### 代码解析
css页面布局和示例17:无冠词排序基本一致
主要分析JavaScript代码:
```JS
<script>
let list=[...document.querySelectorAll("li")];
let input = document.querySelector("input");
let button = document.querySelector("button");
button.addEventListener("click",totalTime);
//将每项的时间长度也显示出来
list.forEach(item=>{
let datatime=item.dataset.time;
item.innerHTML=item.innerHTML+"------------时长:"+datatime;
});
//总时间计算函数
function totalTime(){
let sum = list.reduce((cal,item)=>{
let datatime=item.getAttribute("data-time");
let [mins,seconds] = datatime.split(":").map(item=>parseFloat(item));//解构赋值
return cal+(mins*60+seconds); //reduce求和注意要让cal带入计算
},0);
let hours=sum/3600;
input.value=Math.floor(sum/3600)+"个小时"+Math.floor((sum%3600)/60)+"分"+sum%60+"秒";
}
</script>
```
> 主要内容就是两部分:
1、获得各项的data-time值,然后进行累加
2、根据总秒数进行时分秒的计算
`let [mins,seconds] = datatime.split(":").map(item=>parseFloat(item));`
解构赋值:这里是将时和秒数进行切割形成新的数组,然后对数组中的每一项用parseFloat()进行字符串转换为数值,然后进行解构赋值,例如:[mins,seconds]=[03,58];这里将03赋值给mins,将58赋值给seconds.
`input.value=Math.floor(sum/3600)+"个小时"+Math.floor((sum%3600)/60)+"分"+sum%60+"秒";`
总秒数除以3600得到的商就是小时个数,然后进行向下取整;
总秒数对3600取余,实际得到的是出去小时后还剩下多少秒,然后对剩下的秒数除以60(一分钟60秒)得到有多少分钟;
总秒数对60取余:即是说总秒数中把能凑成60的,也就是说能组成分钟的除去后还剩多少秒,就是要求的秒数。
================================================
FILE: 18 - Day18 - Reduce、Map混合使用计算时分秒/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Adding Up Times with Reduce</title>
<style>
body{
background: url("https://source.unsplash.com/nDqA4d5NL0k/2000x2000");
background-size:cover;
padding:50px;
display:flex;
flex-direction: column; /*这里是将ul,和div进行垂直居中排列,二者相对位置是上下,不加的话相对位置水平*/
/* justify-content: center; */
align-items:center;
min-height:100vh;
}
.videos{
list-style: inside square;
font-size:20px;
background: white;
width:500px;
margin:auto;
padding:0px;
box-shadow: 0 0 0 20px rgba(0,0,0,0.25);
}
.videos li{
border-bottom:1px solid #efefef;
padding:20px;
/* white-space: pre; */
}
.videos li:last-chid{
border-bottom:none;
}
.result{
margin-top:30px;
margin-bottom: 50px;
/* display: flex;
justify-content: center;
align-items: center; */
}
button{
font-size: 25px;
width:100px;
padding:3px;
border-radius: 5px;
margin-right: 10px;
box-shadow: 0 0 5px 0 rgba(0,0,0,0.25);
}
input{
height:30px;
font-size: 25px;
text-align: center;
padding:3px;
}
</style>
</head>
<body>
<ul class="videos">
<li data-time="5:43">
Video 1
</li>
<li data-time="2:33">
Video 2
</li>
<li data-time="3:45">
Video 3
</li>
<li data-time="0:47">
Video 4
</li>
<li data-time="5:21">
Video 5
</li>
<li data-time="6:56">
Video 6
</li>
<li data-time="3:46">
Video 7
</li>
<li data-time="5:25">
Video 8
</li>
<li data-time="3:14">
Video 9
</li>
<li data-time="3:31">
Video 10
</li>
<li data-time="5:59">
Video 11
</li>
<li data-time="3:07">
Video 12
</li>
<li data-time="11:29">
Video 13
</li>
<li data-time="8:57">
Video 14
</li>
<li data-time="5:49">
Video 15
</li>
<li data-time="5:52">
Video 16
</li>
<li data-time="5:50">
Video 17
</li>
<li data-time="9:13">
Video 18
</li>
<li data-time="11:51">
Video 19
</li>
<li data-time="7:58">
Video 20
</li>
<li data-time="4:40">
Video 21
</li>
<li data-time="4:45">
Video 22
</li>
<li data-time="6:46">
Video 23
</li>
<li data-time="7:24">
Video 24
</li>
<li data-time="7:12">
Video 25
</li>
<li data-time="5:23">
Video 26
</li>
<li data-time="3:34">
Video 27
</li>
<li data-time="8:22">
Video 28
</li>
<li data-time="5:17">
Video 29
</li>
<li data-time="3:10">
Video 30
</li>
<li data-time="4:43">
Video 31
</li>
<li data-time="19:43">
Video 32
</li>
<li data-time="0:47">
Video 33
</li>
<li data-time="0:47">
Video 34
</li>
<li data-time="3:14">
Video 35
</li>
<li data-time="3:59">
Video 36
</li>
<li data-time="2:43">
Video 37
</li>
<li data-time="4:17">
Video 38
</li>
<li data-time="6:56">
Video 39
</li>
<li data-time="3:05">
Video 40
</li>
<li data-time="2:06">
Video 41
</li>
<li data-time="1:59">
Video 42
</li>
<li data-time="1:49">
Video 43
</li>
<li data-time="3:36">
Video 44
</li>
<li data-time="7:10">
Video 45
</li>
<li data-time="3:44">
Video 46
gitextract_hmq1lhif/ ├── .gitattributes ├── 01 - JavaScript Drum Kit/ │ ├── README.md │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css ├── 02 - JS and CSS Clock/ │ ├── README.md │ ├── click.css │ ├── clock.js │ ├── clock1.js │ ├── clock2.js │ ├── index.html │ └── style.css ├── 03 - CSS Variables/ │ ├── README.md │ ├── index.html │ ├── style.css │ └── variables.js ├── 04 - Array Cardio Day 1/ │ ├── README.md │ ├── index-FINISHED.html │ └── index-START.html ├── 05 - Flex Panel Gallery/ │ ├── README.md │ ├── animation.html │ ├── index-FINISHED.html │ └── index-START.html ├── 06 - Fetch、filter、正则表达式实现快速古诗匹配/ │ ├── .vscode/ │ │ └── launch.json │ ├── README.md │ └── index.html ├── 07 - Array Cardio Day 2/ │ ├── README.md │ └── index.html ├── 08 - HTML5 Canvas 实现彩虹画笔绘画板/ │ ├── README.md │ └── index.html ├── 09 - Console 调试各种姿势指南/ │ ├── README.md │ └── index-FINISHED.html ├── 10 - JS 实现 Checkbox 中按住 Shift 的多选功能/ │ ├── README.md │ ├── index-FINISHED.html │ ├── index-START.html │ └── sunzhaoxiang.html ├── 11 - 自定义视频播放器/ │ ├── README.md │ ├── index.html │ └── style.css ├── 12 - 键盘输入序列的验证指南/ │ ├── README.md │ └── index.html ├── 13 - 图片随屏幕滚动而滑入滑出的效果/ │ ├── README.md │ ├── index.html │ ├── index.js │ └── style.css ├── 14 - JavaScript 引用和值拷贝/ │ ├── README.md │ ├── index-FINISHED.html │ └── index-START.html ├── 15 - LocalStorage/ │ ├── README.md │ ├── demo.html │ ├── index.html │ └── style.css ├── 16 - 移动鼠标让字体呈现彩虹效果/ │ ├── README.md │ └── index.html ├── 17 - 数组排序/ │ ├── README.md │ └── index.html ├── 18 - Day18 - Reduce、Map混合使用计算时分秒/ │ ├── README.md │ └── index.html ├── 19 - Webcam Fun/ │ ├── README.md │ ├── index.html │ ├── package.json │ ├── scripts.js │ └── style.css ├── 20 - Speech Detection/ │ ├── README.md │ ├── index-FINISHED.html │ ├── index-START.html │ └── package.json ├── 21 - Geolocation/ │ ├── README.md │ ├── index.html │ └── package.json ├── 22 - Follow Along Link Highlighter/ │ ├── README.md │ ├── index.html │ └── style.css ├── 23 - Speech Synthesis/ │ ├── 23 - Speech Synthesis/ │ │ ├── README.md │ │ ├── index.html │ │ └── style.css │ └── speak-easy-synthesis/ │ ├── index.html │ ├── manifest.webapp │ ├── script.js │ └── style.css ├── 24 - Sticky Nav/ │ ├── README.md │ ├── index.html │ └── style.css ├── 25 - Event Capture, Propagation, Bubbling and Once/ │ ├── README.md │ └── index.html ├── 26 - Stripe Follow Along Nav/ │ ├── README.md │ └── index.html ├── 27 - Click and Drag/ │ ├── README.md │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css ├── 28 - Video Speed Controller/ │ ├── .vscode/ │ │ └── settings.json │ ├── README.md │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css ├── 29 - Countdown Timer/ │ ├── .vscode/ │ │ └── settings.json │ ├── README.md │ ├── index.html │ ├── scripts-FINISHED.js │ ├── scripts-START.js │ └── style.css ├── 30 - Whack A Mole/ │ ├── README.md │ ├── index-FINISHED.html │ ├── index-START.html │ └── style.css ├── 31 - Canvas CountClock/ │ ├── Countdown.html │ ├── Countdown.js │ ├── digit.js │ └── readme.md ├── LICENSE └── README.md
SYMBOL INDEX (29 symbols across 9 files)
FILE: 02 - JS and CSS Clock/clock.js
function left (line 10) | function left(){
function right (line 34) | function right(){
FILE: 02 - JS and CSS Clock/clock1.js
function initDate (line 15) | function initDate() {
function updateDate (line 30) | function updateDate() {
FILE: 02 - JS and CSS Clock/clock2.js
function left (line 15) | function left(){
function right (line 49) | function right(){
FILE: 03 - CSS Variables/variables.js
function spacingchange (line 7) | function spacingchange(){
function blurchange (line 13) | function blurchange(){
function basechange (line 20) | function basechange(){
FILE: 13 - 图片随屏幕滚动而滑入滑出的效果/index.js
function throttle (line 5) | function throttle(func,wait){
function handle (line 21) | function handle(){
FILE: 19 - Webcam Fun/scripts.js
function getVideo (line 7) | function getVideo(){
function printToCanvas (line 19) | function printToCanvas(){
function takePhoto (line 43) | function takePhoto(){
function redEffect (line 59) | function redEffect(imagedata){
function rgbsplit (line 69) | function rgbsplit(imagedata){
function greenScreen (line 79) | function greenScreen(imagedata) {
FILE: 23 - Speech Synthesis/speak-easy-synthesis/script.js
function populateVoiceList (line 14) | function populateVoiceList() {
function speak (line 38) | function speak(){
FILE: 29 - Countdown Timer/scripts-FINISHED.js
function timer (line 6) | function timer(seconds) {
function displayTimeLeft (line 27) | function displayTimeLeft(seconds) {
function displayEndTime (line 35) | function displayEndTime(timestamp) {
function startTimer (line 43) | function startTimer() {
FILE: 31 - Canvas CountClock/Countdown.js
function getCurrentShowTimeSeconds (line 58) | function getCurrentShowTimeSeconds(){
function update (line 66) | function update(){
function updateBalls (line 106) | function updateBalls(){
function addBalls (line 138) | function addBalls(x,y,num){
function render (line 157) | function render(cxt){
function renderdigit (line 184) | function renderdigit(x,y,num,cxt){
Condensed preview — 112 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (438K chars).
[
{
"path": ".gitattributes",
"chars": 109,
"preview": "*.css linguist-language=javascript\n*.html linguist-language=javascript \n*.java linguist-language=javascript\n"
},
{
"path": "01 - JavaScript Drum Kit/README.md",
"chars": 7950,
"preview": "# Day1 JavaScript Drum Kit 中文指南\n## 简介\n第一天的练习是用JS制作一个爵士鼓的页面,通过敲击键盘上不同的字母,会发出不同的声音,并且页面上会伴随着敲击的动画。\n效果如下:\n;\n}\nbody,\nht"
},
{
"path": "02 - JS and CSS Clock/README.md",
"chars": 6133,
"preview": "# Day02 - JavaScript + CSS Clock\n\n## 简介\n第二天的练习是用JS+CSS模拟时钟效果。\n\n效果如下:\n是 ES6 定义的迭代方法,这些迭代方法都有一个特点,就是对数组的每一项都运行给定"
},
{
"path": "04 - Array Cardio Day 1/index-FINISHED.html",
"chars": 4868,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Array Cardio 💪</title>\n</head>\n\n<body>\n <p><"
},
{
"path": "04 - Array Cardio Day 1/index-START.html",
"chars": 2800,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Array Cardio 💪</title>\n</head>\n<body>\n <p><em"
},
{
"path": "05 - Flex Panel Gallery/README.md",
"chars": 4656,
"preview": "# Day05 - Flex 实现可伸缩的图片墙 中文指南\n\n## 实现效果\n\n点击任意一张图片,图片展开,同时从图片上下两方分别移入文字。点击已经展开的图片后,图片被压缩,同时该图片上下两端的文字被挤走。\n\n\n## 各种调"
},
{
"path": "09 - Console 调试各种姿势指南/index-FINISHED.html",
"chars": 2667,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Console Tricks!</title>\n</head>\n\n<body>\n\n <p"
},
{
"path": "10 - JS 实现 Checkbox 中按住 Shift 的多选功能/README.md",
"chars": 2722,
"preview": "# Day10 - JS 实现 Checkbox 中按住 Shift 的多选功能\n\n## 项目效果\n\n);//转换为真正的数组\n // imgs.forEach(item=>item.addEventListener(\"sc"
},
{
"path": "13 - 图片随屏幕滚动而滑入滑出的效果/style.css",
"chars": 974,
"preview": "html{\n box-sizing:border-box;\n background: linear-gradient(silver,#ffc600);\n font-family: 'helvetica neue';\n "
},
{
"path": "14 - JavaScript 引用和值拷贝/README.md",
"chars": 4375,
"preview": "### Day14 - JavaScript 引用和值拷贝\n\n#### 项目效果\n效果展示如下面的图片,另外也可以自行在浏览器的控制台中打印显示进行理解。\n\n#### 按值操作(深拷贝)\n\n基本类型由值操作。以下类型在JavaScript中"
},
{
"path": "14 - JavaScript 引用和值拷贝/index-FINISHED.html",
"chars": 2060,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>JS Reference VS Copy</title>\n</head>\n\n<body>\n"
},
{
"path": "14 - JavaScript 引用和值拷贝/index-START.html",
"chars": 1258,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>JS Reference VS Copy</title>\n</head>\n<body>\n\n "
},
{
"path": "15 - LocalStorage/README.md",
"chars": 5206,
"preview": "\n## 效果图\n\n\n第十五天主要是练习`LocalSto"
},
{
"path": "15 - LocalStorage/demo.html",
"chars": 1508,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, in"
},
{
"path": "15 - LocalStorage/index.html",
"chars": 4873,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>LocalStorage</title>\n <link rel=\"stylesheet\""
},
{
"path": "15 - LocalStorage/style.css",
"chars": 1324,
"preview": "html {\n box-sizing: border-box;\n background:url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat;\n backgroun"
},
{
"path": "16 - 移动鼠标让字体呈现彩虹效果/README.md",
"chars": 4184,
"preview": "# Day16 - 鼠标移动让文字出现🌈效果中文指南\n\n## 效果图\n\n### 效果图\n\n\n;\nconst canvas = document.querySelector('.photo');\nconst ctx = canvas.get"
},
{
"path": "19 - Webcam Fun/style.css",
"chars": 924,
"preview": "html {\n box-sizing: border-box;\n}\n\n*, *:before, *:after {\n box-sizing: inherit;\n}\n\nhtml {\n font-size: 10px;\n backgro"
},
{
"path": "20 - Speech Detection/README.md",
"chars": 4096,
"preview": "# Day20 - 语言识别系统中文指南\n\n> 本文出自:[春哥个人博客:http://www.liyuechun.org](http://liyuechun.org)\n> 作者:©[黎跃春-追时间的人](http://weibo.com/"
},
{
"path": "20 - Speech Detection/index-FINISHED.html",
"chars": 1798,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Speech Detection</title>\n</head>\n<body>\n\n <di"
},
{
"path": "20 - Speech Detection/index-START.html",
"chars": 1105,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>Speech Detection</title>\n</head>\n<body>\n\n <di"
},
{
"path": "20 - Speech Detection/package.json",
"chars": 286,
"preview": "{\n \"name\": \"gum\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"scripts.js\",\n \"scripts\": {\n \"start\": \"brows"
},
{
"path": "21 - Geolocation/README.md",
"chars": 8273,
"preview": "# Day21 - Geolocation 中文指南\n\n### 涉及知识点主要是geolocation,这里不做详细介绍,[具体内容见我总结的博客,请戳我!](https://blog.csdn.net/qq_39207948/articl"
},
{
"path": "21 - Geolocation/index.html",
"chars": 7111,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Document</title>\n <meta name=\"viewport\" cont"
},
{
"path": "21 - Geolocation/package.json",
"chars": 294,
"preview": "{\n \"name\": \"gum\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"scripts.js\",\n \"scripts\": {\n \"start\": \"brows"
},
{
"path": "22 - Follow Along Link Highlighter/README.md",
"chars": 5092,
"preview": "# Day22 - 鼠标锚点动画生成指南\n\n## 效果图\n\n\n第23天要做一个语音的记事本类似的场景,输入一段内"
},
{
"path": "23 - Speech Synthesis/23 - Speech Synthesis/index.html",
"chars": 2419,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Speech Synthesis</title>\n <link href='https:"
},
{
"path": "23 - Speech Synthesis/23 - Speech Synthesis/style.css",
"chars": 2438,
"preview": "html {\n font-size: 10px;\n box-sizing: border-box;\n}\n\n*, *:before, *:after {\n box-sizing: inherit;\n}\n\nbody {\n margin:"
},
{
"path": "23 - Speech Synthesis/speak-easy-synthesis/index.html",
"chars": 1211,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrom"
},
{
"path": "23 - Speech Synthesis/speak-easy-synthesis/manifest.webapp",
"chars": 341,
"preview": "{\n \"name\": \"SpeechSyn\",\n \"description\": \"Web Speech API speech synthesis demo\",\n \"launch_path\": \"/index.html\",\n \"ico"
},
{
"path": "23 - Speech Synthesis/speak-easy-synthesis/script.js",
"chars": 1884,
"preview": "var synth = window.speechSynthesis;\n\nvar inputForm = document.querySelector('form');\nvar inputTxt = document.querySelect"
},
{
"path": "23 - Speech Synthesis/speak-easy-synthesis/style.css",
"chars": 779,
"preview": "body, html {\n margin: 0;\n}\n\nhtml {\n height: 100%;\n}\n\nbody {\n height: 90%;\n max-width: 800px;\n margin: 0 auto;\n}\n\nh1"
},
{
"path": "24 - Sticky Nav/README.md",
"chars": 117,
"preview": "# Day24 - Sticky Nav 粘性导航栏\n## 效果图\n\n\n### 代码解析\n### 涉及知识点\n### \n"
},
{
"path": "24 - Sticky Nav/index.html",
"chars": 15407,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Sticky Nav</title>\n <link rel=\"stylesheet\" h"
},
{
"path": "24 - Sticky Nav/style.css",
"chars": 1523,
"preview": "html {\n box-sizing: border-box;\n background: #eeeeee;\n font-family: 'helvetica neue';\n font-size: 20px;\n font-weigh"
},
{
"path": "25 - Event Capture, Propagation, Bubbling and Once/README.md",
"chars": 1974,
"preview": "\n\n# Day25 - Event Capture, Propagation, Bubbling and Once\n\n\n## 效果图\n\n\n\n\n## 源码\n\n```javascript\n// jav"
},
{
"path": "26 - Stripe Follow Along Nav/index.html",
"chars": 6962,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Follow Along Nav</title>\n</head>\n\n<body>\n <h"
},
{
"path": "27 - Click and Drag/README.md",
"chars": 6340,
"preview": "# Day27 - Click and Drag\n\n\n## 效果图\n\n[在线效果](http://30daysofjs.michaeleinsohn.com/scroll-drag/)\n\n fixed;\n backgro"
},
{
"path": "28 - Video Speed Controller/.vscode/settings.json",
"chars": 38,
"preview": "{\n \"git.ignoreLimitWarning\": true\n}"
},
{
"path": "28 - Video Speed Controller/README.md",
"chars": 2901,
"preview": "# Day28 - Video Speed Controller\n\n\n## 效果图\n\n[在线效果](http://30daysofjs.michaeleinsohn.com/video-speed/)\n\n\n\n;\nconst endTime = document.querySelecto"
},
{
"path": "29 - Countdown Timer/scripts-START.js",
"chars": 0,
"preview": ""
},
{
"path": "29 - Countdown Timer/style.css",
"chars": 1296,
"preview": "html {\n box-sizing: border-box;\n font-size: 10px;\n background: #8E24AA;\n background: linear-gradient(45deg, #42a5f5 "
},
{
"path": "30 - Whack A Mole/README.md",
"chars": 6662,
"preview": "# Day30 - Whack A Mole\n\n\n\n## 效果图\n\n[在线效果图]()\n\n\n\n\n第30天挑战主要是通过JS原生"
},
{
"path": "30 - Whack A Mole/index-FINISHED.html",
"chars": 2145,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Whack A Mole!</title>\n <link href='https://f"
},
{
"path": "30 - Whack A Mole/index-START.html",
"chars": 1028,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <title>Whack A Mole!</title>\n <link href='https://f"
},
{
"path": "30 - Whack A Mole/style.css",
"chars": 995,
"preview": "html {\n box-sizing: border-box;\n font-size: 10px;\n background: #ffc600;\n}\n\n*,\n*:before,\n*:after {\n box-sizing: inher"
},
{
"path": "31 - Canvas CountClock/Countdown.html",
"chars": 481,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>canvas倒计时特效</title>\n <style>\n #c"
},
{
"path": "31 - Canvas CountClock/Countdown.js",
"chars": 9310,
"preview": "/*\n* @Author: Administrator\n* @Date: 2018-12-22 15:41:18\n* @Last Modified by: Administrator\n* @Last Modified time: 2"
},
{
"path": "31 - Canvas CountClock/digit.js",
"chars": 3538,
"preview": "// 粒子圆的数字采用的是类似点阵屏的显示方式,\n// 数字采用的是10*7的点阵屏(本质是二维数组),符号“:”采用的是10*4的点阵屏,\n// 整体上是三维数组,digit[0]是数字0的点阵,digit[1]是数字1的点阵,以此类推进"
},
{
"path": "31 - Canvas CountClock/readme.md",
"chars": 9224,
"preview": "### 效果展示\n\n### 代"
},
{
"path": "LICENSE",
"chars": 1066,
"preview": "MIT License\n\nCopyright (c) 2018 SUNNERCMS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "README.md",
"chars": 4626,
"preview": "[纯CSS/CSS3制作的动画小demo](https://github.com/SUNNERCMS/CSS-CSS3-Animation-effects) \n# JavaScript 30天每日效果图 \n\n在Github上看到了[we"
}
]
About this extraction
This page contains the full source code of the SUNNERCMS/30daysJavascript GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 112 files (344.0 KB), approximately 123.5k tokens, and a symbol index with 29 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.