使用純JavaScript封裝一個消息提示條功能示例詳解
介紹
一個類似Element UI
、Ant-Design UI
等 UI 框架的消息提示功能,方便在任何網頁環境中直接調用函數使用;區別在不依賴 js 及 css 引用,而是使用純 js 進行封裝實現,代碼更精簡,同時保持和 UI 框架一樣的視覺效果(可自行修改成自己喜歡的樣式)
代碼倉庫
在線預覽效果(點擊【登錄】、【點擊復制】按鈕時觸發提示效果)
思路&佈局
- 先來寫單個提示條,並實現想要的過渡效果,最後再用邏輯操作輸出節點即可;這裡不需要父節點包裹,直接輸出到
<body>
中,保證提示條的代碼結構位置永遠在最上層。
<style> .msg-box { position: fixed; top: 0; left: 50%; display: flex; padding: 12px 16px; border-radius: 2px; background-color: #fff; box-shadow: 0 3px 3px -2px rgba(0,0,0,.2),0 3px 4px 0 rgba(0,0,0,.14),0 1px 8px 0 rgba(0,0,0,.12); transform: translate3d(-50%, 0%, 0); } </style> <body> <div class="msg-box"></div> </body>
- 最基礎的樣式寫好之後,來定義進入的過渡動畫,這裡使用
animation
作為進入動畫,因為節點一輸出就會執行瞭
.msg-box { ...省略以上代碼 animation: msg-move .4s; } @keyframes msg-move { 0% { opacity: 0; transform: translate3d(-50%, -100%, 0); } 100% { opacity: 1; transform: translate3d(-50%, 0%, 0); } }
- 最後就是過渡結束動畫,這裡使用
transition
的過渡方式,即定義一個.hide
,之後通過 js 去控制切換 class 去實現過渡切換
.msg-box { ...省略以上代碼 opacity: 1; transition: .4s all; // 保持和 animation 的過渡時間一致 } .msg-box.hide { opacity: 0; transform: translate3d(-50%, -100%, 0); }
這裡樣式佈局就全部完成瞭,剩下的交給 js 去處理對應的操作邏輯。
操作邏輯
- 因為調用時,消息條是多個,並且為往下疊加的效果,且節點是散佈在
<body>
下,有可能給其他dom
操作插入節點。所以在輸出節點的時候要將其存放起來,然後通過循環的方式去設置每一個節點的padding-top
,這樣就能保證視覺排列效果和代碼操作的順序保持一致瞭,之後所做的刪除操作也是通過循環去設置每一個節點的padding-top
。
/** * 消息隊列 * @type {Array<HTMLElement>} */ const messageList = []; /** * 獲取指定`item`的定位`top` * @param {HTMLElement=} el */ function getItemTop(el) { let top = 10; // 起始的邊距 for (let i = 0; i < messageList.length; i++) { const item = messageList[i]; if (el && el === item) { break; } top += item.clientHeight + 20; // 兩個消息條的間距為20 } return top; } /** * 刪除指定列表項 * @param {HTMLElement} el */ function removeItem(el) { for (let i = 0; i < messageList.length; i++) { const item = messageList[i]; if (item === el) { messageList.splice(i, 1); break; } } el.classList.add(".hide"); messageList.forEach(function (item) { item.style.top = `${getItemTop(item)}px`; }); }
- 輸出節點,並監聽 動畫進入過渡、持續時間、動畫退出過渡 ;
- 進入的過渡使用
addEventListener("animationend", fn)
- 持續時間使用
setTimeout
(延遲 N 秒之後為節點添加.hide
) - 退出的過渡使用
addEventListener("transitionend", fn)
/** 一直累加的定位層級 */ let zIndex = 1000; /** * 顯示一條消息 * @param {string} content 內容 * @param {number} duration 持續時間,優先級比默認值高 */ function show(content, duration) { const el = document.createElement("div"); el.style.top = `${getItemTop()}px`; el.style.zIndex = zIndex; el.innerHTML = content; zIndex++; messageList.push(el); document.body.appendChild(el); // 添加動畫監聽事件 function animationEnd() { el.removeEventListener("animationend", animationEnd); setTimeout(removeItem, duration || 3000, el); } el.addEventListener("animationend", animationEnd); function transitionEnd() { if (getComputedStyle(el).opacity !== "0") return; el.removeEventListener("transitionend", transitionEnd); el.remove(); } el.addEventListener("transitionend", transitionEnd); }
整個消息輸出功能就完成瞭,最後隻需要把對應的方法封裝起來並暴露需要調用的函數,並把css
樣式寫進方法裡就可以在任意地方使用瞭;css
寫進js
裡其實就是把上面寫好的樣式,通過字符串模板的方式用變量接收並賦值給動態輸入的<style>
標簽就完事,另外樣式隔離可以通過模擬css.modeule
的方案去實現,具體看下面完整代碼。
完整代碼
/** * 消息提示條 * @param {object} params * @param {number} params.duration 持續時間(毫秒),默認`3000` * @param {number} params.zIndex 起始定位層級,默認`1000` */ function useMessage(params = {}) { const doc = document; const cssModule = `__${Math.random().toString(36).slice(2, 7)}`; const className = { box: `msg-box${cssModule}`, hide: `hide${cssModule}`, text: `msg-text${cssModule}`, icon: `msg-icon${cssModule}` } const style = doc.createElement("style"); style.textContent = ` .${className.box}, .${className.icon}, .${className.text} { padding: 0; margin: 0; box-sizing: border-box; } .${className.box} { position: fixed; top: 0; left: 50%; display: flex; padding: 12px 16px; border-radius: 2px; background-color: #fff; box-shadow: 0 3px 3px -2px rgba(0,0,0,.2),0 3px 4px 0 rgba(0,0,0,.14),0 1px 8px 0 rgba(0,0,0,.12); white-space: nowrap; animation: ${className.box}-move .4s; transition: .4s all; transform: translate3d(-50%, 0%, 0); opacity: 1; overflow: hidden; } .${className.box}::after { content: ""; position: absolute; left: 0; top: 0; height: 100%; width: 4px; } @keyframes ${className.box}-move { 0% { opacity: 0; transform: translate3d(-50%, -100%, 0); } 100% { opacity: 1; transform: translate3d(-50%, 0%, 0); } } .${className.box}.${className.hide} { opacity: 0; /* transform: translate3d(-50%, -100%, 0); */ transform: translate3d(-50%, -100%, 0) scale(0); } .${className.icon} { display: inline-block; width: 18px; height: 18px; border-radius: 50%; overflow: hidden; margin-right: 6px; position: relative; } .${className.text} { font-size: 14px; line-height: 18px; color: #555; } .${className.icon}::after, .${className.icon}::before { position: absolute; content: ""; background-color: #fff; } .${className.box}.info .${className.icon}, .${className.box}.info::after { background-color: #1890ff; } .${className.box}.success .${className.icon}, .${className.box}.success::after { background-color: #52c41a; } .${className.box}.warning .${className.icon}, .${className.box}.warning::after { background-color: #faad14; } .${className.box}.error .${className.icon}, .${className.box}.error::after { background-color: #ff4d4f; } .${className.box}.info .${className.icon}::after, .${className.box}.warning .${className.icon}::after { top: 15%; left: 50%; margin-left: -1px; width: 2px; height: 2px; border-radius: 50%; } .${className.box}.info .${className.icon}::before, .${className.box}.warning .${className.icon}::before { top: calc(15% + 4px); left: 50%; margin-left: -1px; width: 2px; height: 40%; } .${className.box}.error .${className.icon}::after, .${className.box}.error .${className.icon}::before { top: 20%; left: 50%; width: 2px; height: 60%; margin-left: -1px; border-radius: 1px; } .${className.box}.error .${className.icon}::after { transform: rotate(-45deg); } .${className.box}.error .${className.icon}::before { transform: rotate(45deg); } .${className.box}.success .${className.icon}::after { box-sizing: content-box; background-color: transparent; border: 2px solid #fff; border-left: 0; border-top: 0; height: 50%; left: 35%; top: 13%; transform: rotate(45deg); width: 20%; transform-origin: center; } `.replace(/(\n|\t|\s)*/ig, "$1").replace(/\n|\t|\s(\{|\}|\,|\:|\;)/ig, "$1").replace(/(\{|\}|\,|\:|\;)\s/ig, "$1"); doc.head.appendChild(style); /** 一直累加的定位層級 */ let zIndex = params.zIndex || 1000; /** * 消息隊列 * @type {Array<HTMLElement>} */ const messageList = []; /** * 獲取指定`item`的定位`top` * @param {HTMLElement=} el */ function getItemTop(el) { let top = 10; for (let i = 0; i < messageList.length; i++) { const item = messageList[i]; if (el && el === item) { break; } top += item.clientHeight + 20; } return top; } /** * 刪除指定列表項 * @param {HTMLElement} el */ function removeItem(el) { for (let i = 0; i < messageList.length; i++) { const item = messageList[i]; if (item === el) { messageList.splice(i, 1); break; } } el.classList.add(className.hide); messageList.forEach(function(item) { item.style.top = `${getItemTop(item)}px`; }); } /** * 顯示一條消息 * @param {string} content 內容 * @param {"info"|"success"|"warning"|"error"} type 消息類型 * @param {number} duration 持續時間,優先級比默認值高 */ function show(content, type = "info", duration) { const el = doc.createElement("div"); el.className = `${className.box} ${type}`; el.style.top = `${getItemTop()}px`; el.style.zIndex = zIndex; el.innerHTML = ` <span class="${className.icon}"></span> <span class="${className.text}">${content}</span> `; zIndex++; messageList.push(el); doc.body.appendChild(el); // 添加動畫監聽事件 function animationEnd() { el.removeEventListener("animationend", animationEnd); setTimeout(removeItem, duration || params.duration || 3000, el); } el.addEventListener("animationend", animationEnd); function transitionEnd() { if (getComputedStyle(el).opacity !== "0") return; el.removeEventListener("transitionend", transitionEnd); el.remove(); } el.addEventListener("transitionend", transitionEnd); } return { show, /** * 普通描述提示 * @param {string} msg */ info(msg) { show(msg, "info"); }, /** * 成功提示 * @param {string} msg */ success(msg) { show(msg, "success"); }, /** * 警告提示 * @param {string} msg */ warning(msg) { show(msg, "warning"); }, /** * 錯誤提示 * @param {string} msg */ error(msg) { show(msg, "error"); } } }
以上就是使用純JavaScript封裝一個消息提示條功能示例詳解的詳細內容,更多關於JavaScript封裝消息提示條的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 前端html+css實現動態生日快樂代碼
- 通過vue.extend實現消息提示彈框的方法記錄
- Vue3實現Message消息組件示例
- JavaScript實現簡易登錄註冊頁面
- 使用vant 自定義彈框全過程