Ondersteuning van de bovenste laag in Chrome DevTools

Chrome DevTools voegt ondersteuning toe voor elementen uit de bovenste laag, waardoor het voor ontwikkelaars gemakkelijker wordt om fouten op te sporen in hun code die gebruik maakt van elementen uit de bovenste laag.

In dit artikel wordt beschreven wat elementen van de bovenste laag zijn, hoe DevTools helpt de inhoud van de bovenste laag te visualiseren om de DOM-structuur die elementen van de bovenste laag bevat te begrijpen en fouten op te sporen, en hoe DevTools-ondersteuning voor de bovenste laag wordt geïmplementeerd.

Wat zijn de toplaag en toplaagelementen?

Wat gebeurt er intern precies als je een <dialog> als modaal opent? 🤔

Het wordt in een toplaag geplaatst. Inhoud op de bovenste laag wordt weergegeven bovenop alle andere inhoud. Een modale dialoog moet bijvoorbeeld bovenop alle andere DOM-inhoud verschijnen, zodat de browser dit element automatisch in een 'toplaag' weergeeft in plaats van auteurs te dwingen handmatig tegen de z-index te vechten. Zelfs met de hoogste z-index verschijnt een toplaagelement bovenop een element.

De toplaag kan omschreven worden als ‘de hoogste stapellaag’. Elk document heeft één bijbehorende viewport en dus ook één toplaag. Er kunnen meerdere elementen tegelijkertijd in de bovenste laag zitten. Wanneer dat gebeurt, stapelen ze op elkaar, de laatste bovenaan. Met andere woorden: alle elementen van de bovenste laag worden in een last in, first out (LIFO)-stapel in de bovenste laag geplaatst.

Het <dialog> -element is niet het enige element dat de browser in een toplaag rendert. Momenteel zijn de elementen in de bovenste laag: popovers , modale dialogen en elementen in volledig schermmodus .

Bestudeer de volgende dialoogimplementatie:

<main>
  <button onclick="window.dialog.showModal();">Open Dialog</button>
</main>
<dialog id="dialog"></dialog>

Hier is een demo met een aantal dialoogvensters waarop stijlen zijn toegepast op hun achtergronden (achtergronden worden hieronder beschreven):

Wat is een achtergrond?

Gelukkig is er een manier om de inhoud onder het bovenste laagelement aan te passen.

Elk element in de bovenste laag heeft een CSS-pseudo-element dat een achtergrond wordt genoemd.

De achtergrond is een vak ter grootte van het kijkvenster dat direct onder elk bovenste laagelement wordt weergegeven. Het ::backdrop pseudo-element maakt het mogelijk om alles wat zich onder het element bevindt te verbergen, te stylen of volledig te verbergen als het de bovenste in de bovenste laag is.

Wanneer u meerdere elementen modaal maakt, tekent de browser de achtergrond direct onder het voorste element en bovenop andere elementen op volledig scherm.

Zo stijl je een achtergrond:

/* The browser displays the backdrop only when the dialog.showModal() function opens the dialog.*/
dialog::backdrop {
    background: rgba(255,0,0,.25);
}

Hoe kan ik alleen de eerste achtergrond weergeven?

Elk toplaagelement heeft een achtergrond die bij een toplaagstapel hoort. Deze achtergronden zijn zo ontworpen dat ze elkaar overlappen, dus als de dekking van een achtergrond niet 100% is, zijn de onderliggende achtergronden zichtbaar.

Als alleen de eerste achtergrond in de stapel van de bovenste laag zichtbaar hoeft te zijn, kunt u dit bereiken door de item-ID's in de stapel van de bovenste laag bij te houden.

Als het toegevoegde element niet het eerste element in de bovenste laag is, past de functie die wordt aangeroepen wanneer het element in de bovenste laag wordt geplaatst een klasse hiddenBackdrop toe op de ::backdrop . Deze klasse wordt verwijderd wanneer het element uit de bovenste laag wordt verwijderd.

Bekijk de code in deze voorbeelddemo:

Ondersteuningsontwerp van de bovenste laag in DevTools

DevTools-ondersteuning voor de bovenste laag helpt ontwikkelaars het concept van de bovenste laag te begrijpen en te visualiseren hoe de inhoud van de bovenste laag verandert. Met deze functies kunnen ontwikkelaars het volgende identificeren:

  • De elementen in de bovenste laag op elk moment en hun volgorde.
  • Het element dat op elk punt bovenaan de stapel staat.

Bovendien helpt DevTools-ondersteuning voor de bovenste laag om de positie van het achtergrondpseudo-element in de stapel van de bovenste laag te visualiseren. Hoewel het geen boomelement is, speelt het een belangrijke rol in de werking van de bovenste laag en kan het nuttig zijn voor ontwikkelaars.

Met de ondersteuningsfuncties van de bovenste laag kunt u:

  1. Bekijk op elk moment welke elementen zich in de stapel van de bovenste laag bevinden. De representatiestapel van de bovenste laag verandert dynamisch naarmate elementen worden toegevoegd aan of verwijderd uit de bovenste laag.
  2. Bekijk de elementpositie in de stapel van de bovenste laag.
  3. Spring van het achtergrond-pseudo-element of achtergrond-pseudo-element van de bovenste laag in de boom naar het element of achtergrond-pseudo-element in de representatiecontainer van de bovenste laag en terug.

Laten we eens kijken hoe we deze functies kunnen gebruiken!

Bovenste laag container

Om de elementen van de bovenste laag te helpen visualiseren, voegt DevTools een container voor de bovenste laag toe aan de elementenboom. Het bevindt zich na de afsluitende </html> -tag.

Met deze container kunt u op elk moment de elementen in de stapel van de bovenste laag observeren. De bovenste laagcontainer is een lijst met koppelingen naar de bovenste laagelementen en hun achtergronden. De representatiestapel van de bovenste laag verandert dynamisch naarmate elementen worden toegevoegd aan of verwijderd uit de bovenste laag.

Om toplaagelementen binnen de elementenboom of de toplaagcontainer te vinden, klikt u op de links van de toplaagelementweergave in de toplaagcontainer naar hetzelfde element in de elementenboom en terug.

Om van het containerelement van de bovenste laag naar het boomelement van de bovenste laag te springen, klikt u op de onthulknop naast het element in de container van de bovenste laag.

Springen van de containerlink van de bovenste laag naar het element.

Om van het boomelement van de bovenste laag naar de link in de container van de bovenste laag te springen, klikt u op de badge van de bovenste laag naast het element.

Van een element naar de containerlink op de bovenste laag springen.

U kunt elke badge uitschakelen, inclusief de badge op de bovenste laag . Om de badges uit te schakelen, klikt u met de rechtermuisknop op een badge, kiest u Badge-instellingen en verwijdert u de vinkjes naast de badges die u wilt verbergen.

De badge uitschakelen.

Elementenvolgorde in de stapel van de bovenste laag

De container op de bovenste laag toont de elementen zoals ze in de stapel verschijnen, maar in omgekeerde volgorde. De bovenkant van het stapelelement is de laatste in de elementenlijst van de bovenste laagcontainer. Dit betekent dat het laatste element in de containerlijst op de bovenste laag het element is waarmee u momenteel in het document kunt communiceren.

De badges naast de boomelementen geven aan of de elementen tot de bovenste laag behoren en bevatten het positienummer van een element in de stapel.

In deze schermafbeelding bestaat de stapel van de bovenste laag uit twee elementen, waarbij het tweede element bovenaan de stapel staat. Als je het tweede element verwijdert, schuift het eerste naar boven.

De volgorde van elementen in de stapel.

Achtergronden in de bovenste laagcontainer

Zoals hierboven vermeld, heeft elk element in de bovenste laag een CSS-pseudo-element genaamd achtergrond. Je kunt dit element stylen, dus het is handig om het ook te inspecteren en de weergave ervan te zien.

In de elementenboom bevindt een achtergrondelement zich vóór de afsluitende tag van het element waartoe het behoort. In de bovenste laagcontainer wordt echter een achtergrondlink weergegeven direct boven het bovenste laagelement waartoe deze behoort.

Achtergronden stapelpositie.

Wijzigingen in de DOM-structuur

ElementsTreeElement , de klasse die verantwoordelijk is voor het maken en beheren van individuele DOM-boomelementen in DevTools, was niet voldoende om een ​​container op de bovenste laag te implementeren.

Om de container op de bovenste laag als knooppunt in de boom weer te geven, hebben we een nieuwe klasse toegevoegd die knooppunten van DevTools-boomelementen maakt. Voorheen initialiseerde de klasse die verantwoordelijk was voor het maken van de DevTools-elementenboom elk TreeElement met een DOMNode , een klasse met een backendNodeId en andere backend-gerelateerde eigenschappen. backendNodeId wordt op zijn beurt toegewezen aan de backend.

Het containerknooppunt op de bovenste laag, dat een lijst met links naar elementen van de bovenste laag heeft, moest zich gedragen als een normaal boomelementknooppunt. Dit knooppunt is echter geen 'echt' DOM-knooppunt en de backend hoeft niet het containerknooppunt op de bovenste laag te maken.

Om een ​​frontend-knooppunt te maken dat de bovenste laag vertegenwoordigt, hebben we een nieuw type frontend-knooppunt toegevoegd dat is gemaakt zonder een DOMNode . Dit containerelement op de bovenste laag is het eerste frontend-knooppunt dat geen DOMNode heeft, wat betekent dat het alleen op de frontend bestaat en dat de backend er niets van 'weet'. Om hetzelfde gedrag te hebben als andere knooppunten, hebben we een nieuwe TopLayerContainer -klasse gemaakt die de klasse UI.TreeOutline.TreeElement uitbreidt, die verantwoordelijk is voor het gedrag van frontend-knooppunten.

Om de gewenste plaatsing te bereiken, koppelt de klasse die een element rendert TopLayerContainer als de volgende broer of zus van de <html> -tag.

Een nieuwe toplaagbadge geeft aan dat het element zich in de toplaag bevindt en dient als link naar de snelkoppeling van dit element in het TopLayerContainer -element.

Initieel ontwerp

In eerste instantie was het plan om elementen van de bovenste laag te dupliceren in de container van de bovenste laag, in plaats van een lijst met links naar de elementen te maken. We hebben deze oplossing niet geïmplementeerd vanwege de manier waarop het ophalen van onderliggende elementen van elementen werkt in DevTools. Elk element heeft een ouderaanwijzer die wordt gebruikt bij het ophalen van onderliggende elementen en het is onmogelijk om meerdere verwijzingen te hebben. Daarom kunnen we geen knooppunt hebben dat zich op de juiste manier uitbreidt en alle onderliggende elementen op meerdere plaatsen in de boom bevat. Over het algemeen is het systeem niet gebouwd met dubbele substructuren in gedachten.

Het compromis waar we toe kwamen was het creëren van links naar de frontend DOM-knooppunten in plaats van die knooppunten te dupliceren. De klasse die verantwoordelijk is voor het maken van koppelingen naar elementen in DevTools is ShortcutTreeElement , die de UI.TreeOutline.TreeElement uitbreidt. ShortcutTreeElement gedraagt ​​zich hetzelfde als andere DevTools DOM-boomelementen, maar heeft geen corresponderend knooppunt op de backend en heeft een knop die linkt naar een ElementsTreeElement . Elk ShortcutTreeElement naar het knooppunt in de bovenste laag heeft een onderliggend ShortcutTreeElement dat linkt naar de representatie van een ::backdrop pseudo-element in de DevTools DOM-boom.

Oorspronkelijk ontwerp:

Initieel ontwerp.

Wijzigingen in het Chrome DevTools Protocol (CDP).

Om de ondersteuning op de bovenste laag te implementeren, zijn wijzigingen in Chrome DevTools Protocol (CDP) vereist. CDP dient als communicatieprotocol tussen DevTools en Chromium.

We moeten het volgende toevoegen:

  • Een opdracht om op elk moment vanaf de frontend te bellen.
  • Een gebeurtenis die vanaf de backend op de frontend moet worden geactiveerd.

CDP: DOM.getTopLayerElements -opdracht

Om de huidige elementen van de bovenste laag weer te geven, hebben we een nieuwe experimentele CDP-opdracht nodig die een lijst met knooppunt-ID's retourneert van de elementen die zich in de bovenste laag bevinden. DevTools roept deze opdracht aan telkens wanneer DevTools worden geopend of wanneer de elementen in de bovenste laag veranderen. De opdracht ziet er als volgt uit:

  # Returns NodeIds of the current top layer elements.
  # Top layer renders closest to the user within a viewport, therefore, its elements always
  # appear on top of all other content.
  experimental command getTopLayerElements
    returns
      # NodeIds of the top layer elements.
      array of NodeId nodeIds

CDP: DOM.topLayerElementsUpdated -gebeurtenis

Om de actuele lijst van de elementen van de bovenste laag te krijgen, hebben we elke wijziging van de elementen van de bovenste laag nodig om een ​​experimentele CDP-gebeurtenis te activeren. Deze gebeurtenis informeert de frontend over de wijziging, die vervolgens de opdracht DOM.getTopLayerElements aanroept en de lijst met nieuwe elementen ontvangt.

Het evenement ziet er als volgt uit:

  # Called by the change of the top layer elements.
  experimental event topLayerElementsUpdated

CDP-overwegingen

Er waren meerdere opties hoe de CDP-ondersteuning van de toplaag kon worden geïmplementeerd. Een andere optie die we overwogen was het maken van een gebeurtenis die de lijst met elementen van de bovenste laag zou retourneren in plaats van alleen de voorkant te informeren over een toevoeging of verwijdering van een element van de bovenste laag.

Als alternatief kunnen we twee gebeurtenissen maken in plaats van het commando: topLayerElementAdded en topLayerElementRemoved . In dit geval zouden we een element ontvangen en zouden we de array van de bovenste laagelementen aan de voorkant moeten beheren.

Momenteel roept een frontend-gebeurtenis de opdracht getTopLayerElements aan om een ​​lijst met bijgewerkte elementen op te halen. Als we elke keer dat een gebeurtenis wordt geactiveerd een lijst met elementen of een specifiek element dat de verandering veroorzaakte, zouden verzenden, zouden we één stap van het aanroepen van de opdracht kunnen vermijden. In dit geval verliest de frontend echter de controle over welke elementen worden gepusht.

We hebben het op deze manier geïmplementeerd omdat het naar onze mening beter is als de frontend beslist wanneer toplaagnodes moeten worden aangevraagd. Als de bovenste laag bijvoorbeeld is samengevouwen in de gebruikersinterface of als de gebruiker een DevTools-paneel gebruikt dat niet over de elementenboom beschikt, is het niet nodig om de extra knooppunten op te halen die zich dieper in de boom kunnen bevinden.