quiz icon indicating copy to clipboard operation
quiz copied to clipboard

DOM基础测试42

Open zhangxinxu opened this issue 5 years ago • 18 comments

本期小测关于浮层更好的交互体验细节的处理。

小测图片访问

大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。

```js
// 你的JS代码写在这里
 ```

其他 本期小测没有直播,也没有打分,但是会反馈要点。

zhangxinxu avatar Jan 08 '20 11:01 zhangxinxu

#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 = ""
})

liyongleihf2006 avatar Jan 08 '20 12:01 liyongleihf2006

#overlay:target{
    display:block;
}

可以把HTML结构换一下,省去js操作

<a id="overlay" href="#" hidden>我是浮层</a>

<a href="#overlay">我是按钮</a>

XboxYan avatar Jan 08 '20 12:01 XboxYan

get

kevlin-sean avatar Jan 08 '20 13:01 kevlin-sean

和楼上几位大佬比感觉我的代码多好多啊……

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]
    }
  })
}

LuckyRabbitFeet avatar Jan 08 '20 14:01 LuckyRabbitFeet

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();
});

smileyby avatar Jan 08 '20 15:01 smileyby

记录每次点击的值,希望没有理解错题意

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)
})

livetune avatar Jan 08 '20 16:01 livetune

    <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 = ""
            })
        }

guqianfeng avatar Jan 09 '20 01:01 guqianfeng

#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 = "";
});

asyncguo avatar Jan 09 '20 02:01 asyncguo

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')
}

kuikuiGe avatar Jan 10 '20 09:01 kuikuiGe

考虑到单页应用形如 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();
}

Seasonley avatar Jan 10 '20 16:01 Seasonley

<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>

ziven27 avatar Jan 11 '20 05:01 ziven27

#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()

xxf1996 avatar Jan 11 '20 09:01 xxf1996

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');
}

NeilChen4698 avatar Jan 11 '20 13:01 NeilChen4698

#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()

zy017 avatar Jan 14 '20 07:01 zy017

// 页面初始化
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()
})

Valar103769 avatar Jan 14 '20 08:01 Valar103769

本期小测基本效果实现并不难,但是要想真正体验良好,要花的功夫可不少。

  1. <a>按钮+ :target伪类匹配非常适合兜底——JS挂掉时候交互功能依然OK,但在实际开发的时候这种纯CSS方法肯定是不行的。因为会有其他体验问题。

  2. 无论是 <a> 元素 href触发hash变化,还是JS location.hash = 'overlay',都不适合实际开发,因为锚点定位会触发滚动和重定位等一堆意料之外的问题,例如我们希望浮层动画效果进入,由于锚点定位,完全没戏。所以,需要JS额外处理下,把hash改成(例如)#&overlay。既能标记元素,又不至于触发锚点定位。

  3. 很多人直接location.has不断改变锚点值,这就有个体验问题,当我们的浮层不断显示与隐藏的时候,就会塞入太多的历史记录,导致我们返回的时候,一直在一个页面不断循环。好的体验应该是第一次显示的时候push访问记录,隐藏还原之前状态。location.replace('#xxx')可以一定程度优化这个问题,设定一个标志量,第一次push,之后replace。当然,最好方法是浮层隐藏的时候history.go(-1),需要配合下面的监听绑定。

  4. 我们可以使用location.replace替换hash,也可以使用history.replaceState()替换URL,无论哪种方法,都一定要监听URL的变化,分别是hashchange事件和popstate事件。如果不监听,你URL变化时候,浮层是不会跟着切换的。

  5. 改变query查询也是种不错方法,因为可以被后端直接识别,直出控制会很方便。例如?overlay=1

zhangxinxu avatar Jan 15 '20 10:01 zhangxinxu

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')
    }
}

rayj1993 avatar Jan 17 '20 02:01 rayj1993

  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
    }
  })

zer0fire avatar Jan 17 '20 07:01 zer0fire

[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());

wingmeng avatar Mar 16 '20 11:03 wingmeng