[TOC]
# 題1、使用原生 JS 的 DOM 實現:1. 彈出一個框,可以輸入一個數字 n 。 2. 然后在頁面中制作 n\*n 個的格子 3.每個格子鼠標放上時背景色變為紅色,鼠標挪開時顏色恢復 4. 當點擊一個格子時,顏色變成綠色并鎖定這個顏色(鼠標放上、挪開不改變顏色) 5.點擊一個已經被鎖定的元素時,取消鎖定顏色恢復,繼承可以鼠標放上、挪開時改變顏色。(格子大小可以使用 CSS 控制,背景色的改變通過 JS 控制)JavaScript 小案例

## 實現01-先根據 N 生成 table

## 實現02-鼠標放上、挪開時改變背景色
說明:因為為每個 td 綁定事件,比較浪費資源(td太多),所以我們可以使用 `事件委托` 技術只在 table 上綁定一個事件即可:

## 實現03-標記一個格子被鎖定了
為了標記至少有兩種實現思路:
思路一、創建一個 Map 數據類型,保存每個格子是否被鎖定的狀態。 let locked = new Map() // 當點擊一個格子時 locked.set(格子對象, true) // 被鎖定了
思路二、直接在鼠標上自定義一個屬性來標記它的狀態
~~~
使用時直接 對象.dataSet 即可
~~~
以下采用第二種 dataset 的方案

## 實現04-如果鎖定就不允許修改
在鼠標放上和挪開時判斷是否被鎖定:

## 實現05-再次點擊時解鎖

~~~
<!DOCTYPE html>
<html lang="en">
?
<head>
? ?<meta charset="UTF-8">
? ?<meta name="viewport" content="width=device-width, initial-scale=1.0">
? ?<title>Document</title>
? ?<style>
? ? ? ?td {
? ? ? ? ? ?width: 30px;
? ? ? ? ? ?height: 30px;
? ? ? }
? ?</style>
</head>
?
<body>
? ?<div id="box"></div>
?
? ?<script>
? ? ? ?// 先獲取 box 這個 div
? ? ? ?let box = document.getElementById('box')
? ? ? ?let num = prompt('請輸入一個數字:')
? ? ? ?// 創建格子 table
? ? ? ?let table = document.createElement('table')
? ? ? ?table.border = '1'
? ? ? ?// 循環創建 n 個 tr
? ? ? ?for (let i = 0; i < num; i++) {
? ? ? ? ? ?let tr = document.createElement('tr')
? ? ? ? ? ?// 在 tr 中添加 td
? ? ? ? ? ?for (let j = 0; j < num; j++) {
? ? ? ? ? ? ? ?let td = document.createElement('td')
? ? ? ? ? ? ? ?// 把創建的 td 放到 tr 中
? ? ? ? ? ? ? ?tr.appendChild(td)
? ? ? ? ? }
? ? ? ? ? ?// 把 tr 放到 table 中
? ? ? ? ? ?table.appendChild(tr)
? ? ? }
? ? ? ?// 把 table 放到 box 中
? ? ? ?box.appendChild(table)
?
? ? ? ?// 為 table 綁定事件(事件委托技術:將事件綁定到父元素上)
? ? ? ?// e:事件對象
? ? ? ?// mouseover: 鼠標放上
? ? ? ?table.addEventListener('mouseover', function (e) {
? ? ? ? ? ?// e.srcElement: 觸發這個事件的事件源
? ? ? ? ? ?// 如果觸發事件源是 TD 標簽就改變背景色
? ? ? ? ? ?if (e.srcElement.nodeName == 'TD') {
? ? ? ? ? ? ? ?// 判斷當前格子是否被鎖定,如果沒鎖定就修改
? ? ? ? ? ? ? ?if (e.srcElement.dataset.locked != 1) {
? ? ? ? ? ? ? ? ? ?e.srcElement.style.backgroundColor = 'red'
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? })
?
? ? ? ?// mouseout: 鼠標挪開
? ? ? ?table.addEventListener('mouseout', function (e) {
? ? ? ? ? ?// e.srcElement: 觸發這個事件的事件源
? ? ? ? ? ?// 如果觸發事件源是 TD 標簽就改變背景色
? ? ? ? ? ?if (e.srcElement.nodeName == 'TD') {
? ? ? ? ? ? ? ?// 判斷當前格子是否被鎖定,如果沒鎖定就修改
? ? ? ? ? ? ? ?if (e.srcElement.dataset.locked != 1) {
? ? ? ? ? ? ? ? ? ?e.srcElement.style.backgroundColor = 'white'
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? })
?
? ? ? ?// click: 點擊
? ? ? ?table.addEventListener('click', function (e) {
? ? ? ? ? ?// e.srcElement: 觸發這個事件的事件源
? ? ? ? ? ?// 如果觸發事件源是 TD 標簽就改變背景色
? ? ? ? ? ?if (e.srcElement.nodeName == 'TD') {
? ? ? ? ? ? ? ?e.srcElement.style.backgroundColor = 'green'
? ? ? ? ? ? ? ?// 在這個 td 標簽上標記一下,它已經被鎖定了
? ? ? ? ? ? ? ?e.srcElement.dataset.locked = 1
? ? ? ? ? }
? ? ? })
? ?</script>
</body>
?
</html>
~~~
## 擴展:面向對象的寫法

# 題2、彈出兩個框,一個框輸入一個擁有總金額數量(整數),另一個框可以輸入多個商品的價格(多個數字之間用空格隔開),計算得出,這些總金額最多能購買多少件商品(每件商品只能購物一次)。
比如:輸入 總金額:100 多個商品價格:10 40 49 60 70 80 100 300
得出的結果: 最多能買:3 個 哪些價格的商品:10 40 49 花費多少錢:99
實際思路:
1. 先把價格的字符串轉成數組(split 通過空格轉)
2. 對數組排序(sort((a,b)=>a-b))
3. 循環數組從第1個(最便宜的)開始循環累加商品金額,直到累加的金額大于,總金額為止
~~~
// 總金額
let totalPrice = prompt("請輸入總金額:")
// 商品價格列表
let goodsPrice = prompt("請輸入商品價格(多個用空格隔開):")
// 商品價格列表轉數組
let goodsArr = goodsPrice.split(/\s+/) ? // 根據至少一個連續的空格轉數組
// 讓數組升序排列
goodsArr.sort((a,b)=>a-b)
?
// 從數組的第一個商品開始購買
let sum = 0 // 已購買的商品的總價
let buy = [] ? // 已購買的商品的單價
let price
for(let i=0; i<goodsArr.length; i++) {
? // 價格轉成數字(+、Number、parseInt、parseFloat)
? price = Number(goodsArr[i]) ?
? // 如果沒有超出就繼續
? if( (price + sum) <= totalPrice) {
? ? ? sum += price
? ? ? buy.push(price)
? } else {
? ? ? // 超出了就退出
? ? ? break
? }
}
// 結果輸出
console.log("擁有的總錢數:"+totalPrice)
console.log("可以購買的商品列表:"+goodsPrice)
console.log("最多能購買:"+buy.length+" 個商品")
console.log("能夠購買的商品價格為:"+buy.toString())
console.log("購買這件商品共花費:"+sum)
~~~
運行結果:

# 題3、隨機顏色的 99 乘法表

代碼實現:

# 題4、JS 原生購物車
數據:
~~~
const data = [
? {
? ? ? ?"image": "https://img13.360buyimg.com/babel/s1180x940_jfs/t1/112612/27/8633/100927/5ed0ffe3Ee5006a06/142195ec551409e6.jpg.webp",
? ? ? ?"goods_name": "小米移動電源10000mAh",
? ? ? ?"price": "135",
? ? ? ?"comment": "1.4萬"
? },
? {
? ? ? ?"image": "https://img10.360buyimg.com/pop/s1180x940_jfs/t1/133282/15/506/78667/5ece44afEd0d8193e/89395514aa661a69.jpg.webp",
? ? ? ?"goods_name": "小米電源 高配版",
? ? ? ?"price": "135",
? ? ? ?"comment": "1.4萬"
? },
? {
? ? ? ?"https://img10.360buyimg.com/da/s1180x940_jfs/t1/120568/26/3467/101836/5ed0fda0E49973841/e1801a3d7e067ce7.jpg.webp",
? ? ? ?"goods_name": "小米活塞耳機",
? ? ? ?"price": "135",
? ? ? ?"comment": "1.4萬"
? },
? {
? ? ? "image": ? "https://imgcps.jd.com/ling/100008348542/5omL5py66LSt5a6e5oOg/5aSH6LSn6LaF5YC8/p-5bd8253082acdd181d02fa33/28403921/590x470.jpg",
? ? ? ?"goods_name": "小米耳機",
? ? ? ?"price": "135",
? ? ? ?"comment": "1.4萬"
? }
]
~~~

OOP代碼實現:
1. html 和 CSS

2. 購物車類

3. 為類添加渲染數據的方法

4. 渲染購物車表格

5. 加入購物車

6. 刪除

7. 使用

# 題5、表單驗證

# 題6、有一個數組,數組中有10件商品,每件商品的是一對象類型的數據,在頁面中每次顯示三條記錄,并可以上下按鈕翻頁?
~~~
<div id="app"></div>
<button onclick="prev()">上一頁</button>
<button onclick="next()">下一頁</button>
?
/********** 1. 構建 10 件商品的數據 *******/
let data = []
// 循環生成 10 件商品
for(let i=0; i<10; i++) {
? data.push({
? ? ? id: i+1,
? ? ? goods_name: '手機-' + i,
? ? ? price: (Math.random()*100).toFixed(2)
? })
}
?
/*********** 2. 在頁面中渲染三件商品
如何截取數組?
slice:不會修改原數組
splice:從原數組中把截取的數據刪除
?
翻頁?
使用 slice 從數組中截取出三件商品,截取時的下標:
第1頁 --》 0 ~ 3 ?
第2頁 --> ? 3 ~ 6
第3頁 --> 6 ~ 9
第4頁 --> 9 ~ 12
.......
第n頁 --> (n-1)*3 ? ~ (n-1)*3+3
******/
?
?
const app = document.getElementById('app')
?
// 當前頁
let page = 1
?
// 顯示第 i 頁的數據
function showData(page) {
? // 計算截取的下標
? let start = (page-1)*3
? let end = start + 3
? // 截取出第1頁的數據
? let goods = data.slice(start, end)
? // 先把原數據清空
? app.innerHTML = ''
? goods.forEach(v=>{
? ? ? let div = document.createElement('DIV')
? ? ? div.innerHTML = "商品名稱:"+v.goods_name + ",價格:¥"+v.price
? ? ? app.appendChild(div)
? })
}
?
showData( page )
?
// 下一頁
function next() {
? // 如果不是最后一頁
? if(page < 4) {
? ? ? page++
? ? ? showData(page)
? }
}
?
// 上一頁
function prev() {
? // 如果不是第1頁
? if(page > 1) {
? ? ? page--
? ? ? showData(page)
? }
}
~~~
擴展練習:有一個數組,數組中有10件商品,一次在頁面中顯示三件商品,有一個“換一換”,每點擊一次換三件。要求:頁面中始終顯示三件商品,不夠三件時從前面取,比如:最后只剩一件不夠三件了,那么就從最前面拿2件湊夠三件顯示。
實現思路:1. 每次從數組中截取出前三件商品并從數組中把這三件商品刪除(splice(0,3)) \\2. 把截取出的三件商品再合并到數組的最后(concat)
~~~
<button onclick="change()">換一換</button>
?
function change() {
? ?// 取出前3件商品,并從數組中刪除這三件
? ?let goods = data.splice(0,3)
? ?// 把這3件商品再放回數組的最后
? ?data = data.concat(goods)
? ?// 渲染這三件商品
? ?app.innerHTML = ''
? ?goods.forEach(v=>{
? ? ? ?let div = document.createElement('DIV')
? ? ? ?div.innerHTML = "商品名稱:"+v.goods_name + ",價格:¥"+v.price
? ? ? ?app.appendChild(div)
? })
}
~~~
# 題7、實現一個文章搜索功能:制作一個搜索框和一個搜索按鈕,當點擊搜索按鈕時就調用接口搜索相關文章,每頁顯示15條,并實現翻頁功能?
接口文檔地址:[http://ttapi.research.itcast.cn/app/v1\_0/search](http://ttapi.research.itcast.cn/app/v1_0/search) 請求方式:GET 參數:q(搜索關鍵字) page(當前頁碼) per\_page (每頁條數) 使用技術:原生 JS 制作完之后的效果:

代碼實現: HTML 和 CSS

封裝原生 AJAX 為Promise 對象

兩個輔導函數

點擊搜索時調用接口獲取某一頁的數據

根據返回的數據渲染翻頁按鈕和數據列表

搜索聯想功能

點擊翻頁按鈕時重新調用接口

~~~
<!DOCTYPE html>
<html lang="en">
?
<head>
? ?<meta charset="UTF-8">
? ?<meta name="viewport" content="width=device-width, initial-scale=1.0">
? ?<title>Document</title>
? ?<style>
? ? ? ?img {
? ? ? ? ? ?max-width: 10%;
? ? ? }
?
? ? ? ?#suggestion {
? ? ? ? ? ?position: absolute;
? ? ? ? ? ?background-color: #eee;
? ? ? ? ? ?width: 400px;
? ? ? }
?
? ? ? ?#suggestion div {
? ? ? ? ? ?padding: 5px;
? ? ? }
?
? ? ? ?#suggestion div:hover {
? ? ? ? ? ?background-color: skyblue;
? ? ? }
?
? ? ? ?#page {
? ? ? ? ? ?list-style: none;
? ? ? }
?
? ? ? ?#page li {
? ? ? ? ? ?display: inline-block;
? ? ? ? ? ?border: 1px solid skyblue;
? ? ? ? ? ?padding: 5px 10px;
? ? ? ? ? ?margin: 5px;
? ? ? }
?
? ? ? ?#page li:hover,
? ? ? ?#page li.active {
? ? ? ? ? ?background-color: red;
? ? ? ? ? ?color: white;
? ? ? ? ? ?cursor: pointer;
? ? ? }
? ?</style>
</head>
?
<body>
? ?<!-- autocomplete: 關閉瀏覽器顯示的提示 -->
? ?<input type="text" autocomplete="off" id="keywords" placeholder="請輸入內容">
? ?<button id="btn">搜索</button>
? ?<!-- 搜索聯想 -->
? ?<div id="suggestion"></div>
? ?<!-- 數據列表 -->
? ?<ul id="list"></ul>
? ?<!-- 翻頁 -->
? ?<ul id="page"></ul>
?
? ?<script>
? ? ? ?// 封裝原生 Ajax 為 Promise 對象
? ? ? ?const axios = {
? ? ? ? ? ?_xhr: new XMLHttpRequest(),
? ? ? ? ? ?get: function (url) {
? ? ? ? ? ? ? ?return new Promise((ok, err) => {
? ? ? ? ? ? ? ? ? ?this._xhr.onreadystatechange = () => {
? ? ? ? ? ? ? ? ? ? ? ?if (this._xhr.readyState == 4 && this._xhr.status == 200) {
? ? ? ? ? ? ? ? ? ? ? ? ? ?ok(JSON.parse(this._xhr.responseText))
? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ?this._xhr.open('GET', url)
? ? ? ? ? ? ? ? ? ?this._xhr.send()
? ? ? ? ? ? ? })
? ? ? ? ? }
? ? ? }
?
? ? ? ?// 輔導函數
? ? ? ?function $(id) {
? ? ? ? ? ?return document.getElementById(id)
? ? ? }
?
? ? ? ?function C(tag) {
? ? ? ? ? ?return document.createElement(tag)
? ? ? }
?
? ? ? ?// 獲取頁面中的幾個元素
? ? ? ?const btn = $('btn')
? ? ? ?const keywords = $("keywords")
? ? ? ?const list = $('list')
? ? ? ?const suggestion = $("suggestion")
? ? ? ?const page = $('page')
?
? ? ? ?// 當前頁碼
? ? ? ?let pageNum = 1
?
? ? ? ?// 搜索按鈕
? ? ? ?btn.onclick = function () {
? ? ? ? ? ?// 清空聯想框
? ? ? ? ? ?suggestion.innerHTML = ''
? ? ? ? ? ?// 獲取輸入框中的關鍵字并去掉左右空格
? ? ? ? ? ?let value = keywords.value.trim()
? ? ? ? ? ?if (value === '') {
? ? ? ? ? ? ? ?alert('必須要輸入關鍵字!!!')
? ? ? ? ? ? ? ?return false
? ? ? ? ? }
? ? ? ? ? ?// 調用接口取數據
? ? ? ? ? ?axios.get(`http://ttapi.research.itcast.cn/app/v1_0/search?q=${value}&page=${pageNum}&per_page=15`)
? ? ? ? ? ? ? .then(res => {
? ? ? ? ? ? ? ? ? ?if (res.message != 'OK') return alert('接口出錯')
? ? ? ? ? ? ? ? ? ?// 渲染數據
? ? ? ? ? ? ? ? ? ?render(res.data.results)
? ? ? ? ? ? ? ? ? ?// 渲染翻頁
? ? ? ? ? ? ? ? ? ?renderPage(res.data.total_count)
? ? ? ? ? ? ? })
?
? ? ? ? ? ?// 渲染數據
? ? ? ? ? ?function render(data) {
? ? ? ? ? ? ? ?// 清空原數據
? ? ? ? ? ? ? ?list.innerHTML = ''
? ? ? ? ? ? ? ?// 循環渲染
? ? ? ? ? ? ? ?let li, div, img
? ? ? ? ? ? ? ?data.forEach(v => {
? ? ? ? ? ? ? ? ? ?li = C('li')
? ? ? ? ? ? ? ? ? ?div = C('div')
? ? ? ? ? ? ? ? ? ?div.innerHTML = v.title
?
? ? ? ? ? ? ? ? ? ?li.appendChild(div)
? ? ? ? ? ? ? ? ? ?v.cover.images.forEach(k => {
? ? ? ? ? ? ? ? ? ? ? ?img = C('img')
? ? ? ? ? ? ? ? ? ? ? ?img.src = k
? ? ? ? ? ? ? ? ? ? ? ?li.appendChild(img)
? ? ? ? ? ? ? ? ? });
? ? ? ? ? ? ? ? ? ?// li 放到頁面
? ? ? ? ? ? ? ? ? ?list.appendChild(li)
? ? ? ? ? ? ? });
? ? ? ? ? }
?
? ? ? ? ? ?// 渲染翻頁頁碼
? ? ? ? ? ?function renderPage(totalCount) {
? ? ? ? ? ? ? ?// 清空原頁碼
? ? ? ? ? ? ? ?page.innerHTML = ''
?
? ? ? ? ? ? ? ?// 計算總的頁數 = 向上取整(總的記錄數 / 每頁的條數)
? ? ? ? ? ? ? ?let pageCount = Math.ceil(totalCount / 15)
?
? ? ? ? ? ? ? ?// 循環創建按鈕
? ? ? ? ? ? ? ?let li
? ? ? ? ? ? ? ?for (let i = 0; i < pageCount; i++) {
? ? ? ? ? ? ? ? ? ?// 創建一個按鈕
? ? ? ? ? ? ? ? ? ?li = C('li')
? ? ? ? ? ? ? ? ? ?li.innerText = i
? ? ? ? ? ? ? ? ? ?// 如果是當前頁就添加 active 這個類
? ? ? ? ? ? ? ? ? ?if (pageNum == i) {
? ? ? ? ? ? ? ? ? ? ? ?// 修改標簽的 class 有兩種寫法: className 和 classList
? ? ? ? ? ? ? ? ? ? ? ?// li.className = 'active'
? ? ? ? ? ? ? ? ? ? ? ?li.classList.add('active')
? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ?// 放到頁面中
? ? ? ? ? ? ? ? ? ?page.appendChild(li)
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }
?
? ? ? ?// 當搜索框中的值改變時
? ? ? ?keywords.onkeyup = function () {
? ? ? ? ? ?// 獲取輸入框中的關鍵字并去掉左右空格
? ? ? ? ? ?let value = keywords.value.trim()
? ? ? ? ? ?if (value === '') {
? ? ? ? ? ? ? ?return false
? ? ? ? ? }
?
? ? ? ? ? ?// 調用聯想接口 取數據
? ? ? ? ? ?axios.get(`http://ttapi.research.itcast.cn/app/v1_0/suggestion?q=${value}`).then(res => {
? ? ? ? ? ? ? ?if (res.message == 'OK') {
? ? ? ? ? ? ? ? ? ?// 渲染數據
? ? ? ? ? ? ? ? ? ?// 先清空上次數據
? ? ? ? ? ? ? ? ? ?suggestion.innerHTML = ''
? ? ? ? ? ? ? ? ? ?let div
? ? ? ? ? ? ? ? ? ?// 循環數據
? ? ? ? ? ? ? ? ? ?res.data.options.forEach(v => {
? ? ? ? ? ? ? ? ? ? ? ?// 創建div
? ? ? ? ? ? ? ? ? ? ? ?div = C('div')
? ? ? ? ? ? ? ? ? ? ? ?div.innerHTML = v
? ? ? ? ? ? ? ? ? ? ? ?// 放進去
? ? ? ? ? ? ? ? ? ? ? ?suggestion.appendChild(div)
? ? ? ? ? ? ? ? ? })
? ? ? ? ? ? ? }
? ? ? ? ? })
? ? ? }
?
? ? ? ?// 為聯想的選項設置點擊事件
? ? ? ?// 我們可以直接為所有選項的父元素設置點擊事件(事件委托)
? ? ? ?// 因為事件是會冒泡的,點擊子元素時,事件會傳遞給父元素
? ? ? ?// 所以可以由這個父元素統一處理所有子元素的點擊事件
? ? ? ?// e.srcElmenet 代表:具體觸發點擊事件的子元素(事件源)
? ? ? ?suggestion.onclick = function (e) {
? ? ? ? ? ?// 獲取點擊的 div 的內容
? ? ? ? ? ?let content = e.srcElement.innerText
? ? ? ? ? ?// 把內容放到搜索框中
? ? ? ? ? ?keywords.value = content
? ? ? ? ? ?// 隱藏聯想框
? ? ? ? ? ?suggestion.innerHTML = ''
? ? ? ? ? ?// 直接觸發搜索按鈕的點擊事件
? ? ? ? ? ?btn.onclick()
? ? ? }
?
? ? ? ?// 為所有的翻頁按鈕綁定點擊事件,這里我們使用事件委托,把事件綁定給父元素
? ? ? ?page.onclick = function (e) {
? ? ? ? ? ?// 如果點擊的是一個 li 標簽就執行
? ? ? ? ? ?if (e.srcElement.nodeName === 'LI') {
? ? ? ? ? ? ? ?// 如果點擊的頁碼不是當前頁
? ? ? ? ? ? ? ?if (pageNum !== e.srcElement.innerText) {
? ? ? ? ? ? ? ? ? ?// 修改當前頁為點擊的按鈕的頁碼
? ? ? ? ? ? ? ? ? ?pageNum = e.srcElement.innerText
? ? ? ? ? ? ? ? ? ?// console.log(e.srcElement.innerText);
? ? ? ? ? ? ? ? ? ?// 重要根據頁碼調用接口獲取數據
? ? ? ? ? ? ? ? ? ?btn.onclick()
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }
? ?</script>
</body>
?
</html>
~~~