CSS 部分
兩欄布局
查看代碼要求:垂直兩欄,左邊固定右邊自適應。
<!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>
<style>
.outer {
height: 100px;
margin-bottom: 10px;
}
.left {
background: tomato;
height: 100px;
}
.right {
background: gold;
height: 100px;
}
/* 浮動 */
.outer1 .left {
width: 200px;
float: left;
}
.outer1 .right {
width: auto;
margin-left: 200px;
}
/* flex */
.outer2 {
display: flex;
}
.outer2 .left {
flex-grow: 0;
flex-shrink: 0;
flex-basis: 200px;
}
.outer2 .right {
flex: auto; /* 1 1 auto */
}
/* position */
.outer3 {
position: relative;
}
.outer3 .left {
position: absolute;
width: 200px;
}
.outer3 .right {
margin-left: 200px;
}
/* position again */
.outer4 {
position: relative;
}
.outer4 .left {
width: 200px;
}
.outer4 .right {
position: absolute;
top: 0;
left: 200px;
right: 0;
}
</style>
</head>
<!-- 左右兩欄,左邊固定,右邊自適應 -->
<body>
<div class="outer outer1">
<div class="left">1-left</div>
<div class="right">1-right</div>
</div>
<div class="outer outer2">
<div class="left">2-left</div>
<div class="right">2-right</div>
</div>
<div class="outer outer3">
<div class="left">3-left</div>
<div class="right">3-right</div>
</div>
<div class="outer outer4">
<div class="left">4-left</div>
<div class="right">4-right</div>
</div>
</body>
</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>
<style>
.outer, .left, .middle, .right {
height: 100px;
margin-bottom: 5px;
}
.left {
background: tomato;
}
.middle {
background: lightgreen;
}
.right {
background: gold;
}
/* 左右分別設置絕對定位 中間設置外邊距 */
.outer1 {
position: relative;
}
.outer1 .left {
position: absolute;
width: 100px;
}
.outer1 .middle {
margin: 0 200px 0 100px;
}
.outer1 .right {
position: absolute;
width: 200px;
top: 0;
right: 0;
}
/* flex 布局 */
.outer2 {
display: flex;
}
.outer2 .left {
flex: 0 0 100px;
}
.outer2 .middle {
flex: auto;
}
.outer2 .right {
flex: 0 0 200px;
}
/* 浮動布局 但是 html 中 middle要放到最后 */
.outer3 .left {
float: left;
width: 100px;
}
.outer3 .right {
float: right;
width: 200px;
}
.outer3 .middle {
margin: 0 200px 0 100px;
}
</style>
</head>
<!-- 三欄布局 左右固定 中間自適應 -->
<body>
<div class="outer outer1">
<div class="left">1-left</div>
<div class="middle">1-middle</div>
<div class="right">1-right</div>
</div>
<div class="outer outer2">
<div class="left">2-left</div>
<div class="middle">2-middle</div>
<div class="right">2-right</div>
</div>
<div class="outer outer3">
<div class="left">3-left</div>
<div class="right">3-right</div>
<div class="middle">3-middle</div>
</div>
</body>
</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>
<style>
.outer, .left, .middle, .right {
height: 100px;
margin-bottom: 5px;
}
.left {
background: tomato;
}
.middle {
background: lightgreen;
}
.right {
background: gold;
}
/* 聖杯布局 通過浮動和負邊距實現 */
.outer1 {
padding: 0 200px 0 100px;
}
.outer1 .middle {
width: 100%;
float: left;
}
.outer1 .left {
width: 100px;
float: left;
margin-left: -100%;
position: relative;
left: -100px;
}
.outer1 .right {
width: 200px;
float: left;
margin-left: -200px;
position: relative;
left: 200px;
}
/* 雙飛翼布局 */
.outer2 .middle-wrapper {
width: 100%;
float: left;
}
.outer2 .middle {
margin: 0 200px 0 100px;
}
.outer2 .left {
width: 100px;
float: left;
margin-left: -100%;
}
.outer2 .right {
width: 200px;
float: left;
margin-left: -200px;
}
</style>
</head>
<!-- 三欄布局 左右固定 中間自適應 -->
<body>
<!-- 聖杯布局 middle 最先 -->
<div class="outer outer1">
<div class="middle">聖杯-middle</div>
<div class="left">聖杯-left</div>
<div class="right">聖杯-right</div>
</div>
<!-- 雙飛翼布局 middle 最先 多一層 div -->
<div class="outer outer2">
<div class="middle-wrapper">
<div class="middle">雙飛翼布局-middle</div>
</div>
<div class="left">雙飛翼布局-left</div>
<div class="right">雙飛翼布局-right</div>
</div>
</body>
</html>
三角形
實現一個三角形
常見題目,通過 border 實現
<!DOCTYPE html> <html> <head> <title>三角形</title> <style type="text/css"> .box1, .box2, .box3, .box4 { height: 0px; width: 0px; float: left; border-style: solid; margin: 10px; } .box1 { /* 等腰直角 */ border-width: 100px; border-color: tomato transparent transparent transparent; } .box2 { /* 等邊 */ border-width: 100px 173px; border-color: transparent tomato transparent transparent; } .box3 { /* 等腰 */ border-width: 100px 80px; border-color: transparent transparent tomato transparent; } .box4 { /* 其他 */ border-width: 100px 90px 80px 70px; border-color: transparent transparent transparent tomato; } </style> </head> <body> <div class="box1"></div> <div class="box2"></div> <div class="box3"></div> <div class="box4"></div> </body> </html>
正方形
查看代碼使用 css 實現一個寬高自適應的正方形
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
/* 都是像對於屏幕寬度的比例 */
.square1 {
width: 10%;
height: 10vw;
background: red;
}
/* margin/padding 百分比是相對父元素 width 的 */
.square2 {
width: 20%;
height: 0;
padding-top: 20%;
background: orange;
}
/* 通過子元素 margin */
.square3 {
width: 30%;
overflow: hidden; /* 觸發 BFC */
background: yellow;
}
.square3::after {
content: '';
display: block;
margin-top: 100%; /* 高度相對於 square3 的 width */
}
</style>
</head>
<!-- 畫一個正方形 -->
<body>
<div class="square1"></div>
<div class="square2"></div>
<div class="square3"></div>
</body>
</html>
復制代碼
扇形
實現一個 1/4 圓、任意弧度的扇形
有多種實現方法,這里選幾種簡單方法(我看得懂的)實現。
查看代碼<!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>
<style>
/* 通過 border 和 border-radius 實現 1/4 圓 */
.sector1 {
height: 0;
width: 0;
border: 100px solid;
border-radius: 50%;
border-color: turquoise tomato tan thistle;
}
/* 類似三角形的做法加上父元素 overflow: hidden; 也可以實現任意弧度圓 */
.sector2 {
height: 100px;
width: 200px;
border-radius: 100px 100px 0 0;
overflow: hidden;
}
.sector2::after {
content: '';
display: block;
height: 0;
width: 0;
border-style: solid;
border-width: 100px 58px 0;
border-color: tomato transparent;
transform: translate(42px,0);
}
/* 通過子元素 rotateZ 和父元素 overflow: hidden 實現任意弧度扇形(此處是60°) */
.sector3 {
height: 100px;
width: 100px;
border-top-right-radius: 100px;
overflow: hidden;
/* background: gold; */
}
.sector3::after {
content: '';
display: block;
height: 100px;
width: 100px;
background: tomato;
transform: rotateZ(-30deg);
transform-origin: left bottom;
}
/* 通過 skewY 實現一個60°的扇形 */
.sector4 {
height: 100px;
width: 100px;
border-top-right-radius: 100px;
overflow: hidden;
}
.sector4::after {
content: '';
display: block;
height: 100px;
width: 100px;
background: tomato;
transform: skewY(-30deg);
transform-origin: left bottom;
}
/* 通過漸變設置60°扇形 */
.sector5 {
height: 200px;
width: 200px;
background: tomato;
border-radius: 50%;
background-image: linear-gradient(150deg, transparent 50%, #fff 50%),
linear-gradient(90deg, #fff 50%, transparent 50%);
}
</style>
</head>
<body>
<div style="display: flex; justify-content: space-around;">
<div class="sector1"></div>
<div class="sector2"></div>
<div class="sector3"></div>
<div class="sector4"></div>
<div class="sector5"></div>
</div>
</body>
</html>
水平垂直居中
查看代碼實現子元素的水平垂直居中
<!DOCTYPE html> <html> <head> <title>水平垂直居中</title> <style type="text/css"> .outer { height: 200px; width: 200px; background: tomato; margin: 10px; float: left; position: relative; } .inner { height: 100px; width: 100px; background: black; } /* * 通過 position 和 margin 居中 * 缺點:需要知道 inner 的長寬 */ .inner1 { position: absolute; top: 50%; left: 50%; margin-top: -50px; margin-left: -50px; } /* * 通過 position 和 margin 居中 (2 */ .inner2 { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; } /* * 通過 flex 進行居中 */ .outer3 { display: flex; justify-content: center; align-items: center; } /** * 通過 position 和 transform 居中 */ .inner4 { top: 50%; left: 50%; transform: translate(-50%,-50%); position: absolute; } </style> </head> <body> <div class="outer outer1"> <div class="inner inner1"></div> </div> <div class="outer outer2"> <div class="inner inner2"></div> </div> <div class="outer outer3"> <div class="inner inner3"></div> </div> <div class="outer outer4"> <div class="inner inner4"></div> </div> </body> </html>
清除浮動
要求:清除浮動
可以通過 clear:both 或 BFC 實現
<!DOCTYPE html> <html> <head> <title>清除浮動</title> <style type="text/css"> .outer { width: 200px; background: tomato; margin: 10px; position: relative; } .inner { height: 100px; width: 100px; background: pink; margin: 10px; float: left; } /* 偽元素 */ .outer1::after { content: ''; display: block; clear: both; } /* 創建 BFC */ .outer2 { overflow: hidden; } </style> </head> <body> <div class="outer outer1"> <div class="inner"></div> </div> <div class="outer outer2"> <div class="inner"></div> </div> </body> </html>
彈出框
查看代碼使用 CSS 寫一個彈出框效果
<!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>
<style>
.bg {
height: 666px;
width: 100%;
font-size: 60px;
text-align: center;
}
.dialog {
z-index: 999;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
}
.dialog .content {
min-height: 300px;
width: 600px;
background: #fff;
border-radius: 5px;
border: 1px solid #ebeef5;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div class="bg">
頁面內容
</div>
<div class="dialog">
<div class="content">
彈出框
</div>
</div>
</body>
</html>
導航欄
查看代碼要求:一個
div內部放很多水平div,並可以橫向滾動。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=div, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body,html {
margin: 0;
padding: 0;
}
/* flex 實現 */
/* .nav {
display: flex;
height: 30px;
border: 1px solid #000;
padding: 3px;
overflow-x: auto;
}
.nav::-webkit-scrollbar {
display: none;
}
.item {
flex: 0 0 200px;
height: 30px;
margin-right: 5px;
background: gray;
} */
/* inline-block 和 white-space: nowrap; 實現 */
.nav {
height: 30px;
padding: 3px;
border: 1px solid #000;
overflow-x: auto;
white-space: nowrap;
}
.nav::-webkit-scrollbar {
display: none;
}
.item {
display: inline-block;
width: 200px;
height: 30px;
margin-right: 5px;
background: gray;
}
</style>
</head>
<!-- 水平滾動導航欄 -->
<body>
<div class="nav">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
<div class="item">item4</div>
<div class="item">item5</div>
<div class="item">item6</div>
<div class="item">item7</div>
<div class="item">item8</div>
<div class="item">item9</div>
</div>
</body>
</html>
CSS 部分完,總結,Flex 無敵。
JavaScript 部分
手寫 bind、call 和 apply
Function.prototype.bind = function(context, ...bindArgs) { // func 為調用 bind 的原函數 const func = this; context = context || window; if (typeof func !== 'function') { throw new TypeError('Bind must be called on a function'); } // bind 返回一個綁定 this 的函數 return function(...callArgs) { let args = bindArgs.concat(callArgs); if (this instanceof func) { // 意味着是通過 new 調用的 而 new 的優先級高於 bind return new func(...args); } return func.call(context, ...args); } } // 通過隱式綁定實現 Function.prototype.call = function(context, ...args) { context = context || window; context.func = this; if (typeof context.func !== 'function') { throw new TypeError('call must be called on a function'); } let res = context.func(...args); delete context.func; return res; } Function.prototype.apply = function(context, args) { context = context || window; context.func = this; if (typeof context.func !== 'function') { throw new TypeError('apply must be called on a function'); } let res = context.func(...args); delete context.func; return res; }
實現一個繼承
// 參考 You Dont Know JavaScript 上卷 // 基類 function Base() { } // 派生類 function Derived() { Base.call(this); } // 將派生類的原型的原型鏈掛在基類的原型上 Object.setPrototypeOf(Derived.prototype, Base.prototype);
實現一個 new
// 手動實現一個 new 關鍵字的功能的函數 _new(fun, args) --> new fun(args)
function _new(fun, ...args) { if (typeof fun !== 'function') { return new Error('參數必須是一個函數'); } let obj = Object.create(fun.prototype); let res = fun.call(obj, ...args); if (res !== null && (typeof res === 'object' || typeof res === 'function')) { return res; } return obj; }
實現一個 instanceof
// a instanceof b function _instanceof(a, b) { while (a) { if (a.__proto__ === b.prototype) return true; a = a.__proto__; } return false; }
手寫 jsonp 的實現
// foo 函數將會被調用 傳入后台返回的數據 function foo(data) { console.log('通過jsonp獲取后台數據:', data); document.getElementById('data').innerHTML = data; } /** * 通過手動創建一個 script 標簽發送一個 get 請求 * 並利用瀏覽器對 <script> 不進行跨域限制的特性繞過跨域問題 */ (function jsonp() { let head = document.getElementsByTagName('head')[0]; // 獲取head元素 把js放里面 let js = document.createElement('script'); js.src = 'http://domain:port/testJSONP?a=1&b=2&callback=foo'; // 設置請求地址 head.appendChild(js); // 這一步會發送請求 })(); // 后台代碼 // 因為是通過 script 標簽調用的 后台返回的相當於一個 js 文件 // 根據前端傳入的 callback 的函數名直接調用該函數 // 返回的是 'foo(3)' function testJSONP(callback, a, b) { return `${callback}(${a + b})`; }
ajax 的實現
感覺這個有點無聊了……
查看代碼// Asynchronous Javascript And XML function ajax(options) { // 選項 var method = options.method || 'GET', params = options.params, data = options.data, url = options.url + (params ? '?' + Object.keys(params).map(key => key + '=' + params[key]).join('&') : ''), async = options.async === false ? false : true, success = options.success, headers = options.headers; var request; if (window.XMLHttpRequest) { request = new XMLHttpRequest(); } else { request = new ActiveXObject('Microsoft.XMLHTTP'); } request.onstatechange = function() { /** readyState: 0: 請求未初始化 1: 服務器連接已建立 2: 請求已接收 3: 請求處理中 4: 請求已完成,且響應已就緒 status: HTTP 狀態碼 **/ if (request.readyState === 4 && request.status === 200) { success && success(request.responseText); } } request.open(method, url, async); if (headers) { Object.keys(headers).forEach(key => request.setRequestHeader(key, headers[key])); } method === 'GET' ? request.send() : request.send(request.data); } // e.g. ajax({ method: 'GET', url: '...', success: function(res) { console.log('success', res); }, async: true, params: { p: 'test', t: 666 }, headers: { 'Content-Type': 'application/json' } })
reduce 的實現
function reduce(arr, callback, initial) { let i = 0; let acc = initial === undefined ? arr[i++] : initial; for (; i < arr.length; i++) { acc = callback(acc, arr[i], i, arr); } return acc; }
實現 generator 的自動執行器
要求是 yield 后面只能是 Promise 或 Thunk 函數,詳見 es6.ruanyifeng.com/#docs/gener…
function run(gen) { let g = gen(); function next(data) { let result = g.next(data); if (result.done) return result.value; if (result.value instanceof Promise) { result.value.then(data => next(data)); } else { result.value(next); } } return next(); } // ======== e.g. ========== function func(data, cb) { console.log(data); cb(); } function *gen() { let a = yield Promise.resolve(1); console.log(a); let b = yield Promise.resolve(2); console.log(b); yield func.bind(null, a + b); } run(gen); /** output: 1 2 3 **/
節流
老生常談了,感覺沒必要寫太復雜
/** * 節流函數 限制函數在指定時間段只能被調用一次 * 用法 比如防止用戶連續執行一個耗時操作 對操作按鈕點擊函數進行節流處理 */ function throttle(func, wait) { let timer = null; return function(...args) { if (!timer) { func(...args); timer = setTimeout(() => { timer = null; }, wait); } } }
防抖
/** * 函數調用后不會被立即執行 之后連續 wait 時間段沒有調用才會執行 * 用法 如處理用戶輸入 */ function debounce(func, wait) { let timer = null; return function(...args) { if (timer) clearTimeout(timer); // 如果在定時器未執行期間又被調用 該定時器將被清除 並重新等待 wait 秒 timer = setTimeout(() => { func(...args); }, wait); } }
手寫 Promise
簡單實現,基本功能都有了。
const PENDING = 1; const FULFILLED = 2; const REJECTED = 3; function MyPromise(executor) { let self = this; this.resolveQueue = []; this.rejectQueue = []; this.state = PENDING; this.val = undefined; function resolve(val) { if (self.state === PENDING) { setTimeout(() => { self.state = FULFILLED; self.val = val; self.resolveQueue.forEach(cb => cb(val)); }); } } function reject(err) { if (self.state === PENDING) { setTimeout(() => { self.state = REJECTED; self.val = err; self.rejectQueue.forEach(cb => cb(err)); }); } } try { // 回調是異步執行 函數是同步執行 executor(resolve, reject); } catch(err) { reject(err); } } MyPromise.prototype.then = function(onResolve, onReject) { let self = this; // 不傳值的話默認是一個返回原值的函數 onResolve = typeof onResolve === 'function' ? onResolve : (v => v); onReject = typeof onReject === 'function' ? onReject : (e => { throw e }); if (self.state === FULFILLED) { return new MyPromise(function(resolve, reject) { setTimeout(() => { try { let x = onResolve(self.val); if (x instanceof MyPromise) { x.then(resolve); } else { resolve(x); } } catch(e) { reject(e); } }); }); } if (self.state === REJECTED) { return new MyPromise(function(resolve, reject) { setTimeout(() => { try { let x = onReject(self.val); if (x instanceof MyPromise) { x.then(resolve); } else { resolve(x); } } catch(e) { reject(e); } }); }); } if (self.state === PENDING) { return new MyPromise(function(resolve, reject) { self.resolveQueue.push((val) => { try { let x = onResolve(val); if (x instanceof MyPromise) { x.then(resolve); } else { resolve(x); } } catch(e) { reject(e); } }); self.rejectQueue.push((val) => { try { let x = onReject(val); if (x instanceof MyPromise) { x.then(resolve); } else { resolve(x); } } catch(e) { reject(e); } }); }); } } MyPromise.prototype.catch = function(onReject) { return this.then(null, onReject); } MyPromise.all = function(promises) { return new MyPromise(function(resolve, reject) { let cnt = 0; let result = []; for (let i = 0; i < promises.length; i++) { promises[i].then(res => { result[i] = res; if (++cnt === promises.length) resolve(result); }, err => { reject(err); }) } }); } MyPromise.race = function(promises) { return new MyPromise(function(resolve, reject) { for (let i = 0; i < promises.length; i++) { promises[i].then(resolve, reject); } }); } MyPromise.resolve = function(val) { return new MyPromise(function(resolve, reject) { resolve(val); }); } MyPromise.reject = function(err) { return new MyPromise(function(resolve, reject) { reject(err); }) }
實現一個路由 - Hash
實現原理就是監聽 url 的哈希值變化了
<!DOCTYPE html>
<html>
<head>
<title>hash 路由</title>
</head>
<body>
<header>
<a href="#home">首頁</a>
<a href="#center">個人中心頁</a>
<a href="#help">幫助頁</a>
</header>
<section id="content"></section>
<script>
window.addEventListener('hashchange', (e) => {
let content = document.getElementById('content');
content.innerText = location.hash;
})
</script>
</body>
</html>
路由實現 - history
<!DOCTYPE html>
<html>
<head>
<title>history 路由</title>
</head>
<body>
<header>
<a onclick="changeRoute(this)" data-path="home">首頁</a>
<a onclick="changeRoute(this)" data-path="center">個人中心頁</a>
<a onclick="changeRoute(this)" data-path="help">幫助頁</a>
</header>
<section id="content"></section>
<script>
function changeRoute(route) {
let path = route.dataset.path;
/**
* window.history.pushState(state, title, url)
* state:一個與添加的記錄相關聯的狀態對象,主要用於popstate事件。該事件觸發時,該對象會傳入回調函數。
* 也就是說,瀏覽器會將這個對象序列化以后保留在本地,重新載入這個頁面的時候,可以拿到這個對象。
* 如果不需要這個對象,此處可以填 null。
* title:新頁面的標題。但是,現在所有瀏覽器都忽視這個參數,所以這里可以填空字符串。
* url:新的網址,必須與當前頁面處在同一個域。瀏覽器的地址欄將顯示這個網址。
*/
changePage(path);
history.pushState({ content: path }, null, path);
}
/**
* 調用 history.pushState() 或者 history.replaceState() 不會觸發 popstate 事件。
* 點擊后退、前進按鈕、或者在 js 中調用 history.back()、history.forward()、history.go() 方法會觸發
*/
window.addEventListener('popstate', (e) => {
let content = e.state && e.state.content;
changePage(content);
});
function changePage(pageContent) {
let content = document.getElementById('content');
content.innerText = pageContent;
}
</script>
</body>
</html>
還有一些稍復雜的可以寫,有時間再補。
作者:我不吃餅干呀
鏈接:https://juejin.im/post/5c9edb066fb9a05e267026dc
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
