Uppdatera komponenttillståndet när ett val måste hålla kontroller, text och beteende konsekventa. Åsidosätt CSS när du enbart ändrar presentationen. Den här guiden använder en lokal förhandsvisning av en platta i Safari på Mac för att visa hur du väljer och verifierar båda metoderna.

Läsarens uppgift är specifik: ändra en liten webbsida som du själv skapat så att formkontrollerna förblir synkroniserade, samtidigt som den dekorativa formgivningen är enkel att redigera. Här betyder komponenttillstånd ett JavaScript-objekt som hanteras av sidans egen uppdateringsfunktion. Ingen specifik produkt eller något ramverk förutsätts.

Vilka ändringar hör hemma i komponenttillståndet?

Börja med att fråga dig vad som måste stämma överens efter ändringen. Om valet av en rund platta måste uppdatera en kryssruta, en förhandsvisning och en textbeskrivning, låt det valet ha en enda ägare i tillståndet. Härled sedan alla tre utdata från det. Det här är en rekommendation för implementationen: den gör sambandet mellan ett val och resultaten som visas tydligt.

En CSS-anpassad egenskap som --tile-size har en annan roll. Den tillhandahåller ett värde till deklarationer som refererar till den med var(). Anpassade egenskaper deltar i kaskaden och ärvs normalt; deras beteende följer CSS-regler även när JavaScript anger dem. Specifikationen för CSS Custom Properties definierar detta.

Använd den här beslutshjälpen innan du redigerar:

Önskad ändring Rekommenderad ägare Anledning i det här exemplet
Läsaren väljer en rund eller fyrkantig platta Komponenttillstånd Kryssrutan, beskrivningen och formen måste stämma överens
Läsaren väljer plattans storlek Komponenttillstånd, återgivet i CSS Reglaget, beskrivningen och måtten delar ett värde
Alla plattor ska ha olika dekorativa färger Formatmallens deklaration Inget valt värde eller någon text behöver uppdateras
Förhandsvisningen behöver mer utrymme runtom Formatmallens deklaration Detta ändrar layouten runt plattan
Ett tillfälligt mått behövs vid granskning Lokal CSS-åsidosättning Det är ett visuellt experiment som ska tas bort efteråt

Skillnaden beror på uppgiften. Färgen hör hemma i CSS i den här förhandsvisningen eftersom den är fast dekoration. Om du senare lägger till en färgväljare och en beskrivning av den valda färgen, behandlar du valet som tillstånd och återger dess visuella effekt. Undvik att skapa två värden för samma val som kan redigeras oberoende av varandra.

Inbyggda kontroller har också ett eget tillstånd. Läs en kryssrutas aktuella egenskap checked. HTML-attributet checked anger om den ska vara markerad som standard och följer inte senare ändringar som användaren gör. Den här skillnaden beskrivs i MDN:s referens för kryssrutans egenskap.

Skapa en fristående förhandsvisning på din Mac

Spara följande kompletta dokument som tile-preview.html i en vanlig textredigerare för kod och öppna det sedan i Safari. Behåll filändelsen .html. Exemplet innehåller sin CSS och JavaScript, använder systemtypsnitt och ritar en enkel geometrisk platta utan externa resurser.

Formatmallen ligger i dokumentets head. HTML definierar elementet style som ett sätt att bädda in CSS och skiljer mellan fristående interna format och formatmallar som refererar till ytterligare resurser. Genom att hålla exemplet fristående blir dess formateringsvärden tydliga. Se HTML-standardens avsnitt om style och skript.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tile preview</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      margin: 2rem;
      color: #222;
      background: #fff;
    }
    label { display: block; margin-block: 1rem; }
    .preview { padding: 24px; background: #eee; }
    .tile {
      width: 80px;
      height: 80px;
      border-radius: 0;
      width: var(--tile-size, 80px);
      height: var(--tile-size, 80px);
      border-radius: var(--tile-radius, 0);
      background-color: #547899;
    }
  </style>
</head>
<body>
  <h1>Tile preview</h1>
  <label>
    <input id="round" type="checkbox"> Round tile
  </label>
  <label>
    Tile size
    <input id="size" type="range" min="40" max="160"
           step="1" value="80">
  </label>
  <button id="reset" type="button">Reset choices</button>
  <p id="summary"></p>
  <div class="preview">
    <div id="tile" class="tile" aria-hidden="true"></div>
  </div>
  <script>
    const state = { rounded: false, size: 80 };
    const round = document.getElementById('round');
    const size = document.getElementById('size');
    const summary = document.getElementById('summary');
    const tile = document.getElementById('tile');
    const reset = document.getElementById('reset');

    function render() {
      round.checked = state.rounded;
      size.value = String(state.size);
      tile.style.setProperty('--tile-size', `${state.size}px`);
      tile.style.setProperty('--tile-radius', state.rounded ? '50%' : '0');
      summary.textContent = `Tile A: ${state.rounded ? 'round' : 'square'}, ${state.size}px.`;
    }

    function update(patch) {
      const next = { ...state, ...patch };
      if (typeof next.rounded !== 'boolean' ||
          !Number.isInteger(next.size) ||
          next.size < 40 || next.size > 160) {
        throw new RangeError('Use a boolean and a size from 40 to 160.');
      }
      Object.assign(state, next);
      render();
    }

    round.addEventListener('change', () => {
      update({ rounded: round.checked });
    });
    size.addEventListener('input', () => {
      update({ size: Number(size.value) });
    });
    reset.addEventListener('click', () => {
      update({ rounded: false, size: 80 });
    });
    render();
  </script>
</body>
</html>

Stilgränssnittet som används här är dokumenterat: element.style.setProperty() skriver en inline-deklaration, även en anpassad egenskap. Egenskapsnamn använder CSS-format, till exempel --tile-size. Se MDN:s referens för setProperty.

Prova att välja Round tile, flytta Tile size till ett annat värde och välja Reset choices. De två första åtgärderna ska uppdatera både formen och beskrivningen. Återställningen ska återställa en fyrkant på 80 pixlar. I det här programmet anropar en tilldelning till ett fält i state inte render(). Låt ändringar gå via update() så att validering och rendering sker tillsammans.

Uppdaterings- och renderingslogiken kontrollerades i Node med mockade kontroller och ett mockat stilobjekt för initialvärden, ändrade värden, ogiltiga storlekar och återställning. Kontrollerna godkändes; de bekräftar inte hur sidan renderas i en webbläsare. Använd de synliga kontrollerna ovan för att verifiera filen i Safari.

När bör du åsidosätta en CSS-deklaration?

Om du vill göra en varaktig dekorativ ändring redigerar du den sparade formatmallen. Ändra till exempel background-color för .tile till en annan CSS-färg, eller justera utfyllnaden för .preview. Ingen av ändringarna kräver ett nytt tillståndsfält i det här programmet, eftersom ingen av dem representerar ett valt alternativ.

Om du vill göra ett tillfälligt experiment aktiverar du Safaris inspektör via Safari → Settings… → Advanced → Show features for web developers. Öppna Develop → Show Web Inspector, eller tryck på Option-Command-I. Stegen för Mac finns dokumenterade i WebKits inspektörsguide.

När exemplet är öppet ändrar följande kompletta kodsnutt i konsolen tillfälligt plattans färg och definierar sedan en funktion som tar bort åsidosättningen:

const previewTile = document.getElementById('tile');
if (!previewTile) throw new Error('Open tile-preview.html first.');
previewTile.style.setProperty('background-color', '#8069a6');
function restoreTileColor() {
  previewTile.style.removeProperty('background-color');
}
// Run restoreTileColor() when finished comparing colors.

När den här inline-deklarationen tas bort får den återstående kaskaden avgöra färgen igen. Det tar inte bort färgdeklarationen i formatmallen. Borttagningsmetoden beskrivs i MDN:s referens för removeProperty. Om du vill att den valda färgen ska finnas kvar efter omladdning i det här exemplet redigerar och sparar du CSS-källan.

Låt tillfälliga åsidosättningar vara åtskilda från värden som renderaren äger. Om du ändrar --tile-size manuellt kommer beskrivningen fortfarande att visa tillståndsvärdet, och nästa anrop till render() ersätter experimentet. Ändra storleken via kontrollen eller uppdateringsfunktionen när du vill att hela komponenten ska stämma överens.

Varför verkar en åsidosättning inte göra något?

Ta reda på hur värdet används innan du ökar selektorns specificitet. En deklaration kan vara giltig men ändå förlora i kaskaden, eller rikta sig mot en anpassad egenskap som den vinnande deklarationen inte längre använder.

  1. Kontrollera målet. Bekräfta att du har valt elementet som visar formen.
  2. Kontrollera indata. I exemplet måste storleken vara ett heltal mellan 40 och 160. Ogiltiga uppdateringar ger ett fel innan tillståndet ändras.
  3. Kontrollera användningen. Plattans mått använder var(--tile-size). En vinnande direkt deklaration av width kan kringgå det värdet för bredden, medan höjden fortfarande använder det.
  4. Kontrollera företrädet. Jämför tillämpliga deklarationer, viktighet, lager och specificitet.
  5. Kontrollera nästa interaktion. Om ändringen försvinner när du flyttar en kontroll undersöker du vad render() skriver.

Vanliga inline-deklarationer har företräde framför vanliga regler i författarens formatmall. Viktighet, animationer och övergångar påverkar dock den övergripande ordningen. En senare deklaration vinner inte automatiskt, och en mer specifik selektor löser inte alla konflikter. MDN:s guide till kaskaden beskriver dessa villkor.

Skilj också mellan inline-värden och beräknade värden. element.style visar inline-format, så ett tomt inline-värde betyder inte att elementet saknar formatering. getComputedStyle() läser av beräknade värden efter att aktiva formatmallar har tillämpats. Se MDN:s referens för style och referensen för getComputedStyle.

Kör följande kompletta diagnostiska kodsnutt i exempelsidans konsol:

const inspectedTile = document.getElementById('tile');
if (!inspectedTile) throw new Error('Open tile-preview.html first.');
console.table({
  inlineSizeToken: inspectedTile.style.getPropertyValue('--tile-size'),
  inlineWidth: inspectedTile.style.getPropertyValue('width'),
  resolvedWidth: getComputedStyle(inspectedTile).width,
  resolvedHeight: getComputedStyle(inspectedTile).height
});

Direkt efter att du öppnat det oförändrade exemplet är de förväntade värdena en inline-storleksvariabel på 80px, en tom inline-bredd och beräknade mått på 80px. Detta visar hur en anpassad inline-egenskap kan ge värde till en deklaration i formatmallen utan att skapa en inline-deklaration för width.

Vilka begränsningar för webbläsare och resurser spelar roll?

MDN anger att anpassade CSS-egenskaper har haft brett stöd mellan webbläsare sedan april 2017. Det innebär inte att de stöds i alla äldre webbläsare. Exemplet innehåller vanliga måttdeklarationer före sina var()-deklarationer, så plattan får ett statiskt utseende när anpassade egenskaper inte stöds. Se MDN:s referens för anpassade egenskaper.

Reservvärdet i var(--tile-size, 80px) ger inte kompatibilitet med en webbläsare som inte kan tolka anpassade egenskaper. Det validerar inte heller varje angivet värde: ett definierat värde som blue kan inte bli en giltig bredd bara för att det finns ett reservvärde. Dessa begränsningar förklaras i MDN:s guide till anpassade egenskaper.

Om en äldre målwebbläsare saknar stöd för anpassade egenskaper är det rekommenderade alternativet att ändra renderaren så att den med setProperty() skriver vanliga inline-deklarationer för width, height och border-radius. Behåll samma tillståndsvalidering och synkronisering av kontroller. Då ändras renderingskontraktet, så ta bort konkurrerande skrivningar från renderaren och verifiera målwebbläsaren i stället för att förutsätta att stödet är identiskt.

För den här lokala förhandsvisningen är valet tydligt: använd tillstånd för val av form och storlek, återge de valen via ett litet antal CSS-värden och låt dekorationen ligga i formatmallen. Testa en kontrollinteraktion och återställning efter varje ändring. Förhandsvisningen, kontrollerna och beskrivningen ska fortsätta visa samma valda värden.

Källor