<button class="tooltip js-tooltip" title="Begriffserklärung ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor.">Button</button>
//- Render tooltip
#{tag || 'button'}.tooltip.js-tooltip(title=tooltip)&attributes(attr) #{text}
{
"text": "Button",
"tooltip": "Begriffserklärung ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor."
}
import { createPopper } from '@popperjs/core';
import h from 'hyperscript';
import randomId from '../../../javascripts/utils/random-id';
import icon from '../../../javascripts/utils/icon';
export default class Tooltip {
constructor($el, $tooltip) {
this.$el = $el;
this.$tooltip = $tooltip;
this.popper = null;
this.config = {
closeButtonClass: 'button.tooltip__close',
openOnHover: true,
closeOnFocusOut: true,
placement: this.$el.dataset.placement || 'left',
orientations: [
'right',
'right-start',
'bottom',
'left',
'left-start',
'top',
],
};
this.$close = $tooltip.querySelector(this.config.closeButtonClass);
// Bind events
if (this.config.openOnHover) {
this.onMouseenterBinded = this.onMouseenter.bind(this);
this.onMouseleaveBinded = this.onMouseleave.bind(this);
}
this.onClickBinded = this.onClick.bind(this);
this.onOutsideClickBinded = this.onOutsideClick.bind(this);
this.onTooltipFocusoutBinded = this.onTooltipFocusout.bind(this);
this.onCloseClickBinded = this.onCloseClick.bind(this);
this.$el.addEventListener('mouseenter', this.onMouseenterBinded);
this.$el.addEventListener('click', this.onClickBinded);
this.$close.addEventListener('click', this.onCloseClickBinded);
// Initialize popper
this.popper = createPopper(
this.$el,
this.$tooltip,
{
placement: this.config.placement,
modifiers: [
{
name: 'flip',
options: {
fallbackPlacements: this.config.orientations,
},
},
{
name: 'arrow',
options: {
element: this.$tooltip.querySelector('.tooltip__arrow'),
},
},
{
name: 'offset',
options: {
offset: [0, 8],
},
},
{
name: 'preventOverflow',
options: {
padding: 5,
},
},
],
},
);
}
onMouseenter() {
this.$el.addEventListener('mouseleave', this.onMouseleaveBinded);
this.show();
}
onMouseleave() {
this.$el.removeEventListener('mouseleave', this.onMouseleaveBinded);
this.hide();
}
onClick(event) {
event.preventDefault();
if (this.config.openOnHover) {
this.$el.removeEventListener('mouseleave', this.onMouseleaveBinded);
this.$el.removeEventListener('mouseenter', this.onMouseenterBinded);
}
this.$close.hidden = false;
this.show();
document.addEventListener('click', this.onOutsideClickBinded);
if (this.config.closeOnFocusOut) {
this.$tooltip.addEventListener('focusout', this.onTooltipFocusoutBinded);
}
if (this.config.onClick) {
this.config.onClick();
}
}
onOutsideClick(event) {
const $target = event.target;
if (this.isInside($target)) {
return;
}
this.hideAfterClick();
}
onTooltipFocusout(event) {
const $target = event.relatedTarget;
if (this.isInside($target)) {
return;
}
this.hideAfterClick();
}
onCloseClick() {
this.hideAfterClick();
this.$el.focus();
}
show() {
this.$tooltip.setAttribute('aria-hidden', 'false');
this.popper.update();
}
hide() {
this.$tooltip.setAttribute('aria-hidden', 'true');
this.$el.focus();
}
hideAfterClick() {
this.hide();
this.$close.hidden = true;
document.removeEventListener('click', this.onOutsideClickBinded);
this.$tooltip.removeEventListener('focusout', this.onTooltipFocusoutBinded);
if (this.config.openOnHover) {
this.$el.addEventListener('mouseenter', this.onMouseenterBinded);
}
}
isInside($target) {
return (
this.$tooltip.contains($target)
|| this.$tooltip === $target
|| this.$el.contains($target)
|| this.$el === $target
);
}
}
export const attachTooltip = ($el) => {
const text = $el.getAttribute('title');
const id = `tooltip-${randomId()}`;
if (!text) {
return null;
}
$el.removeAttribute('title');
if (!$el.hasAttribute('aria-label')) {
$el.setAttribute('aria-label', 'Begriffserklärung anzeigen');
}
// Templates
const $text = h('p.tooltip__text', { attrs: { tabindex: '0' } }, text);
const $arrow = h('.tooltip__arrow', { attrs: { 'x-arrow': '' } });
const $close = h(
'button.tooltip__close',
{
type: 'button',
title: 'Schließen',
hidden: true,
},
icon({
icon: 'close',
}),
);
const $tooltip = h(
`.tooltip__popup#${id}`,
{
attrs: {
'aria-hidden': 'true',
},
},
$text,
$arrow,
$close,
);
$el.parentNode.insertBefore($tooltip, $el.nextSibling);
return new Tooltip($el, $tooltip, {
onClick: () => {
$text.focus();
},
});
};
document
.querySelectorAll('.js-tooltip')
.forEach(attachTooltip);
.tooltip {
display: inline-block;
.preview-frame & {
margin-top: 10rem;
}
}
.tooltip__popup {
background-color: #000;
box-shadow: 1px -1px 5px 0 rgba(0, 0, 0, 0.7);
color: #fff;
font-size: 1.4rem;
margin: 0.2rem 0.8rem;
max-width: 26rem;
padding: 1.4rem 1.6rem;
z-index: z('tooltip');
&[aria-hidden='true'] {
display: none;
}
&[aria-hidden='false'] {
display: block;
}
.tooltip__text {
margin-bottom: 0;
}
@include mq($until: m) {
padding-right: 2.4rem;
}
}
.tooltip__arrow {
border-style: solid;
border-width: 0.8rem;
display: block;
height: 0;
position: absolute;
width: 0;
.tooltip__popup[data-popper-placement='top'] &,
.tooltip__popup[data-popper-placement='top-end'] & {
border-bottom: 0;
border-left-color: transparent;
border-right-color: transparent;
border-top-color: #000;
bottom: -0.8rem;
}
.tooltip__popup[data-popper-placement='right'] &,
.tooltip__popup[data-popper-placement='right-end'] & {
border-bottom-color: transparent;
border-left: 0;
border-right-color: #000;
border-top-color: transparent;
left: -0.8rem;
}
.tooltip__popup[data-popper-placement='bottom'] &,
.tooltip__popup[data-popper-placement='bottom-end'] & {
border-bottom-color: #000;
border-left-color: transparent;
border-right-color: transparent;
border-top: 0;
top: -0.8rem;
}
.tooltip__popup[data-popper-placement='left'] &,
.tooltip__popup[data-popper-placement='left-start'] &,
.tooltip__popup[data-popper-placement='left-end'] & {
border-bottom-color: transparent;
border-left-color: #000;
border-right: 0;
border-top-color: transparent;
right: -0.8rem;
}
}
.tooltip__text {
line-height: 1.4;
}
.tooltip__close {
cursor: pointer;
height: 2.4rem;
padding: 0;
position: absolute;
right: 0;
top: 0;
width: 2.4rem;
}
There are no notes for this item.