# Teleporting
This component should work with any portal|teleport
solution. We don't ship one as a dependency because it's not a requirement from a wai-aria guidelines standpoint. That being said, I could not recommend enough the usage of one, to escape common rendering gotchas with dialogs — the overflow trap.
Package | When |
---|---|
vue-simple-portal (opens new window) | The lightweight (3kb ) portal package for vue 2. If don't have any other portal solution on your app, this is the way to go |
vue-portal (opens new window) | From the same creator, more robust than the previous and intended for more advanced usecases than simple dialogs. |
if you already have any of the packages mentioned above or are using a custom solution, see the "usage with portal" chapter for instructions.
# Usage with vue-simple-portal
This demo uses vue-simple-portal (opens new window), but it would be more or less the same with any portal
solution
<template>
<portal v-if="open">
<a11y-dialog>
<!-- your implementation like above -->
</a11y-dialog>
</portal>
</template>
<script>
import { Portal } from '@linusborg/vue-simple-portal'
export default {
components: { Portal, A11yDialog }
}
</script>
# Combine with <transition>
When you use a
<transition>
as the root element of the portal and then remove the portal (i.e. with v-if) or set its disabled prop to true, no leave transition will happen. While this is to expected, as the same thing would happen if you removed a div that contains a<transition>
, it often trips people up, which is why it's mentioned here. — vue-simple-portal (opens new window)
if you really need to apply the v-if
to portal, check the example in the link above
But based on the info above, this also works fine:
<template>
<portal>
<!--
[1] note the v-if is applied to transition not portal.
could also be applied to the component itself
-->
<transition name="fade" appear v-if="open">
<a11y-dialog
:open="open"
v-bind="$attrs"
v-on="$listeners"
#default="slotProps"
>
<!-- your implementation -->
</a11y-dialog>
</transition>
</portal>
</template>
# Prevent background scrolling
Before v0.6.x
the plugin exposed a preventBackgroundScrolling
boolean prop that basically toggled overflow:hidden
on body when open/close. It sounds like a reasonable default but:
- it's very very opinionated. Can actually break custom layouts that don't use
body
as their default scroller element. - also it's fairly known that iOS safari doesn't respond to
overflow:hidden
on body so a more powerful js solution is usually required to make it work.
By these reasons i decided to deprecate this default implementation (which are just a few lines that you can add to your custom wrapper anyways), and replaced it with event emits:
<template>
<a11y-dialog
:open="dialogOpen"
@close="dialogOpen = false"
@show="preventScroll(true, $event)"
@hide="preventScroll(false, $event)"
>
<p>This slot content will be rendered wherever the <portal-target> with name 'a11y-dialogs'
is located.</p>
</a11y-dialog>
</template>
export default {
data(){
return {
dialogOpen: false
}
},
/**
* Similar to legacy preventBackgroundScrolling implementation
* @param {Boolean} prevent - add/remove style body
* @param {Boolean} hasSiblings - if it's not the only dialog open
*/
preventScroll(prevent, hasSiblings) {
if (hasSiblings) return
if (prevent) document.body.setProperty('overflow', 'hidden')
else document.body.removeProperty('overflow')
}
}
So when creating your custom wrapper you can add your own preventBackgroundScrolling
prop and apply your own custom implementation using events.