[JS] 常用 JavaScript snippets
UI 相關
將捲軸到該元素最下方 (scroll to bottom)
const scrollToBottom = (targetElement) => {
targetElement.scrollTo({
top: targetElement.scrollHeight,
behavior: 'smooth',
});
};
避免按鈕點選後卡在 focus 狀態
keywords: prevent button focus when click
使用 e.currentTarget.blur()。
<button id="prevent-focus">FooBar</button>
const btn = document.querySelector('#prevent-focus');
btn.addEventListener('click', (e) => e.currentTarget.blur());
如果只使用
e.target.blur()有可能當點選按鈕中的圖案時,因為target指稱到的是裡面的圖案,所以會無效。
取得鼠標座標(需檢查有效性)
function mousePosition(event) {
if (event.pageX || event.pageY) {
return { x: event.pageX, y: event.pageY };
}
return {
x: event.clientX + document.body.scrollLeft - document.body.clientLeft,
y: event.clientY + document.body.scrollTop - document.body.clientTop,
};
}
取得座標相關資料(需檢查有效性)
檔案可視區域寬: document.documentElement.clientWidth
檔案可視區域高: document.documentElement.clientHeight
網頁可見區域寬: document.body.clientWidth
網頁可見區域高: document.body.clientHeight
網頁可見區域寬: document.body.offsetWidth (包括邊線的寬)
網頁可見區域高: document.body.offsetHeight (包括邊線的高)
網頁正文全文寬: document.body.scrollWidth
網頁正文全文高: document.body.scrollHeight
網頁被捲去的高: document.body.scrollTop
網頁被捲去的左: document.body.scrollLeft
網頁正文部分上: window.screenTop
網頁正文部分左: window.screenLeft
螢幕分辨率的高: window.screen.height
螢幕分辨率的寬: window.screen.width
螢幕可用工作區高度: window.screen.availHeight
螢幕可用工作區寬度: window.screen.availWidth
取得瀏覽器捲軸位置
let scrollOffset = window.scrollY || document.documentElement.scrollTop;
window.scrollY @ MDN
Lazy Load
相關內容可參考:Preload Content 內容預先載入(Lazy Load)
font lazy load
可以監聽字體載入的事件,當字體載入完成後,再放入 CSS 的 font-family 內,如此可以避免一開始畫面沒有文字的情況:
// font lazy load
document.fonts.load('12px Noto Sans TC').then(function () {
const body = document.querySelector('body');
body.style.fontFamily = `"Noto Sans TC", ${window
.getComputedStyle(body)
.getPropertyValue('font-family')}`;
});
顏色相關 Color Related
RGB To Hex
const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');
RGB to Hex @ 30 seconds of code
Random Hex Color
const randomHexColorCode = () => {
let n = (Math.random() * 0xfffff * 1000000).toString(16);
return '#' + n.slice(0, 6);
};
Random Hex Color @ 30 seconds of code
檔案相關 File Related
格式化上傳檔案大小
const prettyBytes = (num, precision = 3, addSpace = true) => {
const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0];
const exponent = Math.min(Math.floor(Math.log10(num < 0 ? -num : num) / 3), UNITS.length - 1);
const n = Number(((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision));
return (num < 0 ? '-' : '') + n + (addSpace ? ' ' : '') + UNITS[exponent];
};
prettyBytes(1000); // "1 KB"
prettyBytes(-27145424323.5821, 5); // "-27.145 GB"
prettyBytes(123456789, 3, false); // "123MB"
Pretty Bytes @ 30 seconds of code
限制上傳檔案類型
<!-- 當今瀏覽器 -->
<input type="file" name="filePath" accept=".jpg,.jpeg,.doc,.docx,.pdf" />
<!-- 限制圖片 -->
<input type="file" class="file" value="上傳" accept="image/*" />
好用工具(Utilities)
pause / sleep
const pause = (ms) => {
let time = new Date();
while (new Date() - time <= ms);
};
text truncate
// DEMO:
// https://repl.it/@PJCHENder/TextTruncate-Function
var truncate = function (elem, limit, after) {
// Make sure an element and number of items to truncate is provided
if (!elem || !limit) return;
// Get the inner content of the element
var content = elem.textContent.trim();
// Convert the content into an array of words
// Remove any words above the limit
content = content.split(' ').slice(0, limit);
// Convert the array of words back into a string
// If there's content to add after it, add it
content = content.join(' ') + (after ? after : '');
// Inject the content back into the DOM
elem.textContent = content;
};
debounce
throttle 和 debounce 的差別在於,throttle 指的是在「一定時間」內最多只能促發某函式「多少次」;debounce 則是指在「一定時間」內能夠促發這個函式「一次」。
/**
* 將要 debounce 的函式代入 func 中,不用 invoke
* wait,設定時間(毫秒)
* immediate 如果是 true 則在事件觸發時會立即執行,
* 但事件結束時不會執行;如果是 false 則事件觸發時不會立即執行,
* 但事件結束時會執行。
**/
export function debounce(callback, wait, immediate) {
let timeout;
function wrapper(...args) {
const self = this;
const later = () => {
timeout = null;
if (!immediate) callback.apply(self, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) callback.apply(this, args);
}
return wrapper;
}
The Difference between Throttling and Debouncing @ CSS Tricks Debouncing and Throttling Explained Through Examples @ CSS Tricks
在一般的情況下使用
/**
* 一秒內只會觸發一次 click 內的 callback
**/
const btn = document.querySelector('#btn');
btn.addEventListener(
'click',
debounce(
(e) => {
console.log('click', e);
},
1000,
true,
),
);
在 Vue 中使用
// 將 debounce function 放在 Vue Instance 外面
function debounce () { ... }
new Vue({
methods: {
wantThisDebounce: debounce(function(){
// Put what you want to do here
}, 1500, false)
}
})
throttle
export function throttle(callback, limit) {
let wait = false;
// DONOT change to arrow function, otherwise 'this' can not be reset
function wrapper(...args) {
if (!wait) {
callback.apply(this, args);
wait = true;
setTimeout(() => {
wait = false;
}, limit);
}
}
return wrapper;
}