Pop-ups: ze maken een heropleving!

Het doel van het Open UI-initiatief is om het voor ontwikkelaars gemakkelijker te maken geweldige gebruikerservaringen te creëren. Om dit te doen, proberen we de meer problematische patronen aan te pakken waarmee ontwikkelaars worden geconfronteerd. We kunnen dit doen door betere, in het platform ingebouwde API's en componenten te bieden.

Een voorbeeld van zo'n probleemgebied zijn pop-ups, die in Open UI worden beschreven als "Popovers".

Popovers hebben lange tijd een nogal polariserende reputatie gehad. Dit komt gedeeltelijk door de manier waarop ze worden gebouwd en ingezet. Het is geen eenvoudig patroon om goed te bouwen, maar ze kunnen veel waarde opleveren door gebruikers naar bepaalde dingen te leiden of hen bewust te maken van de inhoud op uw site, vooral als ze op een smaakvolle manier worden gebruikt.

Er zijn vaak twee grote problemen bij het bouwen van popovers:

  • Hoe u ervoor kunt zorgen dat deze op een geschikte plaats boven de rest van uw inhoud wordt geplaatst.
  • Hoe u het toegankelijk kunt maken (toetsenbordvriendelijk, focusbaar, enzovoort).

De ingebouwde Popover API heeft verschillende doelen , allemaal met hetzelfde overkoepelende doel: het voor ontwikkelaars gemakkelijk maken om dit patroon te bouwen. Opvallende van die doelen zijn:

  • Maak het gemakkelijk om een ​​element en zijn nakomelingen boven de rest van het document weer te geven.
  • Maak het toegankelijk.
  • Er is geen JavaScript vereist voor de meest voorkomende gedragingen (licht negeren, singleton, stapelen, enzovoort).

U kunt de volledige specificaties voor pop-ups bekijken op de OpenUI-site .

Browsercompatibiliteit

Waar kun je nu de ingebouwde Popover API gebruiken? Op het moment van schrijven wordt het ondersteund in Chrome Canary achter de vlag 'Experimentele webplatformfuncties'.

Om die vlag in te schakelen, opent u Chrome Canary en gaat u naar chrome://flags . Schakel vervolgens de vlag 'Experimentele webplatformfuncties' in.

Er is ook een Origin Trial voor ontwikkelaars die dit graag willen testen in een productieomgeving.

Ten slotte is er een polyfill in ontwikkeling voor de API. Zorg ervoor dat je de repository bekijkt op github.com/oddbird/popup-polyfill .

U kunt controleren op pop-upondersteuning met:

const supported = HTMLElement.prototype.hasOwnProperty("popover");

Huidige oplossingen

Wat kunt u momenteel doen om uw inhoud boven alles te promoten? Als dit in uw browser wordt ondersteund, kunt u het HTML Dialog-element gebruiken. Je zou het in "Modale" vorm moeten gebruiken. En hiervoor is JavaScript nodig.

Dialog.showModal();

Er zijn enkele toegankelijkheidsoverwegingen. Het wordt aanbevolen om bijvoorbeeld a11y-dialoog te gebruiken als het gaat om gebruikers van Safari onder versie 15.4.

U kunt ook een van de vele op popovers, waarschuwingen of tooltip gebaseerde bibliotheken gebruiken. Veel van deze hebben de neiging om op een vergelijkbare manier te werken.

  • Voeg een container toe aan de body om popovers weer te geven.
  • Stijl het zo dat het boven al het andere uitsteekt.
  • Maak een element en voeg het toe aan de container om een ​​popover weer te geven.
  • Verberg het door het popover-element uit de DOM te verwijderen.

Dit vereist een extra afhankelijkheid en meer beslissingen voor ontwikkelaars. Er is ook onderzoek nodig om een ​​aanbod te vinden dat alles biedt wat u nodig heeft. De Popover API is bedoeld om in veel scenario's te voorzien, inclusief tooltips. Het doel is om al deze veelvoorkomende scenario's te dekken, zodat ontwikkelaars niet nog een beslissing hoeven te nemen, zodat ze zich kunnen concentreren op het bouwen van hun ervaringen.

Je eerste pop-up

Dit is alles wat je nodig hebt.

<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>

Maar wat gebeurt hier?

  • U hoeft het popover-element niet in een container of iets dergelijks te plaatsen; het is standaard verborgen.
  • U hoeft geen JavaScript te schrijven om het te laten verschijnen. Dat wordt afgehandeld door het popovertoggletarget attribuut.
  • Wanneer het verschijnt, wordt het gepromoveerd naar de bovenste laag. Dat betekent dat het boven het document in de viewport wordt gepromoot. U hoeft z-index niet te beheren of u zorgen te maken over waar uw popover zich in de DOM bevindt. Het zou diep in de DOM kunnen zijn genest, met afgekapte voorouders. Ook kun je via DevTools zien welke elementen zich momenteel in de bovenste laag bevinden. Bekijk dit artikel voor meer informatie over de bovenste laag.

GIF van DevTools-toplaagondersteuning wordt gedemonstreerd

  • U krijgt "Light Dismiss" uit de doos. Daarmee bedoelen we dat u de popover kunt sluiten met een sluitsignaal, zoals door buiten de popover te klikken, met uw toetsenbord naar een ander element te navigeren of op de Esc- toets te drukken. Open het opnieuw en probeer het uit!

Wat krijg je nog meer met popover? Laten we het voorbeeld verder nemen. Overweeg deze demo met wat inhoud op de pagina.

Die zwevende actieknop heeft een vaste positionering met een hoge z-index .

.fab {
  position: fixed;
  z-index: 99999;
}

De popover-inhoud is genest in de DOM, maar wanneer u de popover opent, wordt deze boven dat vaste-positie-element gepromoveerd. U hoeft geen stijlen in te stellen.

Het zal je misschien ook opvallen dat de popover nu een ::backdrop pseudo-element heeft. Alle elementen die zich in de bovenste laag bevinden, krijgen een stylebaar ::backdrop pseudo-element. In dit voorbeeld wordt ::backdrop opgemaakt met een gereduceerde alfa-achtergrondkleur en een achtergrondfilter, dat de onderliggende inhoud vervaagt.

Een pop-over stylen

Laten we onze aandacht richten op het stylen van de popover. Standaard heeft een popover een vaste positie en enige toegepaste opvulling. Het heeft ook display: none . U kunt dit overschrijven om een ​​popover weer te geven. Maar dat zou het niet naar de bovenste laag bevorderen.

[popover] { display: block; }

Ongeacht hoe u uw popover promoot, zodra u een popover naar de bovenste laag promoveert, moet u deze mogelijk indelen of positioneren. Je kunt je niet op de bovenste laag richten en zoiets doen

:open {
  display: grid;
  place-items: center;
}

Standaard wordt een popover in het midden van de viewport geplaatst met behulp van margin: auto . Maar in sommige gevallen wilt u misschien expliciet zijn over de positionering. Bijvoorbeeld:

[popover] {
  top: 50%;
  left: 50%;
  translate: -50%;
}

Als u inhoud in uw popover wilt opmaken met behulp van een CSS-raster of flexbox, kan het verstandig zijn om dit in een element te verpakken. Anders moet u een aparte regel declareren die de display verandert zodra de popover zich in de bovenste laag bevindt. Als u dit standaard instelt, wordt het standaard weergegeven met overheersende display: none .

[popover]:open {
 display: flex;
}

Als je die demo hebt geprobeerd, zul je merken dat de popover nu in en uit gaat. U kunt popovers in- en uitschakelen met behulp van de :open pseudo-selector. De :open pseudo-selector matcht popovers die worden weergegeven (en dus in de bovenste laag).

In dit voorbeeld wordt een aangepaste eigenschap gebruikt om de overgang aan te sturen. En u kunt ook een overgang toepassen op de ::backdrop van de popover.

[popover] {
  --hide: 1;
  transition: transform 0.2s;
  transform: translateY(calc(var(--hide) * -100vh))
            scale(calc(1 - var(--hide)));
}

[popover]::backdrop {
  transition: opacity 0.2s;
  opacity: calc(1 - var(--hide, 1));
}


[popover]:open::backdrop  {
  --hide: 0;
}

Een tip hierbij is om overgangen en animaties te groeperen onder een mediaquery voor beweging. Dit kan ook helpen om uw timing aan te houden. Dit komt omdat u geen waarden kunt delen tussen de popover en de ::backdrop via een aangepaste eigenschap.

@media(prefers-reduced-motion: no-preference) {
  [popover] { transition: transform 0.2s; }
  [popover]::backdrop { transition: opacity 0.2s; }
}

Tot nu toe heb je het gebruik van popovertoggletarget gezien om een ​​popover weer te geven. Om het te negeren, gebruiken we 'Licht negeren'. Maar u krijgt ook de kenmerken popovershowtarget en popoverhidetarget die u kunt gebruiken. Laten we een knop toevoegen aan een popover die deze verbergt en de schakelknop wijzigen om popovershowtarget te gebruiken.

<div id="code-popover" popover>
  <button popoverhidetarget="code-popover">Hide Code</button>
</div>
<button popovershowtarget="code-popover">Reveal Code</button>

Zoals eerder vermeld omvat de Popover API meer dan alleen ons historische begrip van pop-ups. U kunt bouwen voor alle soorten scenario's, zoals meldingen, menu's, tooltips enz.

Sommige van deze scenario's hebben verschillende interactiepatronen nodig. Interacties zoals zweven. Er is geëxperimenteerd met het gebruik van een popoverhovertarget kenmerk, maar dit is momenteel niet geïmplementeerd.

<div popoverhovertarget="hover-popover">Hover for Code</div>

Het idee is dat je een element beweegt om het doel te tonen. Dit gedrag kan worden geconfigureerd via CSS-eigenschappen. Deze CSS-eigenschappen definiëren het tijdsvenster voor het aan- en uitschakelen van een element waarop een popover reageert. Het standaardgedrag waarmee werd geëxperimenteerd, had een popover-show na een expliciete 0.5s van :hover . Dan zou er een lichte ontkenning nodig zijn of de opening van een andere popover om te ontslaan (meer hierover binnenkort). Dit kwam doordat de popover-verbergduur was ingesteld op Infinity .

In de tussentijd kunt u JavaScript gebruiken om die functionaliteit te polyfillen.

let hoverTimer;
const HOVER_TRIGGERS = document.querySelectorAll("[popoverhovertarget]");
const tearDown = () => {
  if (hoverTimer) clearTimeout(hoverTimer);
};
HOVER_TRIGGERS.forEach((trigger) => {
  const popover = document.querySelector(
    `#${trigger.getAttribute("popoverhovertarget")}`
  );
  trigger.addEventListener("pointerenter", () => {
    hoverTimer = setTimeout(() => {
      if (!popover.matches(":open")) popover.showPopOver();
    }, 500);
    trigger.addEventListener("pointerleave", tearDown);
  });
});

Het voordeel van het instellen van iets in een expliciet zweefvenster is dat het ervoor zorgt dat de actie van de gebruiker opzettelijk is (een gebruiker beweegt bijvoorbeeld de aanwijzer over een doel). We willen de pop-up niet tonen tenzij dat hun bedoeling is.

Probeer deze demo uit, waarbij u het doel kunt laten zweven met het venster ingesteld op 0.5s .


Voordat we enkele veelvoorkomende gebruiksscenario's en voorbeelden gaan verkennen, zullen we eerst een paar zaken doornemen.


Soorten pop-over

We hebben niet-JavaScript-interactiegedrag besproken. Maar hoe zit het met het popover-gedrag als geheel? Wat als u geen "Licht ontslag" wilt? Of wil je een singleton-patroon toepassen op je popovers?

Met de Popover API kunt u drie typen popover opgeven die qua gedrag verschillen.

[popover=auto]/[popover] :

  • Ondersteuning voor nesten. Dit betekent ook niet alleen genest in de DOM. De definitie van een voorouderlijke popover is er een die:
    • gerelateerd aan DOM-positie (kind).
    • gerelateerd door attributen te activeren op onderliggende elementen zoals popovertoggletarget , popovershowtarget , enzovoort.
    • gerelateerd aan het anchor (in ontwikkeling CSS Anchoring API).
  • Licht afwijzen.
  • Bij het openen worden andere popovers verwijderd die geen voorouderlijke popovers zijn. Speel eens met de onderstaande demo die laat zien hoe nesten met voorouderlijke popovers werkt. Zie hoe het veranderen van sommige popoverhidetarget / popovershowtarget instanties naar popovertoggletarget dingen verandert.
  • Als je er één weggooit, worden ze allemaal weggegooid, maar als je er één in de stapel weggooit, worden alleen degenen erboven in de stapel weggegooid.

[popover=manual] :

  • Sluit geen andere popovers.
  • Geen licht ontslag.
  • Vereist expliciet afwijzen via triggerelement of JavaScript.

JavaScript-API

Als u meer controle over uw popovers nodig heeft, kunt u dingen aanpakken met JavaScript. Je krijgt zowel een showPopover als hidePopover methode. Je hebt ook popovershow en popoverhide evenementen om naar te luisteren:

Een popover weergeven js popoverElement.showPopover() Een popover verbergen:

popoverElement.hidePopover()

Luister of er een popover wordt weergegeven:

popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)

Luister of er een popover wordt weergegeven en annuleer de weergave:

popoverElement.addEventListener('popovershow',event => {
  event.preventDefault();
  console.warn(‘We blocked a popover from being shown’);
})

Luister of er een popover verborgen is:

popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)

U kunt een verborgen popover niet annuleren:

popoverElement.addEventListener('popoverhide',event => {
  event.preventDefault();
  console.warn("You aren't allowed to cancel the hiding of a popover");
})

Controleer of er een popover in de bovenste laag staat:

popoverElement.matches(':open')

Dit levert extra vermogen voor enkele minder vaak voorkomende scenario's. Toon bijvoorbeeld een popover na een periode van inactiviteit.

Deze demo heeft popovers met hoorbare ploffen, dus we hebben JavaScript nodig om de audio af te spelen. Als we erop klikken, verbergen we de popover, spelen we de audio af en laten we deze vervolgens opnieuw zien.

Toegankelijkheid

Toegankelijkheid staat voorop bij het denken met de Popover API. Toegankelijkheidstoewijzingen koppelen de popover, indien nodig, aan het triggerelement. Dit betekent dat u geen aria-* attributen zoals aria-haspopup hoeft te declareren, ervan uitgaande dat u een van de activerende attributen zoals popovertoggletarget gebruikt.

Voor focusbeheer kunt u het autofocus-attribuut gebruiken om de focus naar een element in een popover te verplaatsen. Dit is hetzelfde als voor een dialoog, maar het verschil ontstaat bij het terugkeren van de focus, en dat komt door licht negeren. In de meeste gevallen keert het sluiten van een popover terug naar het eerder gefocuste element. Maar de focus wordt verplaatst naar een aangeklikt element bij licht negeren, als het focus kan krijgen. Bekijk het gedeelte over focusmanagement in de uitleg.

U moet de " volledige schermversie " van deze demo openen om te zien dat deze werkt.

In deze demo krijgt het gefocuste element een groene omtrek. Probeer met uw toetsenbord door de interface te bladeren. Merk op waar de focus terugkomt wanneer een popover wordt gesloten. Het zal u wellicht ook opvallen dat als u rondkijkt, de popover wordt gesloten. Dat is zo ontworpen. Hoewel popovers over focusbeheer beschikken, houden ze de focus niet vast. En toetsenbordnavigatie identificeert een sluitsignaal wanneer de focus uit de popover beweegt.

Verankering (in ontwikkeling)

Als het om popovers gaat, is een lastig patroon om rekening mee te houden het verankeren van het element aan de trigger. Als bijvoorbeeld een tooltip is ingesteld om boven de trigger te worden weergegeven, maar er door het document wordt gescrolld. Die tooltip kan worden afgesneden door de viewport. Er zijn huidige JavaScript-aanbiedingen om hiermee om te gaan, zoals " Floating UI ". Zij zullen de tooltip voor u herpositioneren om dit te voorkomen en vertrouwen op de gewenste positievolgorde.

Maar we willen dat u dit met uw stijlen kunt definiëren. Er is een begeleidende API in ontwikkeling naast de Popover API om dit aan te pakken. Met de " CSS Anchor Positioning " API kunt u elementen aan andere elementen koppelen, en dit op een manier die elementen herpositioneert, zodat ze niet worden afgesneden door de viewport.

Deze demo gebruikt de Anchoring API in de huidige staat. De positie van de boot reageert op de positie van het anker in de kijkpoort.

Hier is een fragment van de CSS waardoor deze demo werkt. Geen JavaScript vereist.

.anchor {
  --anchor-name: --anchor;
}
.anchored {
  position: absolute;
  position-fallback: --compass;
}
@position-fallback --compass {
  @try {
    bottom: anchor(--anchor top);
    left: anchor(--anchor right);
  }
  @try {
    top: anchor(--anchor bottom);
    left: anchor(--anchor right);
  }
}

Je kunt de specificaties hier bekijken. Er komt ook een polyfill voor deze API.

Voorbeelden

Nu je bekend bent met wat popover te bieden heeft en hoe, gaan we een paar voorbeelden bekijken.

Meldingen

Deze demo toont de melding 'Kopiëren naar klembord'.

  • Gebruikt [popover=manual] .
  • In actie popover weergeven met showPopover .
  • Na een time-out 2000ms verberg je het met hidePopover .

Toast

Deze demo gebruikt de bovenste laag om meldingen over toaststijlen weer te geven.

  • Eén popover met manual fungeert als container.
  • Nieuwe meldingen worden aan de popover toegevoegd en de popover wordt getoond.
  • Ze worden bij klikken verwijderd met de webanimatie-API en verwijderd uit de DOM.
  • Als er geen toasts zijn om te tonen, is de popover verborgen.

Genest menu

Deze demo laat zien hoe een genest navigatiemenu zou kunnen werken.

  • Gebruik [popover=auto] omdat dit geneste popovers toestaat.
  • Gebruik autofocus op de eerste link van elke vervolgkeuzelijst om via het toetsenbord te navigeren.
  • Dit is een perfecte kandidaat voor de CSS Anchoring API. Maar voor deze demo kunt u een kleine hoeveelheid JavaScript gebruiken om de posities bij te werken met behulp van aangepaste eigenschappen.
const ANCHOR = (anchor, anchored) => () => {
  const { top, bottom, left, right } = anchor.getBoundingClientRect();
  anchored.style.setProperty("--top", top);
  anchored.style.setProperty("--right", right);
  anchored.style.setProperty("--bottom", bottom);
  anchored.style.setProperty("--left", left);
};

PRODUCTS_MENU.addEventListener("popovershow", ANCHOR(PRODUCT_TARGET, PRODUCTS_MENU));

Houd er rekening mee dat, omdat deze demo autofocus gebruikt, deze in " volledige schermweergave " moet worden geopend voor toetsenbordnavigatie.

Media-popover

Deze demo laat zien hoe u media kunt laten verschijnen.

  • Gebruikt [popover=auto] voor licht negeren.
  • JavaScript luistert naar de play van de video en laat de video verschijnen.
  • De gebeurtenis popovers popoverhide pauzeert de video.

Popovers in Wiki-stijl

Deze demo laat zien hoe u tooltips voor inline inhoud kunt maken die media bevatten.

  • Gebruikt [popover=auto] . Als je er één laat zien, verberg je de anderen omdat ze niet voorouderlijk zijn.
  • Getoond op pointerenter met JavaScript.
  • Nog een perfecte kandidaat voor de CSS Anchoring API.

Deze demo maakt een navigatielade met behulp van een popover.

  • Gebruikt [popover=auto] voor licht negeren.
  • Gebruikt autofocus om het eerste navigatie-item scherp te stellen.

Achtergronden beheren

Deze demo laat zien hoe u achtergronden kunt beheren voor meerdere popovers waarbij u slechts één ::backdrop zichtbaar wilt hebben.

  • Gebruik JavaScript om een ​​lijst bij te houden van de popovers die zichtbaar zijn.
  • Pas een klassenaam toe op de onderste popover in de bovenste laag.

Aangepaste cursorpopover

Deze demo laat zien hoe u popover kunt gebruiken om een canvas naar de bovenste laag te promoveren en hoe u dit kunt gebruiken om een ​​aangepaste cursor weer te geven.

  • Promoot canvas naar de bovenste laag met showPopover en [popover=manual] .
  • Wanneer andere popovers worden geopend, verbergt en toont u de canvas om er zeker van te zijn dat deze zich bovenaan bevindt.

Actieblad-popover

Deze demo laat zien hoe u een popover als actieblad kunt gebruiken.

  • Zorg ervoor dat de popover standaard wordt weergegeven als overheersende display .
  • Actionsheet wordt geopend met de popover-trigger.
  • Wanneer de popover wordt weergegeven, wordt deze gepromoveerd naar de bovenste laag en vertaald naar weergave.
  • Licht ontslag kan worden gebruikt om het terug te sturen.

Toetsenbordgeactiveerde popover

Deze demo laat zien hoe u popover kunt gebruiken voor de gebruikersinterface in opdrachtpaletstijl.

  • Gebruik cmd + j om de popover weer te geven.
  • De input wordt scherpgesteld met autofocus .
  • De keuzelijst met invoervak ​​is een tweede popover die onder de hoofdingang is geplaatst.
  • Licht negeren sluit het palet als de vervolgkeuzelijst niet aanwezig is.
  • Nog een kandidaat voor de Anchoring API

Getimede pop-over

Deze demo toont na vier seconden een inactiviteitspopover. Een UI-patroon dat vaak wordt gebruikt in apps die beveiligde informatie over een gebruiker bevatten om een ​​uitlogmodaliteit weer te geven.

  • Gebruik JavaScript om de popover weer te geven na een periode van inactiviteit.
  • In de popover-show stelt u de timer opnieuw in.

Screensaver

Net als bij de vorige demo kunt u een vleugje eigenzinnigheid aan uw site toevoegen en een screensaver toevoegen.

  • Gebruik JavaScript om de popover weer te geven na een periode van inactiviteit.
  • Licht negeren om de timer te verbergen en opnieuw in te stellen.

Caret volgen

Deze demo laat zien hoe u een popover na een invoercursor kunt laten volgen.

  • Toon de popover op basis van selectie, sleutelgebeurtenis of invoer van speciale tekens.
  • Gebruik JavaScript om de popover-positie bij te werken met aangepaste eigenschappen met bereik.
  • Dit patroon vereist dat er goed wordt nagedacht over de inhoud die wordt getoond en de toegankelijkheid ervan.
  • Het wordt vaak gezien in de gebruikersinterface voor tekstbewerking en in apps waar je kunt taggen.

Zwevend actieknopmenu

Deze demo laat zien hoe u popover kunt gebruiken om een ​​zwevend actieknopmenu te implementeren zonder JavaScript.

  • Promoot een manual popover met de showPopover methode. Dit is de hoofdknop.
  • Het menu is een andere popover die het doel is van de hoofdknop.
  • Menu wordt geopend met popovertoggletarget .
  • Gebruik autofocus om scherp te stellen op het eerste weergegeven menu-item.
  • Licht negeren sluit het menu.
  • De pictogramdraai gebruikt :has() . Je kunt meer lezen over :has() in dit artikel .

Dat is het!

Dus dat is een intro voor popover, die binnenkort beschikbaar komt als onderdeel van het Open UI-initiatief. Als het verstandig wordt gebruikt, wordt het een fantastische toevoeging aan het webplatform.

Zorg ervoor dat u Open UI eens bekijkt. De popover-uitleg wordt up-to-date gehouden naarmate de API evolueert. En hier is de verzameling voor alle demo's.

Bedankt voor het langskomen!


Foto door Madison Oren op Unsplash