Journal
Teknikk3 min lesetid

Nedtrekksmenyen som nektet å la seg klikke

En meny tegnet seg perfekt, lå nøyaktig der den skulle, og slukte hvert eneste klikk. Synderen var en inngangsanimasjon som aldri helt tok slutt. En kort tur gjennom stablingskontekster i CSS, fyllmodus for animasjoner, og hvordan man jakter på feil man ikke kan se.

UIUtviklerteamet i Bookatu

Vi slapp en nedtrekksmeny som ikke kunne klikkes. Den åpnet seg fint. Den tegnet seg på riktig sted, fullt synlig, riktig z-index, ingen feil i konsollen. Klikkene gikk rett gjennom den og ned i panelet under, som om menyen var et spøkelse. Har du noen gang kjempet med en slik feil, kjenner du allerede den forvirrende delen: alt ser riktig ut, og ingenting virker.

Å finne ut hvem som faktisk ligger øverst

Det første ærlige svaret kom fra nettleseren, ikke fra å lese kode. document.elementFromPoint forteller deg hvilket element som virkelig tar imot et klikk i et gitt punkt. Vi pekte den mot midten av et menyvalg, og den returnerte et kort som lå lenger nede på siden. Menyen lå visuelt øverst og fysisk under.

js
// Who really gets the click at this point?const item = document.querySelector('[role="menuitem"]');const r = item.getBoundingClientRect();const top = document.elementFromPoint(r.x + r.width / 2, r.y + r.height / 2);console.log(top === item || item.contains(top)); // false -> something covers it // Walk the ancestors and list everything that creates a stacking contextfor (let el = item; el; el = el.parentElement) {  const cs = getComputedStyle(el);  const reasons = [];  if (cs.transform !== 'none') reasons.push('transform');  if (cs.filter !== 'none') reasons.push('filter');  if (cs.backdropFilter !== 'none') reasons.push('backdrop-filter');  if (cs.opacity !== '1') reasons.push('opacity');  if (cs.position !== 'static' && cs.zIndex !== 'auto') reasons.push('z-index');  if (reasons.length) console.log(el.className, reasons);}

Vandringen fant det. Headeren som inneholdt menyen rapporterte en transform, enda ikke noe stilark ga den en. Den eneste transformen i nærheten var en inngangsanimasjon, en liten toning som steg opp, og som var ferdig et halvt sekund etter at siden lastet.

Fyllmodus lever lenger enn animasjonen

animation-fill-mode: both betyr at elementet holder den første keyframen før animasjonen starter og beholder den siste keyframen etter at den er ferdig. Beholder, for alltid. Animerer en keyframe transform, bruker den ferdige animasjonen fortsatt en transform, og et transformert element blir en stablingskontekst. Hver etterkommers z-index er nå fanget inne i den, og konkurrerer bare med lokale søsken, aldri med resten av siden.

Siden vår hadde paneler etter headeren som brukte backdrop-filter, som lager sin egen stablingskontekst. To forseglede bokser, og malerekkefølgen mellom dem avgjort av rekkefølgen i treet, ikke av z-index-en vi forvirret fortsatte å skru opp. Menyen kunne hatt z-index på en million. Inne i sin egen boks var det ingen utenfor som hørte den.

Her er fella inni fella. Vi endret den siste keyframen til transform: none, med den tanken at den beholdte verdien da ville være harmløs. Den beregnede stilen rapporterte fortsatt en matrise. Nettlesere løser transformen i en anvendt animasjon som en matriseverdi, og en identitetsmatrise teller fortsatt som en transform når det gjelder stabling. Stablingskonteksten overlevde fiksen vår.

css
/* BAD: the finished animation keeps applying its last keyframe forever.   The header remains a stacking context for the life of the page. */.enter {  animation: rise 0.5s ease-out both;} /* GOOD: fill backwards only. The element returns to its natural state   after the animation, which is identical to the final keyframe anyway. */.enter {  animation: rise 0.5s ease-out backwards;} @keyframes rise {  from { opacity: 0; transform: translateY(10px); }  to   { opacity: 1; transform: none; }}

Regelen vi skrev ned

Inngangsanimasjoner på beholdere fyller bakover, aldri begge veier. Bakover dekker det eneste hullet som betyr noe, bildene før en forsinket animasjon starter. Når animasjonen er ferdig, løsner elementet seg helt fra den og slutter å være en stablingskontekst. Det visuelle resultatet er identisk, fordi en godt bygget inngang ender i elementets naturlige tilstand.

En nedtrekksmeny som åpner seg riktig over frostede glasspaneler i et mørkt dashbord
Etter fiksen: menyen tegnes over de frostede panelene og, viktigere, fanger opp sine egne klikk.
  • Stol på elementFromPoint fremfor øynene dine. Visuell rekkefølge og treffrekkefølge kan være uenige, og bare én av dem tar imot klikket.
  • Når z-index slutter å gi mening, slutt å skru den opp og begynn å jakte på stablingskontekster. Vandringen over tar ett minutt.
  • Sjekk hva de ferdige animasjonene dine etterlater seg. getComputedStyle på en rolig side forteller deg hva som fortsatt er i bruk.
  • Fylte transformer er transformer, selv når keyframen sier none.

Animasjonen varte et halvt sekund. Bivirkningen hadde ingen sluttid i det hele tatt.

stablingskontekst i cssanimation-fill-modez-index virker ikkefeilsøking i frontendcss transform og stabling
Klar til å ta dette i bruk?

Bookatu gir deg en bookingside med ditt eget preg, depositum, medlemskap, gavekort og påminnelser, med 0 % provisjon på bookingene dine.

Kom i gang gratis