Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 62 additions & 21 deletions 15 - LocalStorage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,74 @@
> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 15 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*)

> 创建时间:2017-07-24
最后更新:2017-08-05
最后更新:2017-08-10

## 实现效果
## 知识点
## 过程指南
我们的目的是使网页模拟菜单的效果,在页面中添加新的菜品,而且在页面刷新之后也不清空。

默认情况下,在表单空间拥有焦点时按下 Enter 键或者点击提交按钮,会提交表单,提交时浏览器会在将请求发送给服务器之前触发 submit 事件,可以先添加事件监听后看看效果:
```js
function addItem(e) {
console.log('hello');
}
现在的初始页面中,点击提交按钮(Add Item)时页面默认触发 `submit` 事件,并重新加载页面,这导致重新加载之后的页面中,已丢失刚提交的内容。页面所用到的 CSS 文件已经完成了,我们需要做的是完成 JavaScript 部分的内容,以实现目标效果。

addItems.addEventListener('submit', addItem);
```
结果就是 Console 中闪现 hello 后刷新整个页面,这是 submit 的默认行为,在当前的场景中不适用,先去除。
```js
function addItem(e) {
e.preventDefault();
}
```
这一个挑战所用时间可能比之前的稍稍长一些,但理解了 localStorage 的机制,想明白菜单的实现之后,就会一目了然了,带上耐心我们开始吧~

## 知识点
- Event
- [event.preventDefault](https://developer.mozilla.org/zh-CN/docs/Web/API/Event/preventDefault)
- [eventTarget.addEventListener](https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener)
- [localStorage](https://developer.mozilla.org/zh-CN/docs/Web/API/Storage/LocalStorage)
- `localStorage.setItem()`
- `localStorage.getItem()`
- [JSON](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON)
- `JSON.stringify()`
- `JSON.parse()`

this 获取当前 form,是从 submit 事件获取到的。
## 过程指南

this.querySelector('[name=item]'); 获取其中的输入框
1. 默认情况下,在表单空间拥有焦点时按下 `Enter` 键或者点击提交按钮,会提交表单,提交时,浏览器会在发送请求给服务器之前触发 `submit` 事件,为了验证这个行为,我们可以添加事件监听后看看效果,此处先写一个处理函数:
```js
function addItem(e) {
console.log('hello');
}

this.reset();
以清空 input 中正在输入的值
addItems.addEventListener('submit', addItem);
```
点击按钮后发现 `submit` 事件触发后的结果是, Console 中闪现 hello 后刷新整个页面,这是 `submit` 的默认行为,在当前的场景中不适用,所以我们需要先去除此事件的默认行为。
```js
function addItem(e) {
e.preventDefault();
}
```

使用 localStorage 的时候,直接传入 items 得到的是 [object Object],所以需要在把数据传进去之前就把内容转换成 String 类型的数据。
2. 下面我们开始改造 `addItem` 方法,以实现我们的功能。
首先在事件监听中,`this` 可以获取当前 form 即我们为其添加事件监听的 `addItem` 元素,所以可以借此方便的获取输入框中的值。在 `addItem()` 方法中获取输入,并构造一个对象 `item` 来存储这个信息,:
```js
/* function addItem(){} 中 */
const text = this.querySelector('[name=item]').value;
const item = {
text, // ES6中对 text: text, 的简写
done: false // 为后续的勾选做准备
}
```
3. 把对象放入此前声明好的数组 `items`,同时更新数据,包括页面中的 HTML 内容、LocalStorage。
```js
/* function addItem(){} 中 */
items.push(item);
this.reset();// 以清空 input 中正在输入的值
populateList(items, itemsList);
localStorage.setItem('items', JSON.stringify(items));
```
- **HTML 内容更新**
声明一个 `populateList` 方法来更新页面的内容。接收需要更新的数组作为参数,根据数组里的内容构造一组 `<li>` 组成的列表,并且加上一些标识信息,以助于之后需要实现的选中功能。
```js
function populateList(plates = [], plateslist) {
plateslist.innerHTML = plates.map((plate, i) => {
return `
<li>
<input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''} >
<label for="item${i}">${plate.text}</label>
</li>
`;
}).join('');
}
```
- **LocalStorage 更新**
我们利用 LocalStorage 把信息存到本地,从而可以保证刷新后内容不变。但使用 `localStorage` 的时候,直接把 `items` 传入得到的值是 [object Object],所以需要在把数据传进去之前就把内容转换成 String 类型的数据,之后读取时也使用 `JSON.parse()` 来将数据转换成 JSON 格式。
6 changes: 3 additions & 3 deletions 15 - LocalStorage/index-SOYAINE.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ <h2>LOCAL TAPAS</h2>
<input type="submit" value="+ Add Item">
<input class="check-all" type="button" value="Check All">
<input class="uncheck-all" type="button" value="Uncheck All">
<input class="delete-all" type="button" value="Delete All">

<input class="delete-all" type="reset" value="Delete All">
</form>
</div>

Expand Down Expand Up @@ -72,7 +71,8 @@ <h2>LOCAL TAPAS</h2>
itemsList.addEventListener('click', toggleDone);
populateList(items, itemsList);



// 延伸部分
const checkAllBtn = document.querySelector('.check-all');
const unCheckAllBtn = document.querySelector('.uncheck-all');
const deleteAllBtn = document.querySelector('.delete-all');
Expand Down
55 changes: 55 additions & 0 deletions day16 - mouseMoveShadow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 16 文字阴影的鼠标随动效果

> 本篇作者:©[大史快跑Dashrun](https://github.com/dashrun)——Chinasoft Frontend Web Developer

> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 16 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*)

> 创建时间:2017-08-20
最后更新:2017-08-21

## 挑战任务
初始文件`index-start.html`中提供了一个包含了`h1`元素的`div`元素,`h1`元素已经设置了`text-Shadow`的样式。本次编程挑战中需要完成的效果是根据用户当前的鼠标位置来操纵文字阴影的位置。

## 实现效果
![结果展示](https://github.com/dashrun/vanilla-javascript-30/blob/master/day16-mouseMoveShadow/effects.png)

## 基本知识
`text-shadow`样式为标准CSS3样式,用于添加**一个或多个**文字阴影,用于其的语法格式为:
```css
text-shadow: h-shadow v-shadow blur color

```

## 过程指南
1.在`script`标签中,我们使用3个变量,一个指向`div`元素,一个指向其子元素`h1`,最后一个变量`factor`用于标记阴影距离`h1`中心的距离和鼠标距离`h1`中心距离的比例,用于计算阴影的具体位置。

2.在`hero`元素上监听鼠标移动事件`mousemove`,并添加事件处理的回调函数`shadowMove`.
```js
hero.addEventListener('mousemove',shadowMove);
```

3.为获得第一个阴影的瞬时位置,需要通过鼠标位置距离`h1`中心的距离乘以`factor`系数来获得,`pos`表示鼠标当前位置的坐标,range指代`hero`元素的宽和高:
```js
var disX = parseInt((pos.x-range.x/2)*factor);
var disY = parseInt((pos.y-range.y/2)*factor);
```
4.从事件发生的event对象中获取需要的值:
```js
var range = {
x:hero.offsetWidth,
y:hero.offsetHeight
}
var pos = {
x:e.target.offsetLeft+e.offsetX,
y:e.target.offsetTop+e.offsetY
}
```
5.计算出`h1`元素第一个阴影位置后,可以以坐标镜像或旋转90°等不同的方式来生成其他阴影,本例中我们采用绕`h1`元素中心旋转90°的方式共生成4个阴影:
```js
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
`;
```
Binary file added day16 - mouseMoveShadow/effects.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions day16 - mouseMoveShadow/index-finished-es5.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mouse Shadow-ES5</title>
</head>
<body>

<div class="hero">
<h1 contenteditable>🔥WOAH!</h1>
</div>
<!--CSS part-->
<style>
html {
color:black;
font-family: sans-serif;
}

body {
margin: 0;
}

.hero {
min-height: 100vh;
display:flex;
justify-content: center;
align-items: center;
color:black;
}

h1 {
text-shadow: 10px 10px 0 rgba(0,0,0,1);
font-size: 100px;
}
</style>
<!--Js part-->
<script>
var hero = document.querySelector('.hero');
var text = hero.querySelector('h1');
var factor = 0.4;//当鼠标移动至显示区域边界时,阴影距离占hero元素宽和高的比例
//在div范围内监听
hero.addEventListener('mousemove',shadowMove);

//文字阴影效果及移动函数
function shadowMove(e){
var range = {
x:hero.offsetWidth,
y:hero.offsetHeight
}
var pos = {
x:e.target.offsetLeft+e.offsetX,
y:e.target.offsetTop+e.offsetY
}
//计算阴影移动距离
var disX = parseInt((pos.x-range.x/2)*factor);
var disY = parseInt((pos.y-range.y/2)*factor);
//修改阴影样式
text.style.textShadow = disX+'px '+disY+'px 0 #3498db,'+(-disX)+'px '+disY+'px 0 #1abc9c,'+disY+'px '+(-disX)+'px 0 #9b59b6,'+(-disY)+'px '+(-disX)+'px 0 #e74c3c';
}
</script>
</body>
</html>
72 changes: 72 additions & 0 deletions day16 - mouseMoveShadow/index-finished-es6.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mouse Shadow</title>
</head>
<body>

<div class="hero">
<h1 contenteditable>🔥WOAH!</h1>
</div>

<style>
html {
color:black;
font-family: sans-serif;
}

body {
margin: 0;
}

.hero {
min-height: 100vh;
display:flex;
justify-content: center;
align-items: center;
color:black;
}


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 factor = 0.4; //当鼠标移动至显示区域边界时,阴影距离占hero元素宽和高的比例

function shadowMove(e) {
//解构赋值
const { offsetWidth: width, offsetHeight: height } = hero;
let { offsetX: x, offsetY: y } = e;

//将鼠标位置转换为相对视口左上角的坐标,本例中由于hero元素占满视口故未起实际作用
if (this !== e.target) {
x = x + e.target.offsetLeft;
y = y + e.target.offsetTop;
}

const xWalk = parseInt((x-width/2)*factor);
const yWalk = parseInt((y-height/2)*factor);

//使用模板字符串赋值
text.style.textShadow = `
${xWalk}px ${yWalk}px 0 rgba(255,0,255,0.7),
${xWalk * -1}px ${yWalk}px 0 rgba(0,255,255,0.7),
${yWalk}px ${xWalk * -1}px 0 rgba(0,255,0,0.7),
${yWalk * -1}px ${xWalk}px 0 rgba(0,0,255,0.7)
`;

}

//在hero范围内监听鼠标移动事件
hero.addEventListener('mousemove', shadowMove);
</script>
</body>
</html>
40 changes: 40 additions & 0 deletions day16 - mouseMoveShadow/index-start.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mouse Shadow</title>
</head>
<body>

<div class="hero">
<h1 contenteditable>🔥WOAH!</h1>
</div>

<style>
html {
color:black;
font-family: sans-serif;
}

body {
margin: 0;
}

.hero {
min-height: 100vh;
display:flex;
justify-content: center;
align-items: center;
color:black;
}

h1 {
text-shadow: 10px 10px 0 rgba(0,0,0,1);
font-size: 100px;
}
</style>

<script>
</script>
</body>
</html>
6 changes: 6 additions & 0 deletions day16 - mouseMoveShadow/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@charset "UTF-8";
/**
* @dashrun (@389399428@qq.com)
* @date 2017-08-20
* @version $1.0
*/