一区二区日本_久久久久久久国产精品_无码国模国产在线观看_久久99深爱久久99精品_亚洲一区二区三区四区五区午夜_日本在线观看一区二区

如何解決 iframe 無(wú)法觸發(fā) clickOutside,iframeclickoutside

前言

在公司的一次小組分享會(huì)上,組長(zhǎng)給我們分享了一個(gè)他在項(xiàng)目中遇到的一個(gè)問(wèn)題。愛(ài)掏網(wǎng) - it200.com在一個(gè)嵌入 iframe 的系統(tǒng)中,當(dāng)我們點(diǎn)擊按鈕展開(kāi) Dropdown 展開(kāi)后,再去點(diǎn)擊 iframe 發(fā)現(xiàn)無(wú)法觸發(fā) Dropdown 的 clickOutside 事件,導(dǎo)致 Dropdown 無(wú)法關(guān)閉。愛(ài)掏網(wǎng) - it200.com

查看在線示例

為什么無(wú)法觸發(fā) clickOutside

目前大多數(shù)的 UI 組件庫(kù),例如 Element、Ant Design、iView 等都是通過(guò)鼠標(biāo)事件來(lái)處理, 下面這段是 iView 中的 clickOutside 代碼,iView 直接給 Document 綁定了 click 事件,當(dāng) click 事件觸發(fā)時(shí)候,判斷點(diǎn)擊目標(biāo)是否包含在綁定元素中,如果不是就執(zhí)行綁定的函數(shù)。愛(ài)掏網(wǎng) - it200.com

bind (el, binding, vnode) {
  function documentHandler (e) {
    if (el.contains(e.target)) {
      return false;
    }
    if (binding.expression) {
      binding.value(e);
    }
  }
  el.__vueClickOutside__ = documentHandler;
  document.addEventListener('click', documentHandler);
}
復(fù)制代碼

但 iframe 中加載的是一個(gè)相對(duì)獨(dú)立的 Document,如果直接在父頁(yè)面中給 Document 綁定 click 事件,點(diǎn)擊 iframe 并不會(huì)觸發(fā)該事件。愛(ài)掏網(wǎng) - it200.com

知道問(wèn)題出現(xiàn)在哪里,接下來(lái)我們來(lái)思考怎么解決?

給 iframe 的 body 元素綁定事件

我們可以通過(guò)一些特殊的方式給 iframe 綁定上事件,但這種做法不優(yōu)雅,而且也是存在問(wèn)題的。愛(ài)掏網(wǎng) - it200.com我們來(lái)想想一下這樣一個(gè)場(chǎng)景,左邊是一個(gè)側(cè)邊欄(導(dǎo)航欄),上面是一個(gè) Header 里面有一些 Dropdown 或是 Select 組件,下面是一個(gè)頁(yè)面區(qū)域。愛(ài)掏網(wǎng) - it200.com

但這些頁(yè)面有的是嵌入 iframe,有些是當(dāng)前系統(tǒng)的頁(yè)面。愛(ài)掏網(wǎng) - it200.com如果使用這種方法,我們?cè)谇袚Q路由的時(shí)候就要不斷的去判斷這個(gè)頁(yè)面是否包含 iframe,然后重新綁定/解綁事件。愛(ài)掏網(wǎng) - it200.com而且如果 iframe 和當(dāng)前系統(tǒng)不是同域(大多數(shù)情況都不是同域的),那么這種做法是無(wú)效的。愛(ài)掏網(wǎng) - it200.com

添加遮罩層

我們可以通過(guò)給 iframe 添加一個(gè)透明遮罩層,點(diǎn)擊 Dropdown 的時(shí)候顯示透明遮罩層,點(diǎn)擊 Dropdown 之外的區(qū)域或遮罩層,就派發(fā) clickOutside 事件并關(guān)閉遮罩層,這樣雖然可以觸發(fā) clickOutside 事件,但存在一個(gè)問(wèn)題,如果用戶點(diǎn)擊的區(qū)域正好是 iframe 頁(yè)面中的某個(gè)按鈕,那么第一次點(diǎn)擊是不會(huì)生效的,這種做法對(duì)于交互不是很友好。愛(ài)掏網(wǎng) - it200.com

監(jiān)聽(tīng) focusin 與 focusout 事件

其實(shí)我們可以換一種思路,為什么一定要用鼠標(biāo)事件來(lái)做這件事呢?focusin 與 focusout 事件就很適合處理當(dāng)前這種情況。愛(ài)掏網(wǎng) - it200.com

當(dāng)我們點(diǎn)擊綁定的元素之外時(shí)就觸發(fā) focusout 事件,這時(shí)我們可以添加一個(gè)定時(shí)器,延時(shí)調(diào)用我們綁定的函數(shù)。愛(ài)掏網(wǎng) - it200.com而當(dāng)我們點(diǎn)擊綁定元素例如 Dropdown 會(huì)觸發(fā) focusin 事件,這時(shí)候我們判斷目標(biāo)是否包含在綁定元素中,如果包含在綁定元素中就清除定時(shí)器。愛(ài)掏網(wǎng) - it200.com

不過(guò)使用 focusin 與 focusout 事件需要解決一個(gè)問(wèn)題,那就是要將綁定的元素變成 focusable 元素,那么怎么將元素變成 focusable 元素呢?我們通過(guò)將元素的 tabindex 屬性置為 -1 , 該元素就變成 focusable 的元素。愛(ài)掏網(wǎng) - it200.com

需要注意的是,元素變成 focusable 元素之后,當(dāng)它獲取焦點(diǎn)的時(shí)候,瀏覽器會(huì)給它加上默認(rèn)的高亮樣式,如果你不需要這種樣式可以將 outline 屬性設(shè)置為 none。愛(ài)掏網(wǎng) - it200.com

不過(guò)這種方法雖然很棒,但是也會(huì)存在一些問(wèn)題,瀏覽器兼容性,下面是 MDN 給出的瀏覽器兼容情況,從圖中可以看出 Firefox 低版本不支持這個(gè)事件,所以你需要去權(quán)衡你的項(xiàng)目是否支持低版本的 Firefox 瀏覽器。愛(ài)掏網(wǎng) - it200.com

使用 focus-outside 庫(kù)

focus-outside?正是為了解決上述問(wèn)題所創(chuàng)建的倉(cāng)庫(kù),代碼不到 200 行。愛(ài)掏網(wǎng) - it200.com使用起來(lái)也非常方便,它只有兩個(gè)方法,bind 與 unbind,不依賴其他第三方庫(kù),并且支持為多個(gè)元素綁定同一個(gè)函數(shù)。愛(ài)掏網(wǎng) - it200.com

為什么要給多個(gè)元素綁定同一個(gè)函數(shù),這么做是為了兼容 Element 與 Ant Design,因?yàn)?Element 與 Ant Design 會(huì)將 Dropdown 插入 body 元素中,它的按鈕和容器是分離的,當(dāng)我們點(diǎn)擊按鈕顯示 Dropdown,當(dāng)我們點(diǎn)擊 Dropdown 區(qū)域,這時(shí)候按鈕會(huì)失去焦點(diǎn)觸發(fā) focusout 事件。愛(ài)掏網(wǎng) - it200.com事實(shí)上我們并不希望這時(shí)關(guān)閉 Dropdown,所以我將它們視為同一個(gè)綁定源。愛(ài)掏網(wǎng) - it200.com

這里說(shuō)明下 Element 與 Ant Design 為什么要將彈出層放在 body 元素中,因?yàn)槿绻苯訉?Dropdown 掛載在父元素下,會(huì)受到父元素樣式的影響。愛(ài)掏網(wǎng) - it200.com比如當(dāng)父元素有 overflow: hidden,Dropdown 就有可能被隱藏掉。愛(ài)掏網(wǎng) - it200.com

簡(jiǎn)單使用

// import { bind, unbidn } from 'focus-outside'
// 建議使用下面這種別名,防止和你的函數(shù)命名沖突了。愛(ài)掏網(wǎng) - it200.com
import { bind: focusBind, unbind: focusUnbind } from 'focus-outside'

// 如果你是使用 CDN 引入的,應(yīng)該這樣使用
// 
// const { bind: focusBind, unbind: focusUnbind } = FocusOutside

const elm = document.querySelector('#dorpdown-button')
// 綁定函數(shù)
focusBind(elm, callback)

function callback () {
  console.log('您點(diǎn)擊了 dropdown 按鈕外面的區(qū)域')
  // 清除綁定
  focusUnbind(elm, callback)
}
復(fù)制代碼

查看在線示例

注意

前面說(shuō)到過(guò)元素變成 focusable 元素后,當(dāng)它獲取焦點(diǎn)瀏覽器會(huì)給它加上高亮樣式,如果你不希望看到和這個(gè)樣式,你需要將這個(gè)元素的 CSS 屬性 outline 設(shè)置為 none。愛(ài)掏網(wǎng) - it200.comfocsout-outside 0.5.0 版本中新增 className 參數(shù),為每個(gè)綁定的元素添加 focus-outside 默認(rèn)類名,你要可以通過(guò)傳遞 className 參數(shù)自定義類名,當(dāng)執(zhí)行 unbind 函數(shù)時(shí)候會(huì)將類名從元素上刪除 。愛(ài)掏網(wǎng) - it200.com



// js
const elm = document.querySelector('#focus-ele')
// 默認(rèn)類名是 focus-outside
focusBind(elm, callback, 'my-focus-name')

// css
// 如果你需要覆蓋所有的默認(rèn)樣式,可以在這段代碼放在全局 CSS 中。愛(ài)掏網(wǎng) - it200.com
.my-focus-name {
  outline: none;
}
復(fù)制代碼

在 Vue 中使用

// outside.js
export default {
  bind (el, binding) {
    focusBind(el, binding.value)
  },

  unbind (el, binding) {
    focusUnbind(el, binding.value)
  }
}

// xx.vue
復(fù)制代碼

查看在線示例

在 Element 中使用


      下拉菜單
    
    黃金糕獅子頭螺螄粉雙皮奶蚵仔煎復(fù)制代碼

查看在線示例

在 Ant Design 中使用

import { Menu, Dropdown, Icon, Button } = antd
import { bind: focusBind, unbind: focusUnbind } = 'focus-outside'

function getItems () {
  return [1,2,3,4].map(item => {
    return {item} st menu item 
  })
}

class MyMenu extends React.Component {
  constructor (props) {
    super(props)
    this.menuElm = null
  }

  render () {
    return ({getItems()})
  }

  componentDidMount () {
    this.menuElm = ReactDOM.findDOMNode(this.refs.menu)
    if (this.menuElm && this.props.outside) focusBind(this.menuElm, this.props.outside)
  }

  componentWillUnmount () {
    if (this.menuElm && this.props.outside) focusUnbind(this.menuElm, this.props.outside)
  }
}

class MyDropdown extends React.Component {
  constructor (props) {
    super(props)
    this.dropdownElm = null
  }

  state = {
    visible: false
  }

  render () {
    const menu = ()
    return (
      
      
    )
  }

  componentDidMount () {
    this.dropdownElm = ReactDOM.findDOMNode(this.refs.divRef)
    if (this.dropdownElm) focusBind(this.dropdownElm, this.handleOutside)
  }

  componentWillUnmount () {
    if (this.dropdownElm) focusUnbind(this.dropdownElm, this.handleOutside)
  }

  handleOutside = () => {
    this.setState({ visible: false })
  }

  handleClick = () => {
    this.setState({ visible: !this.state.visible })
  }
}

ReactDOM.render(
  ,
  document.getElementById('container')
)
復(fù)制代碼

查看在線示例

總結(jié)

iframe 元素?zé)o法觸發(fā)鼠標(biāo)事件,如果在嵌入 iframe 的系統(tǒng)中觸發(fā) clickOutside, 更好的做法是使用 focusin 與 focusout 事件,將 HTML 屬性 tabindex 設(shè)置為 -1 可以將元素變成 focusable 元素。愛(ài)掏網(wǎng) - it200.com瀏覽器會(huì)給 focusable 元素加上默認(rèn)的高亮樣式,如果你不需要這種樣式,可以將 CSS 屬性 outline 設(shè)置為 none。愛(ài)掏網(wǎng) - it200.com

相關(guān)鏈接

  • MDN focusin
  • MDN focusout
  • focus-outside
  • 說(shuō)說(shuō) tabindex 的那些事兒
  • HTML tabindex 屬性與 web 網(wǎng)頁(yè)鍵盤(pán)無(wú)障礙訪問(wèn)




原文發(fā)布時(shí)間為:2024年07月02日

作者:掘金

本文來(lái)源:掘金?如需轉(zhuǎn)載請(qǐng)聯(lián)系原作者
聲明:所有內(nèi)容來(lái)自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。
發(fā)表評(píng)論
更多 網(wǎng)友評(píng)論0 條評(píng)論)
暫無(wú)評(píng)論

返回頂部

主站蜘蛛池模板: 日韩一区av | 成人午夜黄色 | 日韩视频在线观看中文字幕 | 9久9久9久女女女九九九一九 | 国产精品一区二区av | 日韩欧美在线精品 | 欧美精品久久一区 | 久久精品日产第一区二区三区 | 久久不卡视频 | 日韩视频一区二区 | www.99热.com| 成人夜晚看av | 免费久久99精品国产婷婷六月 | 欧美一区二区三区在线看 | 国产视频在线观看一区二区三区 | 丁香婷婷综合激情五月色 | 国产精品久久久久久久久免费相片 | 国产成人综合一区二区三区 | 人人爱干| 青青久久| 国产高清久久久 | 久久999| 欧美一区二区 | 色综合99 | 国产精品久久国产精品 | av片在线播放 | 日韩视频在线一区二区 | 黄色av大片 | 日韩成人久久 | 免费欧美视频 | 国产一级电影在线观看 | 人人澡视频 | 91操操操| 仙人掌旅馆在线观看 | 国产精品一区二区av | 伊人网站视频 | 日本一区二区影视 | 性高湖久久久久久久久3小时 | 亚洲免费观看视频网站 | 精品亚洲一区二区 | 在线观看黄免费 |