quiz
quiz copied to clipboard
DOM基础测试42
本期小测关于浮层更好的交互体验细节的处理。
大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。
```js // 你的JS代码写在这里 ```
其他 本期小测没有直播,也没有打分,但是会反馈要点。
#overlay:target{
display:block;
}
#overlay{
display:none;
position:absolute;
top:0;
bottom:0;
left:0;
right:0;
background:rgba(0,0,0,.1);
}
<div id="overlay" hidden></div>
<button id="showButton">按钮</button>
document.querySelector('#showButton')
.addEventListener('click',function(){
location.href = "#overlay";
})
document.querySelector("#overlay")
.addEventListener('click',function(){
location.href = ""
})
#overlay:target{
display:block;
}
可以把HTML结构换一下,省去js操作
<a id="overlay" href="#" hidden>我是浮层</a>
<a href="#overlay">我是按钮</a>
get
和楼上几位大佬比感觉我的代码多好多啊……
window.onload = function() {
let overlay = document.getElementById('overlay')
const btn = document.getElementById('showButton')
const url = location.href
const arrUrl = url.split('?')
if (arrUrl[1] === 'overlay') {
overlay.hidden = false
} else {
overlay.hidden = true
}
btn.addEventListener('click', function() {
if (arrUrl[1] === undefined) {
location.href = '?overlay'
}
})
overlay.addEventListener('click', function() {
if (arrUrl[1] === 'overlay') {
location.href = arrUrl[0]
}
})
}
function showOrHide(){
var hash = window.location.hash;
if (hash === '#showOverlay') {
overlay.removeAttribute('hidden');
} else {
overlay.setAttribute('hidden', '');
}
}
showOrHide();
showButton.addEventListener('click', function(){
location.href = '#showOverlay';
});
overlay.addEventListener('click', function(){
location.href = '#hiddenOverlay';
});
window.addEventListener("hashchange", function(){
showOrHide();
});
记录每次点击的值,希望没有理解错题意
const param = new URLSearchParams(location.search)
if (param.get('overlayShow')) {
overlay.hidden = param.get('overlayShow') === 'true'
}
console.log(typeof param.get('overlayShow'))
showButton.addEventListener('click', function () {
overlay.hidden = !overlay.hidden
param.set('overlayShow', overlay.hidden)
console.log(param + '')
history.replaceState(null, null, '?' + param)
})
<div id="overlay" hidden></div>
<button id="showButton">show</button>
#overlay{
width: 100px;
height: 100px;
background-color: red;
}
{
let showBtn = document.querySelector("#showButton");
let div = document.querySelector("#overlay");
showBtn.addEventListener("click", () => {
div.removeAttribute("hidden")
window.loacation.href = "#overlay"
})
div.addEventListener("click", function(){
this.setAttribute("hidden", true)
window.loacation.href = ""
})
}
#overlay:target {
display: block;
}
#overlay {
height: 100vh;
background-color: rgba(0, 0, 0, 0.2);
}
let overlay = document.getElementById("overlay");
let showButton = document.getElementById("showButton");
showButton.addEventListener("click", () => {
location.hash = "#overlay";
});
overlay.addEventListener("click", () => {
location.hash = "";
});
var btn = document.getElementById('showButton')
var overlay = document.getElementById('overlay')
btn.addEventListener('click',function(){
overlay.removeAttribute('hidden')
})
var btn = document.getElementById('showButton')
var overlay = document.getElementById('overlay')
overlay.addEventListener('click',function(){
this.setAttribute('hidden',true)
})
function changeUrl(){
var overlay = document.getElementById('overlay')
var searchParams = (new URL(document.location)).searchParams
if(overlay.hidden){
overlay.removeAttribute('hidden')
searchParams.set('overlay',true)
}else{
overlay.setAttribute('hidden',true)
searchParams.set('overlay',false)
}
history.replaceState({},'',[location.origin,location.pathname,'?',searchParams.toString()].join(''))
}
var btn = document.getElementById('showButton')
btn.addEventListener('click',function(){
changeUrl()
})
var overlay = document.getElementById('overlay')
overlay.addEventListener('click',function(){
changeUrl()
})
var searchParams = (new URL(document.location)).searchParams
if(searchParams.get("overlay") == 'true'){
overlay.removeAttribute('hidden')
}
考虑到单页应用形如 www.foo.com/#/article/123?overlay=true&lang=en
懒得写事件绑定和元素获取
window.onload=()=>{
overlay.hidden=!/#[^?]?.*\boverlay=true/.test(location.hash)
}
showButton.onClick=()=>{
var p=new URLSearchParams(location.hash.replace(/[^\?]+\?/,''));
p.set('overlay',overlay.hidden=!overlay.hidden);
location.hash=location.hash.split('?',1)[0]+'?'+p.toString();
}
<style>
#overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
color: #ffffff;
}
#overlay[hidden] {
display: none;
}
#overlay:target {
display: block;
}
</style>
<div id="overlay" hidden onclick="window.location.hash='#';">弹窗</div>
<button id="showButton" onclick="window.location.hash='overlay';">显示弹窗</button>
<a href="#overlay" title="显示弹窗">显示弹窗</a>
#overlay {
position: fixed;
left: 0;
top: 0;
display: none;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
}
<button id="showButton">显示浮层</button>
<div id="overlay"></div>
let overlay = document.getElementById('overlay')
let showButton = document.getElementById('showButton')
// 显示浮层,overlayId是针对于当前页面可能会出现多个浮层的情况
function showOverlay (overlayId) {
let url = new URL(location.href)
let searchObj = new URLSearchParams(url.search)
overlay.style.display = 'block'
searchObj.set('overlay', overlayId) // 设置search参数
url.search = searchObj
history.replaceState({
url: url.href,
title: document.title
}, document.title, url.href) // 使用history对象的state可以避免直接修改url时的刷新
}
// 隐藏浮层
function hiddenOverlay () {
let url = new URL(location.href)
let searchObj = new URLSearchParams(url.search)
overlay.style.display = 'none'
searchObj.delete('overlay') // 清除search参数
url.search = searchObj
history.replaceState({
url: url.href,
title: document.title
}, document.title, url.href)
}
// 检测url的search参数是否出现了显示浮层的参数
function checkOverlay () {
let searchObj = new URLSearchParams(location.search)
let overlayId = searchObj.get('overlay')
if (overlayId) {
showOverlay(overlayId)
} else {
hiddenOverlay()
}
}
showButton.addEventListener('click', e => {
showOverlay(1)
})
overlay.addEventListener('click', e => {
hiddenOverlay()
})
// 监听浏览器历史记录改变
window.addEventListener('popstate', e => {
checkOverlay()
})
checkOverlay()
document.querySelector('#showButton').addEventListener('click', function() {
document.querySelector('#overlay').removeAttribute('hidden');
});
document.querySelector('#overlay').addEventListener('click', function() {
document.querySelector('#overlay').setAttribute('hidden','hidden');
});
document.querySelector('#showButton').addEventListener('click', function() {
document.querySelector('#overlay').removeAttribute('hidden');
document.location.hash = '#overlay';
});
if (document.location.hash === '#overlay') {
document.querySelector('#overlay').removeAttribute('hidden');
}
#overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.3);
}
// 1. 点击按钮浮层显示
document.getElementById('showButton').addEventListener('click', e => {
// location.hash = 'overlay'
location.replace('#?overlay=true')
// document.querySelector('#overlay').hidden = false
})
// 2. 点击浮层浮层隐藏
document.getElementById('overlay').addEventListener('click', e => {
// location.hash = ''
location.replace('#?overlay=false')
// document.querySelector('#overlay').hidden = true
})
// 3.
// 监听 hashchange 变化
function handelHashChange () {
document.getElementById('overlay').hidden = !/overlay=true/.test(location.hash)
}
window.addEventListener('hashchange', handelHashChange)
function __main() {
handelHashChange()
}
__main()
// 页面初始化
const overlayDom = document.querySelector('#overlay')
const btnDom = document.querySelector('#showButton')
setAttributeByUrl()
function setAttributeByUrl(){
const url = new URL(window.location);
const flag = url.searchParams.get('overlay') || '';
flag ? overlayDom.setAttribute('hidden', '') : overlayDom.removeAttribute('hidden')
}
functipn removeQueryString(){
const url = new URL(window.location);
url.searchParams.delete('overlay')
location.href = new URL(url);
}
functipn addQueryString(){
const url = new URL(window.location);
url.searchParams.set('overlay','x')
location.href = new URL(url);
}
functipn toggleQueryString(){
const url = new URL(window.location);
const flag = url.searchParams.has('overlay')
flag ? removeQueryString() : addQueryString()
}
// dom 事件绑定
overlayDom.addEventListener('click', ()=>{
toggleQueryString()
setAttributeByUrl()
} )
btnDom.addEventListener('click', ()=>{
toggleQueryString()
setAttributeByUrl()
})
本期小测基本效果实现并不难,但是要想真正体验良好,要花的功夫可不少。
-
<a>按钮+ :target伪类匹配非常适合兜底——JS挂掉时候交互功能依然OK,但在实际开发的时候这种纯CSS方法肯定是不行的。因为会有其他体验问题。 -
无论是
<a>元素 href触发hash变化,还是JSlocation.hash = 'overlay',都不适合实际开发,因为锚点定位会触发滚动和重定位等一堆意料之外的问题,例如我们希望浮层动画效果进入,由于锚点定位,完全没戏。所以,需要JS额外处理下,把hash改成(例如)#&overlay。既能标记元素,又不至于触发锚点定位。 -
很多人直接location.has不断改变锚点值,这就有个体验问题,当我们的浮层不断显示与隐藏的时候,就会塞入太多的历史记录,导致我们返回的时候,一直在一个页面不断循环。好的体验应该是第一次显示的时候push访问记录,隐藏还原之前状态。
location.replace('#xxx')可以一定程度优化这个问题,设定一个标志量,第一次push,之后replace。当然,最好方法是浮层隐藏的时候history.go(-1),需要配合下面的监听绑定。 -
我们可以使用
location.replace替换hash,也可以使用history.replaceState()替换URL,无论哪种方法,都一定要监听URL的变化,分别是hashchange事件和popstate事件。如果不监听,你URL变化时候,浮层是不会跟着切换的。 -
改变query查询也是种不错方法,因为可以被后端直接识别,直出控制会很方便。例如
?overlay=1。
window.onload = () => {
let url = window.location
let buttonDom = document.querySelector('#showButton')
let divDom = document.querySelector('#overlay')
showButton.addEventListener('click', function () {
let state = divDom.getAttribute('hidden')
let href = url.origin + url.pathname
if (state === null) {
divDom.setAttribute('hidden', 'hidden')
url.replace(href)
} else {
divDom.removeAttribute('hidden')
url.replace(href + '#' + divDom.getAttribute('id'))
}
})
let domHash = url.hash
if (domHash !== '') {
domHash = domHash.replace('#', '');
document.querySelector('#' + domHash).removeAttribute('hidden')
}
}
let param = new URLSearchParams(window.location.search)
let dialog = document.querySelector('#overlay')
let showButton = document.querySelector('#showButton')
let urlFlag = param.get('show')
if(urlFlag) {
dialog.style.display = 'block'
}
showButton.addEventListener('click', () => {
let param = new URLSearchParams(window.location.search)
if(urlFlag) {
let param = new URLSearchParams(window.location.search)
param.delete('show')
window.location.search = param.toString()
dialog.style.display = ''
urlFlag = false
} else {
let param = new URLSearchParams(window.location.search)
param.set('show', true)
window.location.search = param.toString()
dialog.style.display = 'block'
urlFlag = true
}
})
[hidden] {display: none;}
.overlay {
padding: 1em;
background: #fff;
border: 1px solid #ccc;
}
<button id="showButton1">显示浮层1</button>
<button id="showButton2">显示浮层2</button>
<div class="overlay" id="overlay1" hidden>
我是 <strong>浮层1</strong>
</div>
<div class="overlay" id="overlay2" hidden>
我是 <strong>浮层2</strong>
</div>
class Overlay {
constructor(id) {
this.el = document.getElementById(id);
if (!this.el) return;
this.toggle(Overlay.urlState(id));
// 点击自身关闭浮层
this.el.addEventListener('click', () => this.close());
}
static urlState(key, action) {
const { href, hash } = location;
const openFlag = `#${key}=open`;
const stateRegExp = new RegExp(openFlag, 'gi');
if (typeof action === 'undefined') {
return stateRegExp.test(hash)
}
let url = href.replace(stateRegExp, '') +
(action === 'ADD' ? openFlag : '');
history.replaceState(null, null, url);
}
toggle(isOpen) {
let action = isOpen ? 'ADD' : 'DEL';
this.el.hidden = !isOpen;
Overlay.urlState(this.el.id, action);
}
open() {
this.toggle(true)
}
close() {
this.toggle(false)
}
}
const overlay1 = new Overlay('#overlay1');
const overlay2 = new Overlay('#overlay2');
const $$ = el => document.querySelector(el);
// 点击按钮显示对应浮层
$$('#showButton1').addEventListener('click', () => overlay1.open());
$$('#showButton2').addEventListener('click', () => overlay2.open());