CINXE.COM

Advanced Svelte: Reactivity, lifecycle, accessibility - Learn web development | MDN

<!doctype html><html lang="en-US" prefix="og: https://ogp.me/ns#"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" href="https://developer.mozilla.org/favicon-48x48.bc390275e955dacb2e65.png"/><link rel="apple-touch-icon" href="https://developer.mozilla.org/apple-touch-icon.528534bba673c38049c2.png"/><meta name="theme-color" content="#ffffff"/><link rel="manifest" href="https://developer.mozilla.org/manifest.f42880861b394dd4dc9b.json"/><link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="MDN Web Docs"/><title>Advanced Svelte: Reactivity, lifecycle, accessibility - Learn web development | MDN</title><link rel="alternate" title="Fortgeschrittenes Svelte: Reaktivität, Lebenszyklus, Barrierefreiheit" href="https://developer.mozilla.org/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility" hrefLang="de"/><link rel="alternate" title="Svelte 进阶:响应式、生命周期以及无障碍" href="https://developer.mozilla.org/zh-CN/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility" hrefLang="zh"/><link rel="alternate" title="Advanced Svelte: Reactivity, lifecycle, accessibility" href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility" hrefLang="en"/><link rel="preload" as="font" type="font/woff2" href="/static/media/Inter.var.c2fe3cb2b7c746f7966a.woff2" crossorigin=""/><link rel="alternate" type="application/rss+xml" title="MDN Blog RSS Feed" href="https://developer.mozilla.org/en-US/blog/rss.xml" hrefLang="en"/><meta name="description" content="In this article we have finished adding all the required functionality to our app, plus we&#x27;ve taken care of a number of accessibility and usability issues. We also finished splitting our app into manageable components, each one with a unique responsibility."/><meta property="og:url" content="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility"/><meta property="og:title" content="Advanced Svelte: Reactivity, lifecycle, accessibility - Learn web development | MDN"/><meta property="og:type" content="website"/><meta property="og:locale" content="en_US"/><meta property="og:description" content="In this article we have finished adding all the required functionality to our app, plus we&#x27;ve taken care of a number of accessibility and usability issues. We also finished splitting our app into manageable components, each one with a unique responsibility."/><meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.d893525a4fb5fb1f67a2.png"/><meta property="og:image:type" content="image/png"/><meta property="og:image:height" content="1080"/><meta property="og:image:width" content="1920"/><meta property="og:image:alt" content="The MDN Web Docs logo, featuring a blue accent color, displayed on a solid black background."/><meta property="og:site_name" content="MDN Web Docs"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="MozDevNet"/><link rel="canonical" href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility"/><style media="print">.article-actions-container,.document-toc-container,.language-menu,.main-menu-toggle,.on-github,.page-footer,.place,.sidebar,.top-banner,.top-navigation-main,ul.prev-next{display:none!important}.main-page-content,.main-page-content pre{padding:2px}.main-page-content pre{border-left-width:2px}</style><script src="/static/js/gtag.js" defer=""></script><script defer="" src="/static/js/main.5e889624.js"></script><link href="/static/css/main.26c64ea7.css" rel="stylesheet"/></head><body><script>if(document.body.addEventListener("load",(t=>{t.target.classList.contains("interactive")&&t.target.setAttribute("data-readystate","complete")}),{capture:!0}),window&&document.documentElement){const t={light:"#ffffff",dark:"#1b1b1b"};try{const e=window.localStorage.getItem("theme");e&&(document.documentElement.className=e,document.documentElement.style.backgroundColor=t[e]);const o=window.localStorage.getItem("nop");o&&(document.documentElement.dataset.nop=o)}catch(t){console.warn("Unable to read theme from localStorage",t)}}</script><div id="root"><ul id="nav-access" class="a11y-nav"><li><a id="skip-main" href="#content">Skip to main content</a></li><li><a id="skip-search" href="#top-nav-search-input">Skip to search</a></li><li><a id="skip-select-language" href="#languages-switcher-button">Skip to select language</a></li></ul><div class="page-wrapper category-learn document-page"><div class="top-banner loading"><section class="place top container"></section></div><div class="sticky-header-container"><header class="top-navigation "><div class="container "><div class="top-navigation-wrap"><a href="/en-US/" class="logo" aria-label="MDN homepage"><svg id="mdn-docs-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 694.9 104.4" style="enable-background:new 0 0 694.9 104.4" xml:space="preserve" role="img"><title>MDN Web Docs</title><path d="M40.3 0 11.7 92.1H0L28.5 0h11.8zm10.4 0v92.1H40.3V0h10.4zM91 0 62.5 92.1H50.8L79.3 0H91zm10.4 0v92.1H91V0h10.4z" class="logo-m"></path><path d="M627.9 95.6h67v8.8h-67v-8.8z" class="logo-_"></path><path d="M367 42h-4l-10.7 30.8h-5.5l-10.8-26h-.4l-10.5 26h-5.2L308.7 42h-3.8v-5.6H323V42h-6.5l6.8 20.4h.4l10.3-26h4.7l11.2 26h.5l5.7-20.3h-6.2v-5.6H367V42zm34.9 20c-.4 3.2-2 5.9-4.7 8.2-2.8 2.3-6.5 3.4-11.3 3.4-5.4 0-9.7-1.6-13.1-4.7-3.3-3.2-5-7.7-5-13.7 0-5.7 1.6-10.3 4.7-14s7.4-5.5 12.9-5.5c5.1 0 9.1 1.6 11.9 4.7s4.3 6.9 4.3 11.3c0 1.5-.2 3-.5 4.7h-25.6c.3 7.7 4 11.6 10.9 11.6 2.9 0 5.1-.7 6.5-2 1.5-1.4 2.5-3 3-4.9l6 .9zM394 51.3c.2-2.4-.4-4.7-1.8-6.9s-3.8-3.3-7-3.3c-3.1 0-5.3 1-6.9 3-1.5 2-2.5 4.4-2.8 7.2H394zm51 2.4c0 5-1.3 9.5-4 13.7s-6.9 6.2-12.7 6.2c-6 0-10.3-2.2-12.7-6.7-.1.4-.2 1.4-.4 2.9s-.3 2.5-.4 2.9h-7.3c.3-1.7.6-3.5.8-5.3.3-1.8.4-3.7.4-5.5V22.3h-6v-5.6H416v27c1.1-2.2 2.7-4.1 4.7-5.7 2-1.6 4.8-2.4 8.4-2.4 4.6 0 8.4 1.6 11.4 4.7 3 3.2 4.5 7.6 4.5 13.4zm-7.7.6c0-4.2-1-7.4-3-9.5-2-2.2-4.4-3.3-7.4-3.3-3.4 0-6 1.2-8 3.7-1.9 2.4-2.9 5-3 7.7V57c0 3 1 5.6 3 7.7s4.5 3.1 7.6 3.1c3.6 0 6.3-1.3 8.1-3.9 1.8-2.7 2.7-5.9 2.7-9.6zm69.2 18.5h-13.2v-7.2c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2 5.7 0 9.8 2.2 12.3 6.5V22.3h-8.6v-5.6h15.8v50.6h6v5.5zM493.2 56v-4.4c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm53.1-1.4c0 5.6-1.8 10.2-5.3 13.7s-8.2 5.3-13.9 5.3-10.1-1.7-13.4-5.1c-3.3-3.4-5-7.9-5-13.5 0-5.3 1.6-9.9 4.7-13.7 3.2-3.8 7.9-5.7 14.2-5.7s11 1.9 14.1 5.7c3 3.7 4.6 8.1 4.6 13.3zm-7.7-.2c0-4-1-7.2-3-9.5s-4.8-3.5-8.2-3.5c-3.6 0-6.4 1.2-8.3 3.7s-2.9 5.6-2.9 9.5c0 3.7.9 6.8 2.8 9.4 1.9 2.6 4.6 3.9 8.3 3.9 3.6 0 6.4-1.3 8.4-3.8 1.9-2.6 2.9-5.8 2.9-9.7zm45 5.8c-.4 3.2-1.9 6.3-4.4 9.1-2.5 2.9-6.4 4.3-11.8 4.3-5.2 0-9.4-1.6-12.6-4.8-3.2-3.2-4.8-7.7-4.8-13.7 0-5.5 1.6-10.1 4.7-13.9 3.2-3.8 7.6-5.7 13.2-5.7 2.3 0 4.6.3 6.7.8 2.2.5 4.2 1.5 6.2 2.9l1.5 9.5-5.9.7-1.3-6.1c-2.1-1.2-4.5-1.8-7.2-1.8-3.5 0-6.1 1.2-7.7 3.7-1.7 2.5-2.5 5.7-2.5 9.6 0 4.1.9 7.3 2.7 9.5 1.8 2.3 4.4 3.4 7.8 3.4 5.2 0 8.2-2.9 9.2-8.8l6.2 1.3zm34.7 1.9c0 3.6-1.5 6.5-4.6 8.5s-7 3-11.7 3c-5.7 0-10.6-1.2-14.6-3.6l1.2-8.8 5.7.6-.2 4.7c1.1.5 2.3.9 3.6 1.1s2.6.3 3.9.3c2.4 0 4.5-.4 6.5-1.3 1.9-.9 2.9-2.2 2.9-4.1 0-1.8-.8-3.1-2.3-3.8s-3.5-1.3-5.8-1.7-4.6-.9-6.9-1.4c-2.3-.6-4.2-1.6-5.7-2.9-1.6-1.4-2.3-3.5-2.3-6.3 0-4.1 1.5-6.9 4.6-8.5s6.4-2.4 9.9-2.4c2.6 0 5 .3 7.2.9 2.2.6 4.3 1.4 6.1 2.4l.8 8.8-5.8.7-.8-5.7c-2.3-1-4.7-1.6-7.2-1.6-2.1 0-3.7.4-5.1 1.1-1.3.8-2 2-2 3.8 0 1.7.8 2.9 2.3 3.6 1.5.7 3.4 1.2 5.7 1.6 2.2.4 4.5.8 6.7 1.4 2.2.6 4.1 1.6 5.7 3 1.4 1.6 2.2 3.7 2.2 6.6zM197.6 73.2h-17.1v-5.5h3.8V51.9c0-3.7-.7-6.3-2.1-7.9-1.4-1.6-3.3-2.3-5.7-2.3-3.2 0-5.6 1.1-7.2 3.4s-2.4 4.6-2.5 6.9v15.6h6v5.5h-17.1v-5.5h3.8V51.9c0-3.8-.7-6.4-2.1-7.9-1.4-1.5-3.3-2.3-5.6-2.3-3.2 0-5.5 1.1-7.2 3.3-1.6 2.2-2.4 4.5-2.5 6.9v15.8h6.9v5.5h-20.2v-5.5h6V42.4h-6.1v-5.6h13.4v6.4c1.2-2.1 2.7-3.8 4.7-5.2 2-1.3 4.4-2 7.3-2s5.3.7 7.5 2.1c2.2 1.4 3.7 3.5 4.5 6.4 1.1-2.5 2.7-4.5 4.9-6.1s4.8-2.4 7.9-2.4c3.5 0 6.5 1.1 8.9 3.3s3.7 5.6 3.7 10.2v18.2h6.1v5.5zm42.5 0h-13.2V66c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2s9.8 2.2 12.3 6.5V22.7h-8.6v-5.6h15.8v50.6h6v5.5zm-13.3-16.8V52c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm61.5 16.8H269v-5.5h6V51.9c0-3.7-.7-6.3-2.2-7.9-1.4-1.6-3.4-2.3-5.7-2.3-3.1 0-5.6 1-7.4 3s-2.8 4.4-2.9 7v15.9h6v5.5h-19.3v-5.5h6V42.4h-6.2v-5.6h13.6V43c2.6-4.6 6.8-6.9 12.7-6.9 3.6 0 6.7 1.1 9.2 3.3s3.7 5.6 3.7 10.2v18.2h6v5.4h-.2z" class="logo-text"></path></svg></a><button title="Open main menu" type="button" class="button action has-icon main-menu-toggle" aria-haspopup="menu" aria-label="Open main menu" aria-expanded="false"><span class="button-wrap"><span class="icon icon-menu "></span><span class="visually-hidden">Open main menu</span></span></button></div><div class="top-navigation-main"><nav class="main-nav" aria-label="Main menu"><ul class="main-menu nojs"><li class="top-level-entry-container "><button type="button" id="references-button" class="top-level-entry menu-toggle" aria-controls="references-menu" aria-expanded="false">References</button><a href="/en-US/docs/Web" class="top-level-entry">References</a><ul id="references-menu" class="submenu references hidden inline-submenu-lg" aria-labelledby="references-button"><li class="apis-link-container mobile-only "><a href="/en-US/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li><li class="html-link-container "><a href="/en-US/docs/Web/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Structure of content on the web</p></div></a></li><li class="css-link-container "><a href="/en-US/docs/Web/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Code used to describe document style</p></div></a></li><li class="javascript-link-container "><a href="/en-US/docs/Web/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">General-purpose scripting language</p></div></a></li><li class="http-link-container "><a href="/en-US/docs/Web/HTTP" class="submenu-item "><div class="submenu-icon http"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP</div><p class="submenu-item-description">Protocol for transmitting web resources</p></div></a></li><li class="apis-link-container "><a href="/en-US/docs/Web/API" class="submenu-item "><div class="submenu-icon apis"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web APIs</div><p class="submenu-item-description">Interfaces for building web applications</p></div></a></li><li class="apis-link-container "><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Extensions</div><p class="submenu-item-description">Developing extensions for web browsers</p></div></a></li><li class="apis-link-container desktop-only "><a href="/en-US/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li></ul></li><li class="top-level-entry-container active"><button type="button" id="guides-button" class="top-level-entry menu-toggle" aria-controls="guides-menu" aria-expanded="false">Guides</button><a href="/en-US/docs/Learn" class="top-level-entry">Guides</a><ul id="guides-menu" class="submenu guides hidden inline-submenu-lg" aria-labelledby="guides-button"><li class="apis-link-container mobile-only "><a href="/en-US/docs/Learn" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="apis-link-container desktop-only "><a href="/en-US/docs/Learn" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="html-link-container "><a href="/en-US/docs/Learn/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Learn to structure web content with HTML</p></div></a></li><li class="css-link-container "><a href="/en-US/docs/Learn/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Learn to style content using CSS</p></div></a></li><li class="javascript-link-container "><a href="/en-US/docs/Learn/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">Learn to run scripts in the browser</p></div></a></li><li class=" "><a href="/en-US/docs/Web/Accessibility" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Accessibility</div><p class="submenu-item-description">Learn to make the web accessible to all</p></div></a></li></ul></li><li class="top-level-entry-container "><button type="button" id="mdn-plus-button" class="top-level-entry menu-toggle" aria-controls="mdn-plus-menu" aria-expanded="false">Plus</button><a href="/en-US/plus" class="top-level-entry">Plus</a><ul id="mdn-plus-menu" class="submenu mdn-plus hidden inline-submenu-lg" aria-labelledby="mdn-plus-button"><li class=" "><a href="/en-US/plus" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview</div><p class="submenu-item-description">A customized MDN experience</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li><li class=" "><a href="/en-US/plus/updates" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Updates</div><p class="submenu-item-description">All browser compatibility updates at a glance</p></div></a></li><li class=" "><a href="/en-US/plus/docs/features/overview" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Documentation</div><p class="submenu-item-description">Learn how to use MDN Plus</p></div></a></li><li class=" "><a href="/en-US/plus/docs/faq" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">FAQ</div><p class="submenu-item-description">Frequently asked questions about MDN Plus</p></div></a></li></ul></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/curriculum/">Curriculum <sup class="new">New</sup></a></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/blog/">Blog</a></li><li class="top-level-entry-container "><button type="button" id="tools-button" class="top-level-entry menu-toggle" aria-controls="tools-menu" aria-expanded="false">Tools</button><ul id="tools-menu" class="submenu tools hidden inline-submenu-lg" aria-labelledby="tools-button"><li class=" "><a href="/en-US/play" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Playground</div><p class="submenu-item-description">Write, test and share your code</p></div></a></li><li class=" "><a href="/en-US/observatory" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP Observatory</div><p class="submenu-item-description">Scan a website for free</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li></ul></li></ul></nav><div class="header-search"><form action="/en-US/search" class="search-form search-widget" id="top-nav-search-form" role="search"><label id="top-nav-search-label" for="top-nav-search-input" class="visually-hidden">Search MDN</label><input aria-activedescendant="" aria-autocomplete="list" aria-controls="top-nav-search-menu" aria-expanded="false" aria-labelledby="top-nav-search-label" autoComplete="off" id="top-nav-search-input" role="combobox" type="search" class="search-input-field" name="q" placeholder="   " required="" value=""/><button type="button" class="button action has-icon clear-search-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear search input</span></span></button><button type="submit" class="button action has-icon search-button"><span class="button-wrap"><span class="icon icon-search "></span><span class="visually-hidden">Search</span></span></button><div id="top-nav-search-menu" role="listbox" aria-labelledby="top-nav-search-label"></div></form></div><div class="theme-switcher-menu"><button type="button" class="button action has-icon theme-switcher-menu small" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-theme-os-default "></span>Theme</span></button></div><ul class="auth-container"><li><a href="/users/fxa/login/authenticate/?next=%2Fen-US%2Fdocs%2FLearn%2FTools_and_testing%2FClient-side_JavaScript_frameworks%2FSvelte_reactivity_lifecycle_accessibility" class="login-link" rel="nofollow">Log in</a></li><li><a href="/users/fxa/login/authenticate/?next=%2Fen-US%2Fdocs%2FLearn%2FTools_and_testing%2FClient-side_JavaScript_frameworks%2FSvelte_reactivity_lifecycle_accessibility" target="_self" rel="nofollow" class="button primary mdn-plus-subscribe-link"><span class="button-wrap">Sign up for free</span></a></li></ul></div></div></header><div class="article-actions-container"><div class="container"><button type="button" class="button action has-icon sidebar-button" aria-label="Expand sidebar" aria-expanded="false" aria-controls="sidebar-quicklinks"><span class="button-wrap"><span class="icon icon-sidebar "></span></span></button><nav class="breadcrumbs-container" aria-label="Breadcrumb"><ol typeof="BreadcrumbList" vocab="https://schema.org/" aria-label="breadcrumbs"><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Guides</span></a><meta property="position" content="1"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn/Tools_and_testing" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Tools and testing</span></a><meta property="position" content="2"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Understanding client-side JavaScript frameworks</span></a><meta property="position" content="3"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility" class="breadcrumb-current-page" property="item" typeof="WebPage"><span property="name">Advanced Svelte: Reactivity, lifecycle, accessibility</span></a><meta property="position" content="4"/></li></ol></nav><div class="article-actions"><button type="button" class="button action has-icon article-actions-toggle" aria-label="Article actions"><span class="button-wrap"><span class="icon icon-ellipses "></span><span class="article-actions-dialog-heading">Article Actions</span></span></button><ul class="article-actions-entries"><li class="article-actions-entry"><div class="languages-switcher-menu open-on-focus-within"><button id="languages-switcher-button" type="button" class="button action small has-icon languages-switcher-menu" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-language "></span>English (US)</span></button><div class="hidden"><ul class="submenu language-menu " aria-labelledby="language-menu-button"><li class=" "><form class="submenu-item locale-redirect-setting"><div class="group"><label class="switch"><input type="checkbox" name="locale-redirect"/><span class="slider"></span><span class="label">Remember language</span></label><a href="https://github.com/orgs/mdn/discussions/739" rel="external noopener noreferrer" target="_blank" title="Enable this setting to automatically switch to this language when it&#x27;s available. (Click to learn more.)"><span class="icon icon-question-mark "></span></a></div></form></li><li class=" "><a data-locale="de" href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility" class="button submenu-item"><span>Deutsch</span><span title="Diese Übersetzung ist Teil eines Experiments."><span class="icon icon-experimental "></span></span></a></li><li class=" "><a data-locale="zh-CN" href="/zh-CN/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility" class="button submenu-item"><span>中文 (简体)</span></a></li></ul></div></div></li></ul></div></div></div></div><div class="main-wrapper"><div class="sidebar-container"><aside id="sidebar-quicklinks" class="sidebar" data-macro="LearnSidebar"><button type="button" class="button action backdrop" aria-label="Collapse sidebar"><span class="button-wrap"></span></button><nav aria-label="Related Topics" class="sidebar-inner"><header class="sidebar-actions"><section class="sidebar-filter-container"><div class="sidebar-filter "><label id="sidebar-filter-label" class="sidebar-filter-label" for="sidebar-filter-input"><span class="icon icon-filter"></span><span class="visually-hidden">Filter sidebar</span></label><input id="sidebar-filter-input" autoComplete="off" class="sidebar-filter-input-field false" type="text" placeholder="Filter" value=""/><button type="button" class="button action has-icon clear-sidebar-filter-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear filter input</span></span></button></div></section></header><div class="sidebar-inner-nav"><div class="in-nav-toc"><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In this article</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#code_along_with_us">Code along with us</a></li><li class="document-toc-item "><a class="document-toc-link" href="#working_on_the_moreactions_component">Working on the MoreActions component</a></li><li class="document-toc-item "><a class="document-toc-link" href="#reactivity_gotchas_updating_objects_and_arrays">Reactivity gotchas: updating objects and arrays</a></li><li class="document-toc-item "><a class="document-toc-link" href="#finishing_our_moreactions_component">Finishing our MoreActions component</a></li><li class="document-toc-item "><a class="document-toc-link" href="#working_with_the_dom_focusing_on_the_details">Working with the DOM: focusing on the details</a></li><li class="document-toc-item "><a class="document-toc-link" href="#exploring_keyboard_accessibility_issues_in_our_to-do_app">Exploring keyboard accessibility issues in our to-do app</a></li><li class="document-toc-item "><a class="document-toc-link" href="#creating_a_newtodo_component">Creating a NewTodo component</a></li><li class="document-toc-item "><a class="document-toc-link" href="#working_with_dom_nodes_using_the_bindthisdom_node_directive">Working with DOM nodes using the <code>bind:this={dom_node}</code> directive</a></li><li class="document-toc-item "><a class="document-toc-link" href="#component_lifecycle_and_the_onmount_function">Component lifecycle, and the <code>onMount()</code> function</a></li><li class="document-toc-item "><a class="document-toc-link" href="#waiting_for_the_dom_to_be_updated_with_the_tick_function">Waiting for the DOM to be updated with the <code>tick()</code> function</a></li><li class="document-toc-item "><a class="document-toc-link" href="#adding_functionality_to_html_elements_with_the_useaction_directive">Adding functionality to HTML elements with the <code>use:action</code> directive</a></li><li class="document-toc-item "><a class="document-toc-link" href="#component_binding_exposing_component_methods_and_variables_using_the_bindthiscomponent_directive">Component binding: exposing component methods and variables using the <code>bind:this={component}</code> directive</a></li><li class="document-toc-item "><a class="document-toc-link" href="#the_code_so_far">The code so far</a></li><li class="document-toc-item "><a class="document-toc-link" href="#summary">Summary</a></li></ul></section></div></div><div class="sidebar-body"><ol><li class="section"><a href="/en-US/docs/Learn/Getting_started_with_the_web">Complete beginners start here!</a></li><li><details><summary>Getting started with the web</summary><ol><li><a href="/en-US/docs/Learn/Getting_started_with_the_web">Getting started with the web</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/Installing_basic_software">Installing basic software</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/What_will_your_website_look_like">What will your website look like?</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/Dealing_with_files">Dealing with files</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics">HTML basics</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics">CSS basics</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics">JavaScript basics</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/Publishing_your_website">Publishing your website</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/How_the_Web_works">How the web works</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/HTML">HTML — Structuring the web</a></li><li><details><summary>Introduction to HTML</summary><ol><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML">Introduction to HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started">Getting started with HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML">What's in the head? Metadata in HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/HTML_text_fundamentals">HTML text fundamentals</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks">Creating hyperlinks</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formatting">Advanced text formatting</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure">Document and website structure</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Debugging_HTML">Debugging HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Marking_up_a_letter">Marking up a letter</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Structuring_a_page_of_content">Structuring a page of content</a></li></ol></details></li><li><details><summary>Multimedia and embedding</summary><ol><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding">Multimedia and embedding</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML">Images in HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content">Video and audio content</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Other_embedding_technologies">From object to iframe — other embedding technologies</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web">Adding vector graphics to the web</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">Responsive images</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Mozilla_splash_page">Mozilla splash page</a></li></ol></details></li><li><details><summary>HTML tables</summary><ol><li><a href="/en-US/docs/Learn/HTML/Tables">HTML tables</a></li><li><a href="/en-US/docs/Learn/HTML/Tables/Basics">HTML table basics</a></li><li><a href="/en-US/docs/Learn/HTML/Tables/Advanced">HTML table advanced features and accessibility</a></li><li><a href="/en-US/docs/Learn/HTML/Tables/Structuring_planet_data">Structuring planet data</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/CSS">CSS — Styling the web</a></li><li><details><summary>CSS first steps</summary><ol><li><a href="/en-US/docs/Learn/CSS/First_steps">CSS first steps</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/What_is_CSS">What is CSS?</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/Getting_started">Getting started with CSS</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/How_CSS_is_structured">How CSS is structured</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/How_CSS_works">How CSS works</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/Styling_a_biography_page">Styling a biography page</a></li></ol></details></li><li><details><summary>CSS building blocks</summary><ol><li><a href="/en-US/docs/Learn/CSS/Building_blocks">CSS building blocks</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors">CSS selectors</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Type_Class_and_ID_Selectors">Type, class, and ID selectors</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Attribute_selectors">Attribute selectors</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements">Pseudo-classes and pseudo-elements</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators">Combinators</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance">Cascade, specificity, and inheritance</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers">Cascade layers</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/The_box_model">The box model</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Backgrounds_and_borders">Backgrounds and borders</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Handling_different_text_directions">Handling different text directions</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Overflowing_content">Overflowing content</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Values_and_units">CSS values and units</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Sizing_items_in_CSS">Sizing items in CSS</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Images_media_form_elements">Images, media, and form elements</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Styling_tables">Styling tables</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Advanced_styling_effects">Advanced styling effects</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Debugging_CSS">Debugging CSS</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Organizing">Organizing your CSS</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Fundamental_CSS_comprehension">Fundamental CSS comprehension</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Creating_fancy_letterheaded_paper">Creating fancy letterheaded paper</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/A_cool_looking_box">A cool-looking box</a></li></ol></details></li><li><details><summary>Styling text</summary><ol><li><a href="/en-US/docs/Learn/CSS/Styling_text">CSS styling text</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Fundamentals">Fundamental text and font styling</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Styling_lists">Styling lists</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Styling_links">Styling links</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Web_fonts">Web fonts</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Typesetting_a_homepage">Typesetting a community school homepage</a></li></ol></details></li><li><details><summary>CSS layout</summary><ol><li><a href="/en-US/docs/Learn/CSS/CSS_layout">CSS layout</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Introduction">Introduction to CSS layout</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Normal_Flow">Normal Flow</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Flexbox">Flexbox</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Grids">Grids</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Floats">Floats</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Positioning">Positioning</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Multiple-column_Layout">Multiple-column layout</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design">Responsive design</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Media_queries">Beginner's guide to media queries</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods">Legacy layout methods</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers">Supporting older browsers</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Fundamental_Layout_Comprehension">Fundamental layout comprehension</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/JavaScript">JavaScript — Dynamic client-side scripting</a></li><li><details><summary>JavaScript first steps</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/First_steps">JavaScript first steps</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript">What is JavaScript?</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/A_first_splash">A first splash into JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong">What went wrong? Troubleshooting JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Variables">Storing the information you need — Variables</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Math">Basic math in JavaScript — numbers and operators</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings">Handling text — strings in JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods">Useful string methods</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays">Arrays</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator">Silly story generator</a></li></ol></details></li><li><details><summary>JavaScript building blocks</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks">JavaScript building blocks</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/conditionals">Making decisions in your code — conditionals</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code">Looping code</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions">Functions — reusable blocks of code</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Build your own function</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">Function return values</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events">Introduction to events</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Event_bubbling">Event bubbling</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Image_gallery">Image gallery</a></li></ol></details></li><li><details><summary>Introducing JavaScript objects</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Objects">Introducing JavaScript objects</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics">JavaScript object basics</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">Object prototypes</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_programming">Object-oriented programming</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript">Classes in JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON">Working with JSON</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_building_practice">Object building practice</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">Adding features to our bouncing balls demo</a></li></ol></details></li><li><details><summary>Asynchronous JavaScript</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous">Asynchronous JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">How to use promises</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Implementing_a_promise-based_API">How to implement a promise-based API</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing_workers">Introducing workers</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Sequencing_animations">Sequencing animations</a></li></ol></details></li><li><details><summary>Client-side web APIs</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs">Client-side web APIs</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Introduction to web APIs</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">Third-party APIs</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing graphics</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">Video and Audio APIs</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">Client-side storage</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Forms">Web forms — Working with user data</a></li><li><details><summary>Web form building blocks</summary><ol><li><a href="/en-US/docs/Learn/Forms">Web form building blocks</a></li><li><a href="/en-US/docs/Learn/Forms/Your_first_form">Your first form</a></li><li><a href="/en-US/docs/Learn/Forms/How_to_structure_a_web_form">How to structure a web form</a></li><li><a href="/en-US/docs/Learn/Forms/Basic_native_form_controls">Basic native form controls</a></li><li><a href="/en-US/docs/Learn/Forms/HTML5_input_types">The HTML5 input types</a></li><li><a href="/en-US/docs/Learn/Forms/Other_form_controls">Other form controls</a></li><li><a href="/en-US/docs/Learn/Forms/Styling_web_forms">Styling web forms</a></li><li><a href="/en-US/docs/Learn/Forms/Advanced_form_styling">Advanced form styling</a></li><li><a href="/en-US/docs/Learn/Forms/UI_pseudo-classes">UI pseudo-classes</a></li><li><a href="/en-US/docs/Learn/Forms/Form_validation">Client-side form validation</a></li><li><a href="/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data">Sending form data</a></li></ol></details></li><li><details><summary>Advanced web form techniques</summary><ol><li><a href="/en-US/docs/Learn/Forms/How_to_build_custom_form_controls">How to build custom form controls</a></li><li><a href="/en-US/docs/Learn/Forms/Sending_forms_through_JavaScript">Sending forms through JavaScript</a></li><li><a href="/en-US/docs/Learn/Forms/Property_compatibility_table_for_form_controls">CSS property compatibility table for form controls</a></li><li><a href="/en-US/docs/Learn/Forms/HTML_forms_in_legacy_browsers">HTML forms in legacy browsers</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Accessibility">Accessibility — Make the web usable by everyone</a></li><li><details><summary>Accessibility guides</summary><ol><li><a href="/en-US/docs/Learn/Accessibility">Accessibility</a></li><li><a href="/en-US/docs/Learn/Accessibility/What_is_accessibility">What is accessibility?</a></li><li><a href="/en-US/docs/Learn/Accessibility/HTML">HTML: A good basis for accessibility</a></li><li><a href="/en-US/docs/Learn/Accessibility/CSS_and_JavaScript">CSS and JavaScript accessibility best practices</a></li><li><a href="/en-US/docs/Learn/Accessibility/WAI-ARIA_basics">WAI-ARIA basics</a></li><li><a href="/en-US/docs/Learn/Accessibility/Multimedia">Accessible multimedia</a></li><li><a href="/en-US/docs/Learn/Accessibility/Mobile">Mobile accessibility</a></li><li><a href="/en-US/docs/Learn/Accessibility/Accessibility_troubleshooting">Assessment: Accessibility troubleshooting</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Performance">Performance — Making websites fast and responsive</a></li><li><details><summary>Performance guides</summary><ol><li><a href="/en-US/docs/Learn/Performance">Web performance</a></li><li><a href="/en-US/docs/Learn/Performance/why_web_performance">The "why" of web performance</a></li><li><a href="/en-US/docs/Learn/Performance/What_is_web_performance">What is web performance?</a></li><li><a href="/en-US/docs/Learn/Performance/Perceived_performance">Perceived performance</a></li><li><a href="/en-US/docs/Learn/Performance/Measuring_performance">Measuring performance</a></li><li><a href="/en-US/docs/Learn/Performance/Multimedia">Multimedia: Images</a></li><li><a href="/en-US/docs/Learn/Performance/video">Multimedia: video</a></li><li><a href="/en-US/docs/Learn/Performance/JavaScript">JavaScript performance optimization</a></li><li><a href="/en-US/docs/Learn/Performance/HTML">HTML performance optimization</a></li><li><a href="/en-US/docs/Learn/Performance/CSS">CSS performance optimization</a></li><li><a href="/en-US/docs/Learn/Performance/business_case_for_performance">The business case for web performance</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/MathML">MathML — Writing mathematics with MathML</a></li><li><details><summary>MathML first steps</summary><ol><li><a href="/en-US/docs/Learn/MathML/First_steps">MathML first steps</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Getting_started">Getting started with MathML</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Text_containers">MathML Text Containers</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Fractions_and_roots">MathML fractions and roots</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Scripts">MathML scripted elements</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Tables">MathML tables</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Three_famous_mathematical_formulas">Three famous mathematical formulas</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/../Games">Games — Developing games for the web</a></li><li><details><summary>Guides and tutorials</summary><ol><li><a href="/en-US/docs/Games/Introduction">Introduction to game development for the Web</a></li><li><a href="/en-US/docs/Games/Techniques">Techniques for game development</a></li><li><a href="/en-US/docs/Games/Tutorials">Tutorials</a></li><li><a href="/en-US/docs/Games/Publishing_games">Publishing games</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Tools_and_testing">Tools and testing</a></li><li><details><summary>Client-side web development tools</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools">Understanding client-side web development tools</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview">Client-side tooling overview</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line">Command line crash course</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management">Package management basics</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Introducing_complete_toolchain">Introducing a complete toolchain</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Deployment">Deploying our app</a></li></ol></details></li><li><details><summary>Introduction to client-side frameworks</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction">Introduction to client-side frameworks</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features">Framework main features</a></li></ol></details></li><li><details><summary>React</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">Getting started with React</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning">Beginning our React todo list</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components">Componentizing our React app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state">React interactivity: Events and state</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility">Accessibility in React</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources">React resources</a></li></ol></details></li><li><details><summary>Ember</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started">Getting started with Ember</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization">Ember app structure and componentization</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state">Ember interactivity: Events, classes and state</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer">Ember Interactivity: Footer functionality, conditional rendering</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing">Routing in Ember</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources">Ember resources and troubleshooting</a></li></ol></details></li><li><details><summary>Vue</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started">Getting started with Vue</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component">Creating our first Vue component</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists">Rendering a list of Vue components</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models">Adding a new todo form: Vue events, methods, and models</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling">Styling Vue components with CSS</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties">Using Vue computed properties</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering">Vue conditional rendering: editing existing todos</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management">Vue refs and lifecycle methods for focus management</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources">Vue resources</a></li></ol></details></li><li><details open=""><summary>Svelte</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started">Getting started with Svelte</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning">Starting our Svelte to-do list app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props">Dynamic behavior in Svelte: working with variables and props</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components">Componentizing our Svelte app</a></li><li><em><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility" aria-current="page">Advanced Svelte: Reactivity, lifecycle, accessibility</a></em></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores">Working with Svelte stores</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript">TypeScript support in Svelte</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next">Deployment and next steps</a></li></ol></details></li><li><details><summary>Angular</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started">Getting started with Angular</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning">Beginning our Angular todo list app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling">Styling our Angular app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component">Creating an item component</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering">Filtering our to-do items</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building">Building Angular applications and further resources</a></li></ol></details></li><li><details><summary>Git and GitHub</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/GitHub">Git and GitHub</a></li></ol></details></li><li><details><summary>Cross browser testing</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing">Cross browser testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Introduction">Introduction to cross-browser testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies">Strategies for carrying out testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS">Handling common HTML and CSS problems</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript">Handling common JavaScript problems</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility">Handling common accessibility problems</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection">Implementing feature detection</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Automated_testing">Introduction to automated testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Setting up your own test automation environment</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Server-side">Server-side website programming</a></li><li><details><summary>First steps</summary><ol><li><a href="/en-US/docs/Learn/Server-side/First_steps">Server-side website programming first steps</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Introduction">Introduction to the server side</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview">Client-Server Overview</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Web_frameworks">Server-side web frameworks</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">Website security</a></li></ol></details></li><li><details><summary>Django web framework (Python)</summary><ol><li><a href="/en-US/docs/Learn/Server-side/Django">Django Web Framework (Python)</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">Assessment: DIY Django mini blog</a></li></ol></details></li><li><details><summary>Express Web Framework (Node.js/JavaScript)</summary><ol><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs">Express web framework (Node.js/JavaScript)</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node development environment</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Common_questions">Further resources</a></li><li><details><summary>Common questions</summary><ol><li><a href="/en-US/docs/Learn/Common_questions">Common questions</a></li><li><a href="/en-US/docs/Learn/HTML/Howto">Use HTML to solve common problems</a></li><li><a href="/en-US/docs/Learn/CSS/Howto">Use CSS to solve common problems</a></li><li><a href="/en-US/docs/Learn/JavaScript/Howto">Solve common problems in your JavaScript code</a></li><li><a href="/en-US/docs/Learn/Common_questions/Web_mechanics">Web mechanics</a></li><li><a href="/en-US/docs/Learn/Common_questions/Tools_and_setup">Tools and setup</a></li><li><a href="/en-US/docs/Learn/Common_questions/Design_and_accessibility">Design and accessibility</a></li></ol></details></li></ol></div></div><section class="place side"></section></nav></aside><div class="toc-container"><aside class="toc"><nav><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In this article</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#code_along_with_us">Code along with us</a></li><li class="document-toc-item "><a class="document-toc-link" href="#working_on_the_moreactions_component">Working on the MoreActions component</a></li><li class="document-toc-item "><a class="document-toc-link" href="#reactivity_gotchas_updating_objects_and_arrays">Reactivity gotchas: updating objects and arrays</a></li><li class="document-toc-item "><a class="document-toc-link" href="#finishing_our_moreactions_component">Finishing our MoreActions component</a></li><li class="document-toc-item "><a class="document-toc-link" href="#working_with_the_dom_focusing_on_the_details">Working with the DOM: focusing on the details</a></li><li class="document-toc-item "><a class="document-toc-link" href="#exploring_keyboard_accessibility_issues_in_our_to-do_app">Exploring keyboard accessibility issues in our to-do app</a></li><li class="document-toc-item "><a class="document-toc-link" href="#creating_a_newtodo_component">Creating a NewTodo component</a></li><li class="document-toc-item "><a class="document-toc-link" href="#working_with_dom_nodes_using_the_bindthisdom_node_directive">Working with DOM nodes using the <code>bind:this={dom_node}</code> directive</a></li><li class="document-toc-item "><a class="document-toc-link" href="#component_lifecycle_and_the_onmount_function">Component lifecycle, and the <code>onMount()</code> function</a></li><li class="document-toc-item "><a class="document-toc-link" href="#waiting_for_the_dom_to_be_updated_with_the_tick_function">Waiting for the DOM to be updated with the <code>tick()</code> function</a></li><li class="document-toc-item "><a class="document-toc-link" href="#adding_functionality_to_html_elements_with_the_useaction_directive">Adding functionality to HTML elements with the <code>use:action</code> directive</a></li><li class="document-toc-item "><a class="document-toc-link" href="#component_binding_exposing_component_methods_and_variables_using_the_bindthiscomponent_directive">Component binding: exposing component methods and variables using the <code>bind:this={component}</code> directive</a></li><li class="document-toc-item "><a class="document-toc-link" href="#the_code_so_far">The code so far</a></li><li class="document-toc-item "><a class="document-toc-link" href="#summary">Summary</a></li></ul></section></div></nav></aside><section class="place side"></section></div></div><main id="content" class="main-content "><article class="main-page-content" lang="en-US"><header><h1>Advanced Svelte: Reactivity, lifecycle, accessibility</h1></header><div class="section-content"><ul class="prev-next"> <li><a class="button secondary" href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components"><span class="button-wrap"> Previous </span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks"><span class="button-wrap"> Overview: Understanding client-side JavaScript frameworks</span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores"><span class="button-wrap"> Next </span></a></li> </ul> <p>In the last article we added more features to our to-do list and started to organize our app into components. In this article we will add the app's final features and further componentize our app. We will learn how to deal with reactivity issues related to updating objects and arrays. To avoid common pitfalls, we'll have to dig a little deeper into Svelte's reactivity system. We'll also look at solving some accessibility focus issues, and more besides.</p> <figure class="table-container"><table> <tbody> <tr> <th scope="row">Prerequisites:</th> <td> <p> At minimum, it is recommended that you are familiar with the core <a href="/en-US/docs/Learn/HTML">HTML</a>, <a href="/en-US/docs/Learn/CSS">CSS</a>, and <a href="/en-US/docs/Learn/JavaScript">JavaScript</a> languages, and have knowledge of the <a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line">terminal/command line</a>. </p> <p> You'll need a terminal with node and npm installed to compile and build your app. </p> </td> </tr> <tr> <th scope="row">Objective:</th> <td> Learn some advanced Svelte techniques involving solving reactivity issues, keyboard accessibility problems to do with component lifecycle, and more. </td> </tr> </tbody> </table></figure> <p>We'll focus on some accessibility issues involving focus management. To do so, we'll utilize some techniques for accessing DOM nodes and executing methods like <a href="/en-US/docs/Web/API/HTMLElement/focus"><code>focus()</code></a> and <a href="/en-US/docs/Web/API/HTMLInputElement/select"><code>select()</code></a>. We will also see how to declare and clean up event listeners on DOM elements.</p> <p>We also need to learn a bit about component lifecycle to understand when these DOM nodes get mounted and unmounted from the DOM and how we can access them. We will also learn about the <code>action</code> directive, which will allow us to extend the functionality of HTML elements in a reusable and declarative way.</p> <p>Finally, we will learn a bit more about components. So far, we've seen how components can share data using props, and communicate with their parents using events and two-way data binding. Now we will see how components can also expose methods and variables.</p> <p>The following new components will be developed throughout the course of this article:</p> <ul> <li><code>MoreActions</code>: Displays the <em>Check All</em> and <em>Remove Completed</em> buttons, and emits the corresponding events required to handle their functionality.</li> <li><code>NewTodo</code>: Displays the <code>&lt;input&gt;</code> field and <em>Add</em> button for adding a new to-do.</li> <li><code>TodosStatus</code>: Displays the "x out of y items completed" status heading.</li> </ul></div><section aria-labelledby="code_along_with_us"><h2 id="code_along_with_us"><a href="#code_along_with_us">Code along with us</a></h2><div class="section-content"></div></section><section aria-labelledby="git"><h3 id="git"><a href="#git">Git</a></h3><div class="section-content"><p>Clone the GitHub repo (if you haven't already done it) with:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>git clone https://github.com/opensas/mdn-svelte-tutorial.git </code></pre></div> <p>Then to get to the current app state, run</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>cd mdn-svelte-tutorial/05-advanced-concepts </code></pre></div> <p>Or directly download the folder's content:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>npx degit opensas/mdn-svelte-tutorial/05-advanced-concepts </code></pre></div> <p>Remember to run <code>npm install &amp;&amp; npm run dev</code> to start your app in development mode.</p></div></section><section aria-labelledby="repl"><h3 id="repl"><a href="#repl">REPL</a></h3><div class="section-content"><p>To code along with us using the REPL, start at</p> <p><a href="https://svelte.dev/repl/76cc90c43a37452e8c7f70521f88b698?version=3.23.2" class="external" target="_blank">https://svelte.dev/repl/76cc90c43a37452e8c7f70521f88b698?version=3.23.2</a></p></div></section><section aria-labelledby="working_on_the_moreactions_component"><h2 id="working_on_the_moreactions_component"><a href="#working_on_the_moreactions_component">Working on the MoreActions component</a></h2><div class="section-content"><p>Now we'll tackle the <em>Check All</em> and <em>Remove Completed</em> buttons. Let's create a component that will be in charge of displaying the buttons and emitting the corresponding events.</p> <ol> <li> <p>Create a new file, <code>components/MoreActions.svelte</code>.</p> </li> <li> <p>When the first button is clicked, we'll emit a <code>checkAll</code> event to signal that all the to-dos should be checked/unchecked. When the second button is clicked, we'll emit a <code>removeCompleted</code> event to signal that all of the completed to-dos should be removed. Put the following content into your <code>MoreActions.svelte</code> file:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; import { createEventDispatcher } from "svelte"; const dispatch = createEventDispatcher(); let completed = true; const checkAll = () =&gt; { dispatch("checkAll", completed); completed = !completed; }; const removeCompleted = () =&gt; dispatch("removeCompleted"); &lt;/script&gt; &lt;div class="btn-group"&gt; &lt;button type="button" class="btn btn__primary" on:click={checkAll}&gt;{completed ? 'Check' : 'Uncheck'} all&lt;/button&gt; &lt;button type="button" class="btn btn__primary" on:click={removeCompleted}&gt;Remove completed&lt;/button&gt; &lt;/div&gt; </code></pre></div> <p>We've also included a <code>completed</code> variable to toggle between checking and unchecking all tasks.</p> </li> <li> <p>Back over in <code>Todos.svelte</code>, we are going to import our <code>MoreActions</code> component and create two functions to handle the events emitted by the <code>MoreActions</code> component.</p> <p>Add the following import statement below the existing ones:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>import MoreActions from "./MoreActions.svelte"; </code></pre></div> </li> <li> <p>Then add the described functions at the end of the <code>&lt;script&gt;</code> section:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const checkAllTodos = (completed) =&gt; todos.forEach((t) =&gt; (t.completed = completed)); const removeCompletedTodos = () =&gt; (todos = todos.filter((t) =&gt; !t.completed)); </code></pre></div> </li> <li> <p>Now go to the bottom of the <code>Todos.svelte</code> markup section and replace the <code>&lt;div class="btn-group"&gt;</code> element that we copied into <code>MoreActions.svelte</code> with a call to the <code>MoreActions</code> component, like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;!-- MoreActions --&gt; &lt;MoreActions on:checkAll={(e) =&gt; checkAllTodos(e.detail)} on:removeCompleted={removeCompletedTodos} /&gt; </code></pre></div> </li> <li> <p>OK, let's go back into the app and try it out. You'll find that the <em>Remove Completed</em> button works fine, but the <em>Check All</em>/<em>Uncheck All</em> button just silently fails.</p> </li> </ol> <p>To find out what is happening here, we'll have to dig a little deeper into Svelte reactivity.</p></div></section><section aria-labelledby="reactivity_gotchas_updating_objects_and_arrays"><h2 id="reactivity_gotchas_updating_objects_and_arrays"><a href="#reactivity_gotchas_updating_objects_and_arrays">Reactivity gotchas: updating objects and arrays</a></h2><div class="section-content"><p>To see what's happening we can log the <code>todos</code> array from the <code>checkAllTodos()</code> function to the console.</p> <ol> <li> <p>Update your existing <code>checkAllTodos()</code> function to the following:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const checkAllTodos = (completed) =&gt; { todos.forEach((t) =&gt; (t.completed = completed)); console.log("todos", todos); }; </code></pre></div> </li> <li> <p>Go back to your browser, open your DevTools console, and click <em>Check All</em>/<em>Uncheck All</em> a few times.</p> </li> </ol> <p>You'll notice that the array is successfully updated every time you press the button (the <code>todo</code> objects' <code>completed</code> properties are toggled between <code>true</code> and <code>false</code>), but Svelte is not aware of that. This also means that in this case, a reactive statement like <code>$: console.log('todos', todos)</code> won't be very useful.</p> <p>To find out why this is happening, we need to understand how reactivity works in Svelte when updating arrays and objects.</p> <p>Many web frameworks use the virtual DOM technique to update the page. Basically, the virtual DOM is an in-memory copy of the contents of the web page. The framework updates this virtual representation, which is then synced with the "real" DOM. This is much faster than directly updating the DOM and allows the framework to apply many optimization techniques.</p> <p>These frameworks, by default, basically re-run all our JavaScript on every change against this virtual DOM, and apply different methods to cache expensive calculations and optimize execution. They make little to no attempt to understand what our JavaScript code is doing.</p> <p>Svelte doesn't use a virtual DOM representation. Instead, it parses and analyzes our code, creates a dependency tree, and then generates the required JavaScript to update only the parts of the DOM that need to be updated. This approach usually generates optimal JavaScript code with minimal overhead, but it also has its limitations.</p> <p>Sometimes Svelte cannot detect changes to variables being watched. Remember that to tell Svelte that a variable has changed, you have to assign it a new value. A simple rule to keep in mind is that <strong>The name of the updated variable must appear on the left-hand side of the assignment.</strong></p> <p>For example, in the following piece of code:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const foo = obj.foo; foo.bar = "baz"; </code></pre></div> <p>Svelte won't update references to <code>obj.foo.bar</code>, unless you follow it up with <code>obj = obj</code>. That's because Svelte can't track object references, so we have to explicitly tell it that <code>obj</code> has changed by issuing an assignment.</p> <div class="notecard note"> <p><strong>Note:</strong> If <code>foo</code> is a top-level variable, you can easily tell Svelte to update <code>obj</code> whenever <code>foo</code> is changed with the following reactive statement: <code>$: foo, obj = obj</code>. With this we are defining <code>foo</code> as a dependency, and whenever it changes Svelte will run <code>obj = obj</code>.</p> </div> <p>In our <code>checkAllTodos()</code> function, when we run:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>todos.forEach((t) =&gt; (t.completed = completed)); </code></pre></div> <p>Svelte will not mark <code>todos</code> as changed because it does not know that when we update our <code>t</code> variable inside the <code>forEach()</code> method, we are also modifying the <code>todos</code> array. And that makes sense, because otherwise Svelte would be aware of the inner workings of the <code>forEach()</code> method; the same would therefore be true for any method attached to any object or array.</p> <p>Nevertheless, there are different techniques that we can apply to solve this problem, and all of them involve assigning a new value to the variable being watched.</p> <p>As we already saw, we could just tell Svelte to update the variable with a self-assignment, like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const checkAllTodos = (completed) =&gt; { todos.forEach((t) =&gt; (t.completed = completed)); todos = todos; }; </code></pre></div> <p>This will solve the problem. Internally Svelte will flag <code>todos</code> as changed and remove the apparently redundant self-assignment. Apart from the fact that it looks weird, it's perfectly OK to use this technique, and sometimes it's the most concise way to do it.</p> <p>We could also access the <code>todos</code> array by index, like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const checkAllTodos = (completed) =&gt; { todos.forEach((t, i) =&gt; (todos[i].completed = completed)); }; </code></pre></div> <p>Assignments to properties of arrays and objects — e.g. <code>obj.foo += 1</code> or <code>array[i] = x</code> — work the same way as assignments to the values themselves. When Svelte analyzes this code, it can detect that the <code>todos</code> array is being modified.</p> <p>Another solution is to assign a new array to <code>todos</code> containing a copy of all the to-dos with the <code>completed</code> property updated accordingly, like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const checkAllTodos = (completed) =&gt; { todos = todos.map((t) =&gt; ({ ...t, completed })); }; </code></pre></div> <p>In this case we are using the <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map()</code></a> method, which returns a new array with the results of executing the provided function for each item. The function returns a copy of each to-do using <a href="/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax">spread syntax</a> and overwrites the property of the completed value accordingly. This solution has the added benefit of returning a new array with new objects, completely avoiding mutating the original <code>todos</code> array.</p> <div class="notecard note"> <p><strong>Note:</strong> Svelte allows us to specify different options that affect how the compiler works. The <code>&lt;svelte:options immutable={true}/&gt;</code> option tells the compiler that you promise not to mutate any objects. This allows it to be less conservative about checking whether values have changed and generate simpler and more performant code. For more information on <code>&lt;svelte:options&gt;</code>, check the <a href="https://svelte.dev/docs/special-elements#svelte-options" class="external" target="_blank">Svelte options documentation</a>.</p> </div> <p>All of these solutions involve an assignment in which the updated variable is on the left side of the equation. Any of this techniques will allow Svelte to notice that our <code>todos</code> array has been modified.</p> <p><strong>Choose one, and update your <code>checkAllTodos()</code> function as required. Now you should be able to check and uncheck all your to-dos at once. Try it!</strong></p></div></section><section aria-labelledby="finishing_our_moreactions_component"><h2 id="finishing_our_moreactions_component"><a href="#finishing_our_moreactions_component">Finishing our MoreActions component</a></h2><div class="section-content"><p>We will add one usability detail to our component. We'll disable the buttons when there are no tasks to be processed. To create this, we'll receive the <code>todos</code> array as a prop, and set the <code>disabled</code> property of each button accordingly.</p> <ol> <li> <p>Update your <code>MoreActions.svelte</code> component like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); export let todos; let completed = true; const checkAll = () =&gt; { dispatch('checkAll', completed); completed = !completed; } const removeCompleted = () =&gt; dispatch('removeCompleted'); $: completedTodos = todos.filter((t) =&gt; t.completed).length; &lt;/script&gt; &lt;div class="btn-group"&gt; &lt;button type="button" class="btn btn__primary" disabled={todos.length === 0} on:click={checkAll}&gt;{completed ? 'Check' : 'Uncheck'} all&lt;/button&gt; &lt;button type="button" class="btn btn__primary" disabled={completedTodos === 0} on:click={removeCompleted}&gt;Remove completed&lt;/button&gt; &lt;/div&gt; </code></pre></div> <p>We've also declared a reactive <code>completedTodos</code> variable to enable or disable the <em>Remove Completed</em> button.</p> </li> <li> <p>Don't forget to pass the prop into <code>MoreActions</code> from inside <code>Todos.svelte</code>, where the component is called:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;MoreActions {todos} on:checkAll={(e) =&gt; checkAllTodos(e.detail)} on:removeCompleted={removeCompletedTodos} /&gt; </code></pre></div> </li> </ol></div></section><section aria-labelledby="working_with_the_dom_focusing_on_the_details"><h2 id="working_with_the_dom_focusing_on_the_details"><a href="#working_with_the_dom_focusing_on_the_details">Working with the DOM: focusing on the details</a></h2><div class="section-content"><p>Now that we have completed all of the app's required functionality, we'll concentrate on some accessibility features that will improve the usability of our app for both keyboard-only and screen reader users.</p> <p>In its current state our app has a couple of keyboard accessibility problems involving focus management. Let's have a look at these issues.</p></div></section><section aria-labelledby="exploring_keyboard_accessibility_issues_in_our_to-do_app"><h2 id="exploring_keyboard_accessibility_issues_in_our_to-do_app"><a href="#exploring_keyboard_accessibility_issues_in_our_to-do_app">Exploring keyboard accessibility issues in our to-do app</a></h2><div class="section-content"><p>Right now, keyboard users will find out that the focus flow of our app is not very predictable or coherent.</p> <p>If you click on the input at the top of our app, you'll see a thick, dashed outline around that input. This outline is your visual indicator that the browser is currently focused on this element.</p> <p>If you are a mouse user, you might have skipped this visual hint. But if you are working exclusively with the keyboard, knowing which control has focus is of vital importance. It tells us which control is going to receive our keystrokes.</p> <p>If you press the <kbd>Tab</kbd> key repeatedly, you'll see the dashed focus indicator cycling between all the focusable elements on the page. If you move the focus to the <em>Edit</em> button and press <kbd>Enter</kbd>, suddenly the focus disappears, and you can no longer tell which control will receive our keystrokes.</p> <p>Moreover, if you press the <kbd>Escape</kbd> or <kbd>Enter</kbd> key, nothing happens. And if you click on <em>Cancel</em> or <em>Save</em>, the focus disappears again. For a user working with the keyboard, this behavior will be confusing at best.</p> <p>We'd also like to add some usability features, like disabling the <em>Save</em> button when required fields are empty, giving focus to certain HTML elements or auto-selecting contents when a text input receives focus.</p> <p>To implement all these features, we'll need programmatic access to DOM nodes to run functions like <a href="/en-US/docs/Web/API/HTMLElement/focus"><code>focus()</code></a> and <a href="/en-US/docs/Web/API/HTMLInputElement/select"><code>select()</code></a>. We will also have to use <a href="/en-US/docs/Web/API/EventTarget/addEventListener"><code>addEventListener()</code></a> and <a href="/en-US/docs/Web/API/EventTarget/removeEventListener"><code>removeEventListener()</code></a> to run specific tasks when the control receives focus.</p> <p>The problem is that all these DOM nodes are dynamically created by Svelte at runtime. So we'll have to wait for them to be created and added to the DOM in order to use them. To do so, we'll have to learn about the <a href="https://learn.svelte.dev/tutorial/onmount" class="external" target="_blank">component lifecycle</a> to understand when we can access them — more on this later.</p></div></section><section aria-labelledby="creating_a_newtodo_component"><h2 id="creating_a_newtodo_component"><a href="#creating_a_newtodo_component">Creating a NewTodo component</a></h2><div class="section-content"><p>Let's begin by extracting our new to-do form out to its own component. With what we know so far we can create a new component file and adjust the code to emit an <code>addTodo</code> event, passing the name of the new to-do in with the additional details.</p> <ol> <li> <p>Create a new file, <code>components/NewTodo.svelte</code>.</p> </li> <li> <p>Put the following contents inside it:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); let name = ''; const addTodo = () =&gt; { dispatch('addTodo', name); name = ''; } const onCancel = () =&gt; name = ''; &lt;/script&gt; &lt;form on:submit|preventDefault={addTodo} on:keydown={(e) =&gt; e.key === 'Escape' &amp;&amp; onCancel()}&gt; &lt;h2 class="label-wrapper"&gt; &lt;label for="todo-0" class="label__lg"&gt;What needs to be done?&lt;/label&gt; &lt;/h2&gt; &lt;input bind:value={name} type="text" id="todo-0" autoComplete="off" class="input input__lg" /&gt; &lt;button type="submit" disabled={!name} class="btn btn__primary btn__lg"&gt;Add&lt;/button&gt; &lt;/form&gt; </code></pre></div> <p>Here we are binding the <code>&lt;input&gt;</code> to the <code>name</code> variable with <code>bind:value={name}</code> and disabling the <em>Add</em> button when it is empty (i.e. no text content) using <code>disabled={!name}</code>. We are also taking care of the <kbd>Escape</kbd> key with <code>on:keydown={(e) =&gt; e.key === 'Escape' &amp;&amp; onCancel()}</code>. Whenever the <kbd>Escape</kbd> key is pressed we run <code>onCancel()</code>, which just clears up the <code>name</code> variable.</p> </li> <li> <p>Now we have to <code>import</code> and use it from inside the <code>Todos</code> component, and update the <code>addTodo()</code> function to receive the name of the new todo.</p> <p>Add the following <code>import</code> statement below the others inside <code>Todos.svelte</code>:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>import NewTodo from "./NewTodo.svelte"; </code></pre></div> </li> <li> <p>And update the <code>addTodo()</code> function like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function addTodo(name) { todos = [...todos, { id: newTodoId, name, completed: false }]; } </code></pre></div> <p><code>addTodo()</code> now receives the name of the new to-do directly, so we no longer need the <code>newTodoName</code> variable to give it its value. Our <code>NewTodo</code> component takes care of that.</p> <div class="notecard note"> <p><strong>Note:</strong> The <code>{ name }</code> syntax is just a shorthand for <code>{ name: name }</code>. This one comes from JavaScript itself and has nothing to do with Svelte, besides providing some inspiration for Svelte's own shorthands.</p> </div> </li> <li> <p>Finally for this section, replace the NewTodo form markup with a call to <code>NewTodo</code> component, like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;!-- NewTodo --&gt; &lt;NewTodo on:addTodo={(e) =&gt; addTodo(e.detail)} /&gt; </code></pre></div> </li> </ol></div></section><section aria-labelledby="working_with_dom_nodes_using_the_bindthisdom_node_directive"><h2 id="working_with_dom_nodes_using_the_bindthisdom_node_directive"><a href="#working_with_dom_nodes_using_the_bindthisdom_node_directive">Working with DOM nodes using the <code>bind:this={dom_node}</code> directive</a></h2><div class="section-content"><p>Now we want the <code>&lt;input&gt;</code> element of the <code>NewTodo</code> component to re-gain focus every time the <em>Add</em> button is pressed. For that we'll need a reference to the DOM node of the input. Svelte provides a way to do this with the <code>bind:this={dom_node}</code> directive. When specified, as soon as the component is mounted and the DOM node is created, Svelte assigns a reference to the DOM node to the specified variable.</p> <p>We'll create a <code>nameEl</code> variable and bind it to the input using <code>bind:this={nameEl}</code>. Then inside <code>addTodo()</code>, after adding the new to-do we will call <code>nameEl.focus()</code> to refocus the <code>&lt;input&gt;</code> again. We will do the same when the user presses the <kbd>Escape</kbd> key, with the <code>onCancel()</code> function.</p> <p>Update the contents of <code>NewTodo.svelte</code> like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); let name = ''; let nameEl; // reference to the name input DOM node const addTodo = () =&gt; { dispatch('addTodo', name); name = ''; nameEl.focus(); // give focus to the name input } const onCancel = () =&gt; { name = ''; nameEl.focus(); // give focus to the name input } &lt;/script&gt; &lt;form on:submit|preventDefault={addTodo} on:keydown={(e) =&gt; e.key === 'Escape' &amp;&amp; onCancel()}&gt; &lt;h2 class="label-wrapper"&gt; &lt;label for="todo-0" class="label__lg"&gt;What needs to be done?&lt;/label&gt; &lt;/h2&gt; &lt;input bind:value={name} bind:this={nameEl} type="text" id="todo-0" autoComplete="off" class="input input__lg" /&gt; &lt;button type="submit" disabled={!name} class="btn btn__primary btn__lg"&gt;Add&lt;/button&gt; &lt;/form&gt; </code></pre></div> <p>Try the app out: type a new to-do name in to the <code>&lt;input&gt;</code> field, press <kbd>tab</kbd> to give focus to the <em>Add</em> button, and then hit <kbd>Enter</kbd> or <kbd>Escape</kbd> to see how the input recovers focus.</p></div></section><section aria-labelledby="autofocusing_our_input"><h3 id="autofocusing_our_input"><a href="#autofocusing_our_input">Autofocusing our input</a></h3><div class="section-content"><p>The next feature will add to our <code>NewTodo</code> component will be an <code>autofocus</code> prop, which will allow us to specify that we want the <code>&lt;input&gt;</code> field to be focused on page load.</p> <ol> <li> <p>Our first attempt is as follows: let's try adding the <code>autofocus</code> prop and just call <code>nameEl.focus()</code> from the <code>&lt;script&gt;</code> block. Update the first part of the <code>&lt;script&gt;</code> section of <code>NewTodo.svelte</code> (the first four lines) to look like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); export let autofocus = false; let name = ''; let nameEl; // reference to the name input DOM node if (autofocus) nameEl.focus(); </code></pre></div> </li> <li> <p>Now go back to the <code>Todos</code> component and pass the <code>autofocus</code> prop into the <code>&lt;NewTodo&gt;</code> component call, like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;!-- NewTodo --&gt; &lt;NewTodo autofocus on:addTodo={(e) =&gt; addTodo(e.detail)} /&gt; </code></pre></div> </li> <li> <p>If you try your app out now, you'll see that the page is now blank, and in your DevTools web console you'll see an error along the lines of: <code>TypeError: nameEl is undefined</code>.</p> </li> </ol> <p>To understand what's happening here, let's talk some more about that <a href="https://learn.svelte.dev/tutorial/onmount" class="external" target="_blank">component lifecycle</a> we mentioned earlier.</p></div></section><section aria-labelledby="component_lifecycle_and_the_onmount_function"><h2 id="component_lifecycle_and_the_onmount_function"><a href="#component_lifecycle_and_the_onmount_function">Component lifecycle, and the <code>onMount()</code> function</a></h2><div class="section-content"><p>When a component is instantiated, Svelte runs the initialization code (that is, the <code>&lt;script&gt;</code> section of the component). But at that moment, all the nodes that comprise the component are not attached to the DOM, in fact, they don't even exist.</p> <p>So how can you know when the component has already been created and mounted on the DOM? The answer is that every component has a lifecycle that starts when it is created and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle.</p> <p>The one you'll use most frequently is <code>onMount()</code>, which lets us run a callback as soon as the component has been mounted on the DOM. Let's give it a try and see what happens to the <code>nameEl</code> variable.</p> <ol> <li> <p>To start with, add the following line at the beginning of the <code>&lt;script&gt;</code> section of <code>NewTodo.svelte</code>:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>import { onMount } from "svelte"; </code></pre></div> </li> <li> <p>And these lines at the end of it:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>console.log("initializing:", nameEl); onMount(() =&gt; { console.log("mounted:", nameEl); }); </code></pre></div> </li> <li> <p>Now remove the <code>if (autofocus) nameEl.focus()</code> line to avoid throwing the error we were seeing before.</p> </li> <li> <p>The app will now work again, and you'll see the following in your console:</p> <pre class="brush: plain notranslate">initializing: undefined mounted: &lt;input id="todo-0" class="input input__lg" type="text" autocomplete="off"&gt; </pre> <p>As you can see, while the component is initializing, <code>nameEl</code> is undefined, which makes sense because the <code>&lt;input&gt;</code> node doesn't even exist yet. After the component has been mounted, Svelte assigned a reference to the <code>&lt;input&gt;</code> DOM node to the <code>nameEl</code> variable, thanks to the <code>bind:this={nameEl} directive</code>.</p> </li> <li> <p>To get the autofocus functionality working, replace the previous <code>console.log()</code>/<code>onMount()</code> block you added with this:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>onMount(() =&gt; autofocus &amp;&amp; nameEl.focus()); // if autofocus is true, we run nameEl.focus() </code></pre></div> </li> <li> <p>Go to your app again, and you'll now see the <code>&lt;input&gt;</code> field is focused on page load.</p> </li> </ol> <div class="notecard note"> <p><strong>Note:</strong> You can have a look at the other <a href="https://svelte.dev/docs/svelte" class="external" target="_blank">lifecycle functions in the Svelte docs</a>, and you can see them in action in the <a href="https://learn.svelte.dev/tutorial/onmount" class="external" target="_blank">interactive tutorial</a>.</p> </div></div></section><section aria-labelledby="waiting_for_the_dom_to_be_updated_with_the_tick_function"><h2 id="waiting_for_the_dom_to_be_updated_with_the_tick_function"><a href="#waiting_for_the_dom_to_be_updated_with_the_tick_function">Waiting for the DOM to be updated with the <code>tick()</code> function</a></h2><div class="section-content"><p>Now we will take care of the <code>Todo</code> component's focus management details. First of all, we want a <code>Todo</code> component's edit <code>&lt;input&gt;</code> to receive focus when we enter editing mode by pressing its <em>Edit</em> button. In the same fashion as we saw earlier, we'll create a <code>nameEl</code> variable inside <code>Todo.svelte</code> and call <code>nameEl.focus()</code> after setting the <code>editing</code> variable to <code>true</code>.</p> <ol> <li> <p>Open the file <code>components/Todo.svelte</code> and add a <code>nameEl</code> variable declaration just below your editing and name declarations:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let nameEl; // reference to the name input DOM node </code></pre></div> </li> <li> <p>Now update your <code>onEdit()</code> function like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function onEdit() { editing = true; // enter editing mode nameEl.focus(); // set focus to name input } </code></pre></div> </li> <li> <p>And finally, bind <code>nameEl</code> to the <code>&lt;input&gt;</code> field by updating it like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;input bind:value={name} bind:this={nameEl} type="text" id="todo-{todo.id}" autocomplete="off" class="todo-text" /&gt; </code></pre></div> </li> <li> <p>However, when you try the updated app, you'll get an error along the lines of "TypeError: nameEl is undefined" in the console when you press a to-do's <em>Edit</em> button.</p> </li> </ol> <p>So, what is happening here? When you update a component's state in Svelte, it doesn't update the DOM immediately. Instead, it waits until the next microtask to see if there are any other changes that need to be applied, including in other components. Doing so avoids unnecessary work and allows the browser to batch things more effectively.</p> <p>In this case, when <code>editing</code> is <code>false</code>, the edit <code>&lt;input&gt;</code> is not visible because it does not exist in the DOM. Inside the <code>onEdit()</code> function we set <code>editing = true</code> and immediately afterwards try to access the <code>nameEl</code> variable and execute <code>nameEl.focus()</code>. The problem here is that Svelte hasn't yet updated the DOM.</p> <p>One way to solve this problem is to use <a href="/en-US/docs/Web/API/Window/setTimeout" title="setTimeout()"><code>setTimeout()</code></a> to delay the call to <code>nameEl.focus()</code> until the next event cycle, and give Svelte the opportunity to update the DOM.</p> <p>Try this now:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function onEdit() { editing = true; // enter editing mode setTimeout(() =&gt; nameEl.focus(), 0); // asynchronous call to set focus to name input } </code></pre></div> <p>The above solution works, but it is rather inelegant. Svelte provides a better way to handle these cases. The <a href="https://learn.svelte.dev/tutorial/tick" class="external" target="_blank"><code>tick()</code> function</a> returns a promise that resolves as soon as any pending state changes have been applied to the DOM (or immediately, if there are no pending state changes). Let's try it now.</p> <ol> <li> <p>First of all, import <code>tick</code> at the top of the <code>&lt;script&gt;</code> section alongside your existing import:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>import { tick } from "svelte"; </code></pre></div> </li> <li> <p>Next, call <code>tick()</code> with <a href="/en-US/docs/Web/JavaScript/Reference/Operators/await"><code>await</code></a> from an <a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function">async function</a>; update <code>onEdit()</code> like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>async function onEdit() { editing = true; // enter editing mode await tick(); nameEl.focus(); } </code></pre></div> </li> <li> <p>If you try it now you'll see that everything works as expected.</p> </li> </ol> <div class="notecard note"> <p><strong>Note:</strong> To see another example using <code>tick()</code>, visit the <a href="https://learn.svelte.dev/tutorial/tick" class="external" target="_blank">Svelte tutorial</a>.</p> </div></div></section><section aria-labelledby="adding_functionality_to_html_elements_with_the_useaction_directive"><h2 id="adding_functionality_to_html_elements_with_the_useaction_directive"><a href="#adding_functionality_to_html_elements_with_the_useaction_directive">Adding functionality to HTML elements with the <code>use:action</code> directive</a></h2><div class="section-content"><p>Next up, we want the name <code>&lt;input&gt;</code> to automatically select all text on focus. Moreover, we want to develop this in such a way that it could be easily reused on any HTML <code>&lt;input&gt;</code> and applied in a declarative way. We will use this requirement as an excuse to show a very powerful feature that Svelte provides us to add functionality to regular HTML elements: <a href="https://svelte.dev/docs/svelte-action" class="external" target="_blank">actions</a>.</p> <p>To select the text of a DOM input node, we have to call <a href="/en-US/docs/Web/API/HTMLInputElement/select"><code>select()</code></a>. To get this function called whenever the node gets focused, we need an event listener along these lines:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>node.addEventListener("focus", (event) =&gt; node.select()); </code></pre></div> <p>And, in order to avoid memory leaks, we should also call the <a href="/en-US/docs/Web/API/EventTarget/removeEventListener"><code>removeEventListener()</code></a> function when the node is destroyed.</p> <div class="notecard note"> <p><strong>Note:</strong> All this is just standard WebAPI functionality; nothing here is specific to Svelte.</p> </div> <p>We could achieve all this in our <code>Todo</code> component whenever we add or remove the <code>&lt;input&gt;</code> from the DOM, but we would have to be very careful to add the event listener after the node has been added to the DOM, and remove the listener before the node is removed from the DOM. In addition, our solution would not be very reusable.</p> <p>That's where Svelte actions come into play. Basically they let us run a function whenever an element has been added to the DOM, and after removal from the DOM.</p> <p>In our immediate use case, we will define a function called <code>selectOnFocus()</code> that will receive a node as a parameter. The function will add an event listener to that node so that whenever it gets focused it will select the text. Then it will return an object with a <code>destroy</code> property. The <code>destroy</code> property is what Svelte will execute after removing the node from the DOM. Here we will remove the listener to make sure we don't leave any memory leak behind.</p> <ol> <li> <p>Let's create the function <code>selectOnFocus()</code>. Add the following to the bottom of the <code>&lt;script&gt;</code> section of <code>Todo.svelte</code>:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function selectOnFocus(node) { if (node &amp;&amp; typeof node.select === "function") { // make sure node is defined and has a select() method const onFocus = (event) =&gt; node.select(); // event handler node.addEventListener("focus", onFocus); // when node gets focus call onFocus() return { destroy: () =&gt; node.removeEventListener("focus", onFocus), // this will be executed when the node is removed from the DOM }; } } </code></pre></div> </li> <li> <p>Now we need to tell the <code>&lt;input&gt;</code> to use that function with the <a href="https://svelte.dev/docs/element-directives#use-action" class="external" target="_blank"><code>use:action</code></a> directive:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;input use:selectOnFocus /&gt; </code></pre></div> <p>With this directive we are telling Svelte to run this function, passing the DOM node of the <code>&lt;input&gt;</code> as a parameter, as soon as the component is mounted on the DOM. It will also be in charge of executing the <code>destroy</code> function when the component is removed from DOM. So with the <code>use</code> directive, Svelte takes care of the component's lifecycle for us.</p> <p>In our case, our <code>&lt;input&gt;</code> would end up like so: update the component's first label/input pair (inside the edit template) as follows:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;label for="todo-{todo.id}" class="todo-label"&gt;New name for '{todo.name}'&lt;/label&gt; &lt;input bind:value={name} bind:this={nameEl} use:selectOnFocus type="text" id="todo-{todo.id}" autocomplete="off" class="todo-text" /&gt; </code></pre></div> </li> <li> <p>Let's try it out. Go to your app, press a to-do's <em>Edit</em> button, then <kbd>Tab</kbd> to take focus away from the <code>&lt;input&gt;</code>. Now click on the <code>&lt;input&gt;</code>, and you'll see that the entire input text is selected.</p> </li> </ol></div></section><section aria-labelledby="making_the_action_reusable"><h3 id="making_the_action_reusable"><a href="#making_the_action_reusable">Making the action reusable</a></h3><div class="section-content"><p>Now let's make this function truly reusable across components. <code>selectOnFocus()</code> is just a function without any dependency on the <code>Todo.svelte</code> component, so we can just extract it to a file and use it from there.</p> <ol> <li> <p>Create a new file, <code>actions.js</code>, inside the <code>src</code> folder.</p> </li> <li> <p>Give it the following content:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>export function selectOnFocus(node) { if (node &amp;&amp; typeof node.select === "function") { // make sure node is defined and has a select() method const onFocus = (event) =&gt; node.select(); // event handler node.addEventListener("focus", onFocus); // when node gets focus call onFocus() return { destroy: () =&gt; node.removeEventListener("focus", onFocus), // this will be executed when the node is removed from the DOM }; } } </code></pre></div> </li> <li> <p>Now import it from inside <code>Todo.svelte</code>; add the following import statement just below the others:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>import { selectOnFocus } from "../actions.js"; </code></pre></div> </li> <li> <p>And remove the <code>selectOnFocus()</code> definition from <code>Todo.svelte</code>, since we no longer need it there.</p> </li> </ol></div></section><section aria-labelledby="reusing_our_action"><h3 id="reusing_our_action"><a href="#reusing_our_action">Reusing our action</a></h3><div class="section-content"><p>To demonstrate our action's reusability, let's make use of it in <code>NewTodo.svelte</code>.</p> <ol> <li> <p>Import <code>selectOnFocus()</code> from <code>actions.js</code> in this file too, as before:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>import { selectOnFocus } from "../actions.js"; </code></pre></div> </li> <li> <p>Add the <code>use:selectOnFocus</code> directive to the <code>&lt;input&gt;</code>, like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;input bind:value={name} bind:this={nameEl} use:selectOnFocus type="text" id="todo-0" autocomplete="off" class="input input__lg" /&gt; </code></pre></div> </li> </ol> <p>With a few lines of code we can add functionality to regular HTML elements in a very reusable and declarative way. It just takes an <code>import</code> and a short directive like <code>use:selectOnFocus</code> that clearly describes its purpose. And we can achieve this without the need to create a custom wrapper element like <code>TextInput</code>, <code>MyInput</code>, or similar. Moreover, you can add as many <code>use:action</code> directives as you want to an element.</p> <p>Also, we didn't have to struggle with <code>onMount()</code>, <code>onDestroy()</code>, or <code>tick()</code> — the <code>use</code> directive takes care of the component lifecycle for us.</p></div></section><section aria-labelledby="other_actions_improvements"><h3 id="other_actions_improvements"><a href="#other_actions_improvements">Other actions improvements</a></h3><div class="section-content"><p>In the previous section, while working with the <code>Todo</code> components, we had to deal with <code>bind:this</code>, <code>tick()</code>, and <code>async</code> functions just to give focus to our <code>&lt;input&gt;</code> as soon as it was added to the DOM.</p> <ol> <li> <p>This is how we can implement it with actions instead:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const focusOnInit = (node) =&gt; node &amp;&amp; typeof node.focus === "function" &amp;&amp; node.focus(); </code></pre></div> </li> <li> <p>And then in our markup we just need to add another <code>use:</code> directive:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;input bind:value={name} use:selectOnFocus use:focusOnInit /&gt; </code></pre></div> </li> <li> <p>Our <code>onEdit()</code> function can now be much simpler:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function onEdit() { editing = true; // enter editing mode } </code></pre></div> </li> </ol> <p>As a last example before we move on, let's go back to our <code>Todo.svelte</code> component and give focus to the <em>Edit</em> button after the user presses <em>Save</em> or <em>Cancel</em>.</p> <p>We could try just reusing our <code>focusOnInit</code> action again, adding <code>use:focusOnInit</code> to the <em>Edit</em> button. But we'd be introducing a subtle bug. When you add a new to-do, the focus will be put on the <em>Edit</em> button of the recently added to-do. That's because the <code>focusOnInit</code> action is running when the component is created.</p> <p>That's not what we want — we want the <em>Edit</em> button to receive focus only when the user has pressed <em>Save</em> or <em>Cancel</em>.</p> <ol> <li> <p>So, go back to your <code>Todo.svelte</code> file.</p> </li> <li> <p>First we'll create a flag named <code>editButtonPressed</code> and initialize it to <code>false</code>. Add this just below your other variable definitions:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let editButtonPressed = false; // track if edit button has been pressed, to give focus to it after cancel or save </code></pre></div> </li> <li> <p>Next we'll modify the <em>Edit</em> button's functionality to save this flag, and create the action for it. Update the <code>onEdit()</code> function like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function onEdit() { editButtonPressed = true; // user pressed the Edit button, focus will come back to the Edit button editing = true; // enter editing mode } </code></pre></div> </li> <li> <p>Below it, add the following definition for <code>focusEditButton()</code>:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const focusEditButton = (node) =&gt; editButtonPressed &amp;&amp; node.focus(); </code></pre></div> </li> <li> <p>Finally we <code>use</code> the <code>focusEditButton</code> action on the <em>Edit</em> button, like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;button type="button" class="btn" on:click={onEdit} use:focusEditButton&gt; Edit&lt;span class="visually-hidden"&gt; {todo.name}&lt;/span&gt; &lt;/button&gt; </code></pre></div> </li> <li> <p>Go back and try your app again. At this point, every time the <em>Edit</em> button is added to the DOM, the <code>focusEditButton</code> action is executed, but it will only give focus to the button if the <code>editButtonPressed</code> flag is <code>true</code>.</p> </li> </ol> <div class="notecard note"> <p><strong>Note:</strong> We have barely scratched the surface of actions here. Actions can also have reactive parameters, and Svelte lets us detect when any of those parameters change. So we can add functionality that integrates nicely with the Svelte reactive system. For a more detailed introduction to actions, consider checking out the <a href="https://learn.svelte.dev/tutorial/actions" class="external" target="_blank">Svelte Interactive tutorial</a> or the <a href="https://svelte.dev/docs/element-directives#use-action" class="external" target="_blank">Svelte <code>use:action</code> documentation</a>.</p> </div></div></section><section aria-labelledby="component_binding_exposing_component_methods_and_variables_using_the_bindthiscomponent_directive"><h2 id="component_binding_exposing_component_methods_and_variables_using_the_bindthiscomponent_directive"><a href="#component_binding_exposing_component_methods_and_variables_using_the_bindthiscomponent_directive">Component binding: exposing component methods and variables using the <code>bind:this={component}</code> directive</a></h2><div class="section-content"><p>There's still one accessibility annoyance left. When the user presses the <em>Delete</em> button, the focus vanishes.</p> <p>The last feature we will be looking at in this article involves setting the focus on the status heading after a to-do has been deleted.</p> <p>Why the status heading? In this case, the element that had the focus has been deleted, so there's not a clear candidate to receive focus. We've picked the status heading because it's near the list of to-dos, and it's a way to give a visual feedback about the removal of the task, as well as indicating what's happened to screen reader users.</p> <p>First we'll extract the status heading to its own component.</p> <ol> <li> <p>Create a new file, <code>components/TodosStatus.svelte</code>.</p> </li> <li> <p>Add the following contents to it:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; export let todos; $: totalTodos = todos.length; $: completedTodos = todos.filter((todo) =&gt; todo.completed).length; &lt;/script&gt; &lt;h2 id="list-heading"&gt; {completedTodos} out of {totalTodos} items completed &lt;/h2&gt; </code></pre></div> </li> <li> <p>Import the file at the beginning of <code>Todos.svelte</code>, adding the following <code>import</code> statement below the others:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>import TodosStatus from "./TodosStatus.svelte"; </code></pre></div> </li> <li> <p>Replace the <code>&lt;h2&gt;</code> status heading inside <code>Todos.svelte</code> with a call to the <code>TodosStatus</code> component, passing <code>todos</code> to it as a prop, like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;TodosStatus {todos} /&gt; </code></pre></div> </li> <li> <p>You can also do a bit of cleanup, removing the <code>totalTodos</code> and <code>completedTodos</code> variables from <code>Todos.svelte</code>. Just remove the <code>$: totalTodos = …</code> and the <code>$: completedTodos = …</code> lines, and also remove the reference to <code>totalTodos</code> when we calculate <code>newTodoId</code> and use <code>todos.length</code> instead. To do this, replace the block that begins with <code>let newTodoId</code> with this:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>$: newTodoId = todos.length ? Math.max(...todos.map((t) =&gt; t.id)) + 1 : 1; </code></pre></div> </li> <li> <p>Everything works as expected — we just extracted the last piece of markup to its own component.</p> </li> </ol> <p>Now we need to find a way to give focus to the <code>&lt;h2&gt;</code> status label after a to-do has been removed.</p> <p>So far we saw how to send information to a component via props, and how a component can communicate with its parent by emitting events or using two-way data binding. The child component could get a reference to the <code>&lt;h2&gt;</code> node <code>using bind:this={dom_node}</code> and expose it to the outside using two-way data binding. But doing so would break the component encapsulation; setting focus on it should be its own responsibility.</p> <p>So we need the <code>TodosStatus</code> component to expose a method that its parent can call to give focus to it. It's a very common scenario that a component needs to expose some behavior or information to the consumer; let's see how to achieve it with Svelte.</p> <p>We've already seen that Svelte uses <code>export let varname = …</code> to <a href="https://svelte.dev/docs/svelte-components#script-1-export-creates-a-component-prop" class="external" target="_blank">declare props</a>. But if instead of using <code>let</code> you export a <code>const</code>, <code>class</code>, or <code>function</code>, it is read-only outside the component. Function expressions are valid props, however. In the following example, the first three declarations are props, and the rest are exported values:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; export let bar = "optional default initial value"; // prop export let baz = undefined; // prop export let format = (n) =&gt; n.toFixed(2); // prop // these are readonly export const thisIs = "readonly"; // read-only export export function greet(name) { // read-only export alert(`Hello, ${name}!`); } export const greet = (name) =&gt; alert(`Hello, ${name}!`); // read-only export &lt;/script&gt; </code></pre></div> <p>With this in mind, let's go back to our use case. We'll create a function called <code>focus()</code> that gives focus to the <code>&lt;h2&gt;</code> heading. For that we'll need a <code>headingEl</code> variable to hold the reference to the DOM node, and we'll have to bind it to the <code>&lt;h2&gt;</code> element using <code>bind:this={headingEl}</code>. Our focus method will just run <code>headingEl.focus()</code>.</p> <ol> <li> <p>Update the contents of <code>TodosStatus.svelte</code> like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;script&gt; export let todos; $: totalTodos = todos.length; $: completedTodos = todos.filter((todo) =&gt; todo.completed).length; let headingEl; export function focus() { // shorter version: export const focus = () =&gt; headingEl.focus() headingEl.focus(); } &lt;/script&gt; &lt;h2 id="list-heading" bind:this={headingEl} tabindex="-1"&gt; {completedTodos} out of {totalTodos} items completed &lt;/h2&gt; </code></pre></div> <p>Note that we've added a <code>tabindex</code> attribute to the <code>&lt;h2&gt;</code> to allow the element to receive focus programmatically.</p> <p>As we saw earlier, using the <code>bind:this={headingEl}</code> directive gives us a reference to the DOM node in the variable <code>headingEl</code>. Then we use <code>export function focus()</code> to expose a function that gives focus to the <code>&lt;h2&gt;</code> heading.</p> <p>How can we access those exported values from the parent? Just as you can bind to DOM elements with the <code>bind:this={dom_node}</code> directive, you can also bind to component instances themselves with <code>bind:this={component}</code>. So, when you use <code>bind:this</code> on an HTML element, you get a reference to the DOM node, and when you do it on a Svelte component, you get a reference to the instance of that component.</p> </li> <li> <p>So to bind to the instance of <code>TodosStatus</code>, we'll first create a <code>todosStatus</code> variable in <code>Todos.svelte</code>. Add the following line below your <code>import</code> statements:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let todosStatus; // reference to TodosStatus instance </code></pre></div> </li> <li> <p>Next, add a <code>bind:this={todosStatus}</code> directive to the call, as follows:</p> <div class="code-example"><div class="example-header"><span class="language-name">svelte</span></div><pre class="brush: svelte notranslate"><code>&lt;!-- TodosStatus --&gt; &lt;TodosStatus bind:this={todosStatus} {todos} /&gt; </code></pre></div> </li> <li> <p>Now we can call the <code>exported focus()</code> method from our <code>removeTodo()</code> function:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function removeTodo(todo) { todos = todos.filter((t) =&gt; t.id !== todo.id); todosStatus.focus(); // give focus to status heading } </code></pre></div> </li> <li> <p>Go back to your app. Now if you delete any to-do, the status heading will be focussed. This is useful to highlight the change in numbers of to-dos, both to sighted users and screen reader users.</p> </li> </ol> <div class="notecard note"> <p><strong>Note:</strong> You might be wondering why we need to declare a new variable for component binding. Why can't we just call <code>TodosStatus.focus()</code>? You might have multiple <code>TodosStatus</code> instances active, so you need a way to reference each particular instance. That's why you have to specify a variable to bind each specific instance to.</p> </div></div></section><section aria-labelledby="the_code_so_far"><h2 id="the_code_so_far"><a href="#the_code_so_far">The code so far</a></h2><div class="section-content"></div></section><section aria-labelledby="git_2"><h3 id="git_2"><a href="#git_2">Git</a></h3><div class="section-content"><p>To see the state of the code as it should be at the end of this article, access your copy of our repo like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>cd mdn-svelte-tutorial/06-stores </code></pre></div> <p>Or directly download the folder's content:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>npx degit opensas/mdn-svelte-tutorial/06-stores </code></pre></div> <p>Remember to run <code>npm install &amp;&amp; npm run dev</code> to start your app in development mode.</p></div></section><section aria-labelledby="repl_2"><h3 id="repl_2"><a href="#repl_2">REPL</a></h3><div class="section-content"><p>To see the current state of the code in a REPL, visit:</p> <p><a href="https://svelte.dev/repl/d1fa84a5a4494366b179c87395940039?version=3.23.2" class="external" target="_blank">https://svelte.dev/repl/d1fa84a5a4494366b179c87395940039?version=3.23.2</a></p></div></section><section aria-labelledby="summary"><h2 id="summary"><a href="#summary">Summary</a></h2><div class="section-content"><p>In this article we have finished adding all the required functionality to our app, plus we've taken care of a number of accessibility and usability issues. We also finished splitting our app into manageable components, each one with a unique responsibility.</p> <p>In the meantime, we saw a few advanced Svelte techniques, like:</p> <ul> <li>Dealing with reactivity gotchas when updating objects and arrays</li> <li>Working with DOM nodes using <code>bind:this={dom_node}</code> (binding DOM elements)</li> <li>Using the component lifecycle <code>onMount()</code> function</li> <li>Forcing Svelte to resolve pending state changes with the <code>tick()</code> function</li> <li>Adding functionality to HTML elements in a reusable and declarative way with the <code>use:action</code> directive</li> <li>Accessing component methods using <code>bind:this={component}</code> (binding components)</li> </ul> <p>In the next article we will see how to use stores to communicate between components, and add animations to our components.</p><ul class="prev-next"> <li><a class="button secondary" href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components"><span class="button-wrap"> Previous </span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks"><span class="button-wrap"> Overview: Understanding client-side JavaScript frameworks</span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores"><span class="button-wrap"> Next </span></a></li> </ul></div></section></article><aside class="article-footer"><div class="article-footer-inner"><div class="svg-container"><svg xmlns="http://www.w3.org/2000/svg" width="162" height="162" viewBox="0 0 162 162" fill="none" role="none"><mask id="b" fill="#fff"><path d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z"></path></mask><path stroke="url(#a)" stroke-dasharray="6, 6" stroke-width="2" d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z" mask="url(#b)" style="stroke:url(#a)" transform="translate(-63.992 -25.587)"></path><ellipse cx="8.066" cy="111.597" fill="var(--background-tertiary)" rx="53.677" ry="53.699" transform="matrix(.71707 -.697 .7243 .6895 0 0)"></ellipse><g clip-path="url(#c)" transform="translate(-63.992 -25.587)"><path fill="#9abff5" d="m144.256 137.379 32.906 12.434a4.41 4.41 0 0 1 2.559 5.667l-9.326 24.679a4.41 4.41 0 0 1-5.667 2.559l-8.226-3.108-2.332 6.17c-.466 1.233-.375 1.883-1.609 1.417l-2.253-.527c-.411-.155-.95-.594-1.206-1.161l-4.734-10.484-12.545-4.741a4.41 4.41 0 0 1-2.559-5.667l9.325-24.679a4.41 4.41 0 0 1 5.667-2.559m9.961 29.617 8.227 3.108 3.264-8.638-.498-6.768-4.113-1.555.548 7.258-4.319-1.632zm-12.339-4.663 8.226 3.108 3.264-8.637-.498-6.769-4.113-1.554.548 7.257-4.319-1.632z"></path></g><g clip-path="url(#d)" transform="translate(-63.992 -25.587)"><path fill="#81b0f3" d="M135.35 60.136 86.67 41.654c-3.346-1.27-7.124.428-8.394 3.775L64.414 81.938c-1.27 3.347.428 7.125 3.774 8.395l12.17 4.62-3.465 9.128c-.693 1.826-1.432 2.457.394 3.15l3.014 1.625c.609.231 1.637.274 2.477-.104l15.53-6.983 18.56 7.047c3.346 1.27 7.124-.428 8.395-3.775l13.862-36.51c1.27-3.346-.428-7.124-3.775-8.395M95.261 83.207l-12.17-4.62 4.852-12.779 7.19-7.017 6.085 2.31-7.725 7.51 6.389 2.426zm18.255 6.93-12.17-4.62 4.852-12.778 7.189-7.017 6.085 2.31-7.725 7.51 6.39 2.426z"></path></g><defs><clipPath id="c"><path fill="#fff" d="m198.638 146.586-65.056-24.583-24.583 65.057 65.056 24.582z"></path></clipPath><clipPath id="d"><path fill="#fff" d="m66.438 14.055 96.242 36.54-36.54 96.243-96.243-36.54z"></path></clipPath><linearGradient id="a" x1="97.203" x2="199.995" y1="47.04" y2="152.793" gradientUnits="userSpaceOnUse"><stop stop-color="#086DFC"></stop><stop offset="0.246" stop-color="#2C81FA"></stop><stop offset="0.516" stop-color="#5497F8"></stop><stop offset="0.821" stop-color="#80B0F6"></stop><stop offset="1" stop-color="#9ABFF5"></stop></linearGradient></defs></svg></div><h2>Help improve MDN</h2><fieldset class="feedback"><label>Was this page helpful to you?</label><div class="button-container"><button type="button" class="button primary has-icon yes"><span class="button-wrap"><span class="icon icon-thumbs-up "></span>Yes</span></button><button type="button" class="button primary has-icon no"><span class="button-wrap"><span class="icon icon-thumbs-down "></span>No</span></button></div></fieldset><a class="contribute" href="https://github.com/mdn/content/blob/main/CONTRIBUTING.md" title="This will take you to our contribution guidelines on GitHub." target="_blank" rel="noopener noreferrer">Learn how to contribute</a>.<p class="last-modified-date">This page was last modified on<!-- --> <time dateTime="2024-10-07T09:37:56.000Z">Oct 7, 2024</time> by<!-- --> <a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility/contributors.txt" rel="nofollow">MDN contributors</a>.</p><div id="on-github" class="on-github"><a href="https://github.com/mdn/content/blob/main/files/en-us/learn/tools_and_testing/client-side_javascript_frameworks/svelte_reactivity_lifecycle_accessibility/index.md?plain=1" title="Folder: en-us/learn/tools_and_testing/client-side_javascript_frameworks/svelte_reactivity_lifecycle_accessibility (Opens in a new tab)" target="_blank" rel="noopener noreferrer">View this page on GitHub</a> <!-- -->•<!-- --> <a href="https://github.com/mdn/content/issues/new?template=page-report.yml&amp;mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FLearn%2FTools_and_testing%2FClient-side_JavaScript_frameworks%2FSvelte_reactivity_lifecycle_accessibility&amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EPage+report+details%3C%2Fsummary%3E%0A%0A*+Folder%3A+%60en-us%2Flearn%2Ftools_and_testing%2Fclient-side_javascript_frameworks%2Fsvelte_reactivity_lifecycle_accessibility%60%0A*+MDN+URL%3A+https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FLearn%2FTools_and_testing%2FClient-side_JavaScript_frameworks%2FSvelte_reactivity_lifecycle_accessibility%0A*+GitHub+URL%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Fcontent%2Fblob%2Fmain%2Ffiles%2Fen-us%2Flearn%2Ftools_and_testing%2Fclient-side_javascript_frameworks%2Fsvelte_reactivity_lifecycle_accessibility%2Findex.md%0A*+Last+commit%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Fcontent%2Fcommit%2F1b4e6d1156e8471d38deeea1567c35ef412c5f42%0A*+Document+last+modified%3A+2024-10-07T09%3A37%3A56.000Z%0A%0A%3C%2Fdetails%3E" title="This will take you to GitHub to file a new issue." target="_blank" rel="noopener noreferrer">Report a problem with this content</a></div></div></aside></main></div></div><footer id="nav-footer" class="page-footer"><div class="page-footer-grid"><div class="page-footer-logo-col"><a href="/" class="mdn-footer-logo" aria-label="MDN homepage"><svg width="48" height="17" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mdn-footer-logo-svg">MDN logo</title><path d="M20.04 16.512H15.504V10.416C15.504 9.488 15.344 8.824 15.024 8.424C14.72 8.024 14.264 7.824 13.656 7.824C12.92 7.824 12.384 8.064 12.048 8.544C11.728 9.024 11.568 9.64 11.568 10.392V14.184H13.008V16.512H8.472V10.416C8.472 9.488 8.312 8.824 7.992 8.424C7.688 8.024 7.232 7.824 6.624 7.824C5.872 7.824 5.336 8.064 5.016 8.544C4.696 9.024 4.536 9.64 4.536 10.392V14.184H6.6V16.512H0V14.184H1.44V8.04H0.024V5.688H4.536V7.32C5.224 6.088 6.32 5.472 7.824 5.472C8.608 5.472 9.328 5.664 9.984 6.048C10.64 6.432 11.096 7.016 11.352 7.8C11.992 6.248 13.168 5.472 14.88 5.472C15.856 5.472 16.72 5.776 17.472 6.384C18.224 6.992 18.6 7.936 18.6 9.216V14.184H20.04V16.512Z" fill="currentColor"></path><path d="M33.6714 16.512H29.1354V14.496C28.8314 15.12 28.3834 15.656 27.7914 16.104C27.1994 16.536 26.4154 16.752 25.4394 16.752C24.0154 16.752 22.8954 16.264 22.0794 15.288C21.2634 14.312 20.8554 12.984 20.8554 11.304C20.8554 9.688 21.2554 8.312 22.0554 7.176C22.8554 6.04 24.0634 5.472 25.6794 5.472C26.5594 5.472 27.2794 5.648 27.8394 6C28.3994 6.352 28.8314 6.8 29.1354 7.344V2.352H26.9754V0H32.2314V14.184H33.6714V16.512ZM29.1354 11.04V10.776C29.1354 9.88 28.8954 9.184 28.4154 8.688C27.9514 8.176 27.3674 7.92 26.6634 7.92C25.9754 7.92 25.3674 8.176 24.8394 8.688C24.3274 9.2 24.0714 10.008 24.0714 11.112C24.0714 12.152 24.3114 12.944 24.7914 13.488C25.2714 14.032 25.8394 14.304 26.4954 14.304C27.3114 14.304 27.9514 13.96 28.4154 13.272C28.8954 12.584 29.1354 11.84 29.1354 11.04Z" fill="currentColor"></path><path d="M47.9589 16.512H41.9829V14.184H43.4229V10.416C43.4229 9.488 43.2629 8.824 42.9429 8.424C42.6389 8.024 42.1829 7.824 41.5749 7.824C40.8389 7.824 40.2709 8.056 39.8709 8.52C39.4709 8.968 39.2629 9.56 39.2469 10.296V14.184H40.6869V16.512H34.7109V14.184H36.1509V8.04H34.5909V5.688H39.2469V7.344C39.9669 6.096 41.1269 5.472 42.7269 5.472C43.7509 5.472 44.6389 5.776 45.3909 6.384C46.1429 6.992 46.5189 7.936 46.5189 9.216V14.184H47.9589V16.512Z" fill="currentColor"></path></svg></a><p>Your blueprint for a better internet.</p><ul class="social-icons"><li><a href="https://mozilla.social/@mdn" target="_blank" rel="me noopener noreferrer"><span class="icon icon-mastodon"></span><span class="visually-hidden">MDN on Mastodon</span></a></li><li><a href="https://twitter.com/mozdevnet" target="_blank" rel="noopener noreferrer"><span class="icon icon-twitter-x"></span><span class="visually-hidden">MDN on X (formerly Twitter)</span></a></li><li><a href="https://github.com/mdn/" target="_blank" rel="noopener noreferrer"><span class="icon icon-github-mark-small"></span><span class="visually-hidden">MDN on GitHub</span></a></li><li><a href="/en-US/blog/rss.xml" target="_blank"><span class="icon icon-feed"></span><span class="visually-hidden">MDN Blog RSS Feed</span></a></li></ul></div><div class="page-footer-nav-col-1"><h2 class="footer-nav-heading">MDN</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a href="/en-US/about">About</a></li><li class="footer-nav-item"><a href="/en-US/blog/">Blog</a></li><li class="footer-nav-item"><a href="https://www.mozilla.org/en-US/careers/listings/?team=ProdOps" target="_blank" rel="noopener noreferrer">Careers</a></li><li class="footer-nav-item"><a href="/en-US/advertising">Advertise with us</a></li></ul></div><div class="page-footer-nav-col-2"><h2 class="footer-nav-heading">Support</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="https://support.mozilla.org/products/mdn-plus">Product help</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/MDN/Community/Issues">Report an issue</a></li></ul></div><div class="page-footer-nav-col-3"><h2 class="footer-nav-heading">Our communities</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/community">MDN Community</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="https://discourse.mozilla.org/c/mdn/236" target="_blank" rel="noopener noreferrer">MDN Forum</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/discord" target="_blank" rel="noopener noreferrer">MDN Chat</a></li></ul></div><div class="page-footer-nav-col-4"><h2 class="footer-nav-heading">Developers</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/Web">Web Technologies</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/Learn">Learn Web Development</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/plus">MDN Plus</a></li><li class="footer-nav-item"><a href="https://hacks.mozilla.org/" target="_blank" rel="noopener noreferrer">Hacks Blog</a></li></ul></div><div class="page-footer-moz"><a href="https://www.mozilla.org/" class="footer-moz-logo-link" target="_blank" rel="noopener noreferrer"><svg width="112" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mozilla-footer-logo-svg">Mozilla logo</title><path d="M41.753 14.218c-2.048 0-3.324 1.522-3.324 4.157 0 2.423 1.119 4.286 3.29 4.286 2.082 0 3.447-1.678 3.447-4.347 0-2.826-1.522-4.096-3.413-4.096Zm54.89 7.044c0 .901.437 1.618 1.645 1.618 1.427 0 2.949-1.024 3.044-3.352-.649-.095-1.365-.185-2.02-.185-1.426-.005-2.668.397-2.668 1.92Z" fill="currentColor"></path><path d="M0 0v32h111.908V0H0Zm32.56 25.426h-5.87v-7.884c0-2.423-.806-3.352-2.39-3.352-1.924 0-2.702 1.365-2.702 3.324v4.868h1.864v3.044h-5.864v-7.884c0-2.423-.806-3.352-2.39-3.352-1.924 0-2.702 1.365-2.702 3.324v4.868h2.669v3.044H6.642v-3.044h1.863v-7.918H6.642V11.42h5.864v2.11c.839-1.489 2.3-2.39 4.252-2.39 2.02 0 3.878.963 4.566 3.01.778-1.862 2.361-3.01 4.566-3.01 2.512 0 4.812 1.522 4.812 4.84v6.402h1.863v3.044h-.005Zm9.036.307c-4.314 0-7.296-2.635-7.296-7.106 0-4.096 2.484-7.481 7.514-7.481s7.481 3.38 7.481 7.29c0 4.472-3.228 7.297-7.699 7.297Zm22.578-.307H51.942l-.403-2.11 7.7-8.846h-4.376l-.621 2.17-2.888-.313.498-4.907h12.294l.313 2.11-7.767 8.852h4.533l.654-2.172 3.167.308-.872 4.908Zm7.99 0h-4.191v-5.03h4.19v5.03Zm0-8.976h-4.191v-5.03h4.19v5.03Zm2.618 8.976 6.054-21.358h3.945l-6.054 21.358h-3.945Zm8.136 0 6.048-21.358h3.945l-6.054 21.358h-3.939Zm21.486.307c-1.863 0-2.887-1.085-3.072-2.792-.805 1.427-2.232 2.792-4.498 2.792-2.02 0-4.314-1.085-4.314-4.006 0-3.447 3.323-4.253 6.518-4.253.778 0 1.584.034 2.3.124v-.465c0-1.427-.034-3.133-2.3-3.133-.84 0-1.488.061-2.143.402l-.453 1.578-3.195-.34.549-3.224c2.45-.996 3.692-1.27 5.992-1.27 3.01 0 5.556 1.55 5.556 4.75v6.083c0 .805.314 1.085.963 1.085.184 0 .375-.034.587-.095l.034 2.11a5.432 5.432 0 0 1-2.524.654Z" fill="currentColor"></path></svg></a><ul class="footer-moz-list"><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Website Privacy Notice</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/#cookies" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Cookies</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/legal/terms/mozilla" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Legal</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/governance/policies/participation/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Community Participation Guidelines</a></li></ul></div><div class="page-footer-legal"><p id="license" class="page-footer-legal-text">Visit<!-- --> <a href="https://www.mozilla.org" target="_blank" rel="noopener noreferrer">Mozilla Corporation’s</a> <!-- -->not-for-profit parent, the<!-- --> <a target="_blank" rel="noopener noreferrer" href="https://foundation.mozilla.org/">Mozilla Foundation</a>.<br/>Portions of this content are ©1998–<!-- -->2024<!-- --> by individual mozilla.org contributors. Content available under<!-- --> <a href="/en-US/docs/MDN/Writing_guidelines/Attrib_copyright_license">a Creative Commons license</a>.</p></div></div></footer></div><script type="application/json" id="hydration">{"url":"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility","doc":{"isMarkdown":true,"isTranslated":false,"isActive":true,"flaws":{},"title":"Advanced Svelte: Reactivity, lifecycle, accessibility","mdn_url":"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility","locale":"en-US","native":"English (US)","sidebarHTML":"<ol><li class=\"section\"><a href=\"/en-US/docs/Learn/Getting_started_with_the_web\">Complete beginners start here!</a></li><li><details><summary>Getting started with the web</summary><ol><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web\">Getting started with the web</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/Installing_basic_software\">Installing basic software</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/What_will_your_website_look_like\">What will your website look like?</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/Dealing_with_files\">Dealing with files</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics\">HTML basics</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics\">CSS basics</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics\">JavaScript basics</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/Publishing_your_website\">Publishing your website</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/How_the_Web_works\">How the web works</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/HTML\">HTML — Structuring the web</a></li><li><details><summary>Introduction to HTML</summary><ol><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML\">Introduction to HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started\">Getting started with HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML\">What's in the head? Metadata in HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/HTML_text_fundamentals\">HTML text fundamentals</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks\">Creating hyperlinks</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formatting\">Advanced text formatting</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure\">Document and website structure</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Debugging_HTML\">Debugging HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Marking_up_a_letter\">Marking up a letter</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Structuring_a_page_of_content\">Structuring a page of content</a></li></ol></details></li><li><details><summary>Multimedia and embedding</summary><ol><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding\">Multimedia and embedding</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML\">Images in HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content\">Video and audio content</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Other_embedding_technologies\">From object to iframe — other embedding technologies</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web\">Adding vector graphics to the web</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images\">Responsive images</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Mozilla_splash_page\">Mozilla splash page</a></li></ol></details></li><li><details><summary>HTML tables</summary><ol><li><a href=\"/en-US/docs/Learn/HTML/Tables\">HTML tables</a></li><li><a href=\"/en-US/docs/Learn/HTML/Tables/Basics\">HTML table basics</a></li><li><a href=\"/en-US/docs/Learn/HTML/Tables/Advanced\">HTML table advanced features and accessibility</a></li><li><a href=\"/en-US/docs/Learn/HTML/Tables/Structuring_planet_data\">Structuring planet data</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/CSS\">CSS — Styling the web</a></li><li><details><summary>CSS first steps</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/First_steps\">CSS first steps</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/What_is_CSS\">What is CSS?</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/Getting_started\">Getting started with CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/How_CSS_is_structured\">How CSS is structured</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/How_CSS_works\">How CSS works</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/Styling_a_biography_page\">Styling a biography page</a></li></ol></details></li><li><details><summary>CSS building blocks</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks\">CSS building blocks</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors\">CSS selectors</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Type_Class_and_ID_Selectors\">Type, class, and ID selectors</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Attribute_selectors\">Attribute selectors</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements\">Pseudo-classes and pseudo-elements</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators\">Combinators</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance\">Cascade, specificity, and inheritance</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers\">Cascade layers</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/The_box_model\">The box model</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Backgrounds_and_borders\">Backgrounds and borders</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Handling_different_text_directions\">Handling different text directions</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Overflowing_content\">Overflowing content</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Values_and_units\">CSS values and units</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Sizing_items_in_CSS\">Sizing items in CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Images_media_form_elements\">Images, media, and form elements</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Styling_tables\">Styling tables</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Advanced_styling_effects\">Advanced styling effects</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Debugging_CSS\">Debugging CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Organizing\">Organizing your CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Fundamental_CSS_comprehension\">Fundamental CSS comprehension</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Creating_fancy_letterheaded_paper\">Creating fancy letterheaded paper</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/A_cool_looking_box\">A cool-looking box</a></li></ol></details></li><li><details><summary>Styling text</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/Styling_text\">CSS styling text</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Fundamentals\">Fundamental text and font styling</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Styling_lists\">Styling lists</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Styling_links\">Styling links</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Web_fonts\">Web fonts</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Typesetting_a_homepage\">Typesetting a community school homepage</a></li></ol></details></li><li><details><summary>CSS layout</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout\">CSS layout</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Introduction\">Introduction to CSS layout</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Normal_Flow\">Normal Flow</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Flexbox\">Flexbox</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Grids\">Grids</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Floats\">Floats</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Positioning\">Positioning</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Multiple-column_Layout\">Multiple-column layout</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design\">Responsive design</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Media_queries\">Beginner's guide to media queries</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods\">Legacy layout methods</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers\">Supporting older browsers</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Fundamental_Layout_Comprehension\">Fundamental layout comprehension</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/JavaScript\">JavaScript — Dynamic client-side scripting</a></li><li><details><summary>JavaScript first steps</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps\">JavaScript first steps</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript\">What is JavaScript?</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/A_first_splash\">A first splash into JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong\">What went wrong? Troubleshooting JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Variables\">Storing the information you need — Variables</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Math\">Basic math in JavaScript — numbers and operators</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Strings\">Handling text — strings in JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods\">Useful string methods</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Arrays\">Arrays</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator\">Silly story generator</a></li></ol></details></li><li><details><summary>JavaScript building blocks</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks\">JavaScript building blocks</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/conditionals\">Making decisions in your code — conditionals</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code\">Looping code</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Functions\">Functions — reusable blocks of code</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function\">Build your own function</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Return_values\">Function return values</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Events\">Introduction to events</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Event_bubbling\">Event bubbling</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Image_gallery\">Image gallery</a></li></ol></details></li><li><details><summary>Introducing JavaScript objects</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Objects\">Introducing JavaScript objects</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Basics\">JavaScript object basics</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Object_prototypes\">Object prototypes</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Object-oriented_programming\">Object-oriented programming</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript\">Classes in JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/JSON\">Working with JSON</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Object_building_practice\">Object building practice</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features\">Adding features to our bouncing balls demo</a></li></ol></details></li><li><details><summary>Asynchronous JavaScript</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous\">Asynchronous JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Introducing\">Introducing asynchronous JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Promises\">How to use promises</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Implementing_a_promise-based_API\">How to implement a promise-based API</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Introducing_workers\">Introducing workers</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Sequencing_animations\">Sequencing animations</a></li></ol></details></li><li><details><summary>Client-side web APIs</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs\">Client-side web APIs</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction\">Introduction to web APIs</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents\">Manipulating documents</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data\">Fetching data from the server</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs\">Third-party APIs</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics\">Drawing graphics</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs\">Video and Audio APIs</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage\">Client-side storage</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Forms\">Web forms — Working with user data</a></li><li><details><summary>Web form building blocks</summary><ol><li><a href=\"/en-US/docs/Learn/Forms\">Web form building blocks</a></li><li><a href=\"/en-US/docs/Learn/Forms/Your_first_form\">Your first form</a></li><li><a href=\"/en-US/docs/Learn/Forms/How_to_structure_a_web_form\">How to structure a web form</a></li><li><a href=\"/en-US/docs/Learn/Forms/Basic_native_form_controls\">Basic native form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/HTML5_input_types\">The HTML5 input types</a></li><li><a href=\"/en-US/docs/Learn/Forms/Other_form_controls\">Other form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/Styling_web_forms\">Styling web forms</a></li><li><a href=\"/en-US/docs/Learn/Forms/Advanced_form_styling\">Advanced form styling</a></li><li><a href=\"/en-US/docs/Learn/Forms/UI_pseudo-classes\">UI pseudo-classes</a></li><li><a href=\"/en-US/docs/Learn/Forms/Form_validation\">Client-side form validation</a></li><li><a href=\"/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data\">Sending form data</a></li></ol></details></li><li><details><summary>Advanced web form techniques</summary><ol><li><a href=\"/en-US/docs/Learn/Forms/How_to_build_custom_form_controls\">How to build custom form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/Sending_forms_through_JavaScript\">Sending forms through JavaScript</a></li><li><a href=\"/en-US/docs/Learn/Forms/Property_compatibility_table_for_form_controls\">CSS property compatibility table for form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/HTML_forms_in_legacy_browsers\">HTML forms in legacy browsers</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Accessibility\">Accessibility — Make the web usable by everyone</a></li><li><details><summary>Accessibility guides</summary><ol><li><a href=\"/en-US/docs/Learn/Accessibility\">Accessibility</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/What_is_accessibility\">What is accessibility?</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/HTML\">HTML: A good basis for accessibility</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/CSS_and_JavaScript\">CSS and JavaScript accessibility best practices</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/WAI-ARIA_basics\">WAI-ARIA basics</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/Multimedia\">Accessible multimedia</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/Mobile\">Mobile accessibility</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/Accessibility_troubleshooting\">Assessment: Accessibility troubleshooting</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Performance\">Performance — Making websites fast and responsive</a></li><li><details><summary>Performance guides</summary><ol><li><a href=\"/en-US/docs/Learn/Performance\">Web performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/why_web_performance\">The \"why\" of web performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/What_is_web_performance\">What is web performance?</a></li><li><a href=\"/en-US/docs/Learn/Performance/Perceived_performance\">Perceived performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/Measuring_performance\">Measuring performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/Multimedia\">Multimedia: Images</a></li><li><a href=\"/en-US/docs/Learn/Performance/video\">Multimedia: video</a></li><li><a href=\"/en-US/docs/Learn/Performance/JavaScript\">JavaScript performance optimization</a></li><li><a href=\"/en-US/docs/Learn/Performance/HTML\">HTML performance optimization</a></li><li><a href=\"/en-US/docs/Learn/Performance/CSS\">CSS performance optimization</a></li><li><a href=\"/en-US/docs/Learn/Performance/business_case_for_performance\">The business case for web performance</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/MathML\">MathML — Writing mathematics with MathML</a></li><li><details><summary>MathML first steps</summary><ol><li><a href=\"/en-US/docs/Learn/MathML/First_steps\">MathML first steps</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Getting_started\">Getting started with MathML</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Text_containers\">MathML Text Containers</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Fractions_and_roots\">MathML fractions and roots</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Scripts\">MathML scripted elements</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Tables\">MathML tables</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Three_famous_mathematical_formulas\">Three famous mathematical formulas</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/../Games\">Games — Developing games for the web</a></li><li><details><summary>Guides and tutorials</summary><ol><li><a href=\"/en-US/docs/Games/Introduction\">Introduction to game development for the Web</a></li><li><a href=\"/en-US/docs/Games/Techniques\">Techniques for game development</a></li><li><a href=\"/en-US/docs/Games/Tutorials\">Tutorials</a></li><li><a href=\"/en-US/docs/Games/Publishing_games\">Publishing games</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Tools_and_testing\">Tools and testing</a></li><li><details><summary>Client-side web development tools</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools\">Understanding client-side web development tools</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview\">Client-side tooling overview</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line\">Command line crash course</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management\">Package management basics</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Introducing_complete_toolchain\">Introducing a complete toolchain</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Deployment\">Deploying our app</a></li></ol></details></li><li><details><summary>Introduction to client-side frameworks</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction\">Introduction to client-side frameworks</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features\">Framework main features</a></li></ol></details></li><li><details><summary>React</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started\">Getting started with React</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning\">Beginning our React todo list</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components\">Componentizing our React app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state\">React interactivity: Events and state</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering\">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility\">Accessibility in React</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources\">React resources</a></li></ol></details></li><li><details><summary>Ember</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started\">Getting started with Ember</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization\">Ember app structure and componentization</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state\">Ember interactivity: Events, classes and state</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer\">Ember Interactivity: Footer functionality, conditional rendering</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing\">Routing in Ember</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources\">Ember resources and troubleshooting</a></li></ol></details></li><li><details><summary>Vue</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started\">Getting started with Vue</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component\">Creating our first Vue component</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists\">Rendering a list of Vue components</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models\">Adding a new todo form: Vue events, methods, and models</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling\">Styling Vue components with CSS</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties\">Using Vue computed properties</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering\">Vue conditional rendering: editing existing todos</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management\">Vue refs and lifecycle methods for focus management</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources\">Vue resources</a></li></ol></details></li><li><details open=\"\"><summary>Svelte</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started\">Getting started with Svelte</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning\">Starting our Svelte to-do list app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props\">Dynamic behavior in Svelte: working with variables and props</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components\">Componentizing our Svelte app</a></li><li><em><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility\" aria-current=\"page\">Advanced Svelte: Reactivity, lifecycle, accessibility</a></em></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores\">Working with Svelte stores</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript\">TypeScript support in Svelte</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next\">Deployment and next steps</a></li></ol></details></li><li><details><summary>Angular</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started\">Getting started with Angular</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning\">Beginning our Angular todo list app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling\">Styling our Angular app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component\">Creating an item component</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering\">Filtering our to-do items</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building\">Building Angular applications and further resources</a></li></ol></details></li><li><details><summary>Git and GitHub</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/GitHub\">Git and GitHub</a></li></ol></details></li><li><details><summary>Cross browser testing</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing\">Cross browser testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Introduction\">Introduction to cross-browser testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies\">Strategies for carrying out testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS\">Handling common HTML and CSS problems</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript\">Handling common JavaScript problems</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility\">Handling common accessibility problems</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection\">Implementing feature detection</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Automated_testing\">Introduction to automated testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment\">Setting up your own test automation environment</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Server-side\">Server-side website programming</a></li><li><details><summary>First steps</summary><ol><li><a href=\"/en-US/docs/Learn/Server-side/First_steps\">Server-side website programming first steps</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Introduction\">Introduction to the server side</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview\">Client-Server Overview</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Web_frameworks\">Server-side web frameworks</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Website_security\">Website security</a></li></ol></details></li><li><details><summary>Django web framework (Python)</summary><ol><li><a href=\"/en-US/docs/Learn/Server-side/Django\">Django Web Framework (Python)</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Introduction\">Django introduction</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/development_environment\">Setting up a Django development environment</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website\">Django Tutorial: The Local Library website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/skeleton_website\">Django Tutorial Part 2: Creating a skeleton website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Models\">Django Tutorial Part 3: Using models</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Admin_site\">Django Tutorial Part 4: Django admin site</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Home_page\">Django Tutorial Part 5: Creating our home page</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Generic_views\">Django Tutorial Part 6: Generic list and detail views</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Sessions\">Django Tutorial Part 7: Sessions framework</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Authentication\">Django Tutorial Part 8: User authentication and permissions</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Forms\">Django Tutorial Part 9: Working with forms</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Testing\">Django Tutorial Part 10: Testing a Django web application</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Deployment\">Django Tutorial Part 11: Deploying Django to production</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/web_application_security\">Django web application security</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/django_assessment_blog\">Assessment: DIY Django mini blog</a></li></ol></details></li><li><details><summary>Express Web Framework (Node.js/JavaScript)</summary><ol><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs\">Express web framework (Node.js/JavaScript)</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction\">Express/Node introduction</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment\">Setting up a Node development environment</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website\">Express Tutorial: The Local Library website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website\">Express Tutorial Part 2: Creating a skeleton website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose\">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/routes\">Express Tutorial Part 4: Routes and controllers</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data\">Express Tutorial Part 5: Displaying library data</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/forms\">Express Tutorial Part 6: Working with forms</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/deployment\">Express Tutorial Part 7: Deploying to production</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Common_questions\">Further resources</a></li><li><details><summary>Common questions</summary><ol><li><a href=\"/en-US/docs/Learn/Common_questions\">Common questions</a></li><li><a href=\"/en-US/docs/Learn/HTML/Howto\">Use HTML to solve common problems</a></li><li><a href=\"/en-US/docs/Learn/CSS/Howto\">Use CSS to solve common problems</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Howto\">Solve common problems in your JavaScript code</a></li><li><a href=\"/en-US/docs/Learn/Common_questions/Web_mechanics\">Web mechanics</a></li><li><a href=\"/en-US/docs/Learn/Common_questions/Tools_and_setup\">Tools and setup</a></li><li><a href=\"/en-US/docs/Learn/Common_questions/Design_and_accessibility\">Design and accessibility</a></li></ol></details></li></ol>","sidebarMacro":"LearnSidebar","body":[{"type":"prose","value":{"id":null,"title":null,"isH3":false,"content":"<ul class=\"prev-next\">\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components\"><span class=\"button-wrap\"> Previous </span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks\"><span class=\"button-wrap\"> Overview: Understanding client-side JavaScript frameworks</span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores\"><span class=\"button-wrap\"> Next </span></a></li>\n</ul>\n<p>In the last article we added more features to our to-do list and started to organize our app into components. In this article we will add the app's final features and further componentize our app. We will learn how to deal with reactivity issues related to updating objects and arrays. To avoid common pitfalls, we'll have to dig a little deeper into Svelte's reactivity system. We'll also look at solving some accessibility focus issues, and more besides.</p>\n<figure class=\"table-container\"><table>\n <tbody>\n <tr>\n <th scope=\"row\">Prerequisites:</th>\n <td>\n <p>\n At minimum, it is recommended that you are familiar with the core\n <a href=\"/en-US/docs/Learn/HTML\">HTML</a>,\n <a href=\"/en-US/docs/Learn/CSS\">CSS</a>, and\n <a href=\"/en-US/docs/Learn/JavaScript\">JavaScript</a> languages, and\n have knowledge of the\n <a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line\">terminal/command line</a>.\n </p>\n <p>\n You'll need a terminal with node and npm installed to compile and build\n your app.\n </p>\n </td>\n </tr>\n <tr>\n <th scope=\"row\">Objective:</th>\n <td>\n Learn some advanced Svelte techniques involving solving reactivity\n issues, keyboard accessibility problems to do with component lifecycle,\n and more.\n </td>\n </tr>\n </tbody>\n</table></figure>\n<p>We'll focus on some accessibility issues involving focus management. To do so, we'll utilize some techniques for accessing DOM nodes and executing methods like <a href=\"/en-US/docs/Web/API/HTMLElement/focus\"><code>focus()</code></a> and <a href=\"/en-US/docs/Web/API/HTMLInputElement/select\"><code>select()</code></a>. We will also see how to declare and clean up event listeners on DOM elements.</p>\n<p>We also need to learn a bit about component lifecycle to understand when these DOM nodes get mounted and unmounted from the DOM and how we can access them. We will also learn about the <code>action</code> directive, which will allow us to extend the functionality of HTML elements in a reusable and declarative way.</p>\n<p>Finally, we will learn a bit more about components. So far, we've seen how components can share data using props, and communicate with their parents using events and two-way data binding. Now we will see how components can also expose methods and variables.</p>\n<p>The following new components will be developed throughout the course of this article:</p>\n<ul>\n <li><code>MoreActions</code>: Displays the <em>Check All</em> and <em>Remove Completed</em> buttons, and emits the corresponding events required to handle their functionality.</li>\n <li><code>NewTodo</code>: Displays the <code>&lt;input&gt;</code> field and <em>Add</em> button for adding a new to-do.</li>\n <li><code>TodosStatus</code>: Displays the \"x out of y items completed\" status heading.</li>\n</ul>"}},{"type":"prose","value":{"id":"code_along_with_us","title":"Code along with us","isH3":false,"content":""}},{"type":"prose","value":{"id":"git","title":"Git","isH3":true,"content":"<p>Clone the GitHub repo (if you haven't already done it) with:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>git clone https://github.com/opensas/mdn-svelte-tutorial.git\n</code></pre></div>\n<p>Then to get to the current app state, run</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>cd mdn-svelte-tutorial/05-advanced-concepts\n</code></pre></div>\n<p>Or directly download the folder's content:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>npx degit opensas/mdn-svelte-tutorial/05-advanced-concepts\n</code></pre></div>\n<p>Remember to run <code>npm install &amp;&amp; npm run dev</code> to start your app in development mode.</p>"}},{"type":"prose","value":{"id":"repl","title":"REPL","isH3":true,"content":"<p>To code along with us using the REPL, start at</p>\n<p><a href=\"https://svelte.dev/repl/76cc90c43a37452e8c7f70521f88b698?version=3.23.2\" class=\"external\" target=\"_blank\">https://svelte.dev/repl/76cc90c43a37452e8c7f70521f88b698?version=3.23.2</a></p>"}},{"type":"prose","value":{"id":"working_on_the_moreactions_component","title":"Working on the MoreActions component","isH3":false,"content":"<p>Now we'll tackle the <em>Check All</em> and <em>Remove Completed</em> buttons. Let's create a component that will be in charge of displaying the buttons and emitting the corresponding events.</p>\n<ol>\n <li>\n <p>Create a new file, <code>components/MoreActions.svelte</code>.</p>\n </li>\n <li>\n <p>When the first button is clicked, we'll emit a <code>checkAll</code> event to signal that all the to-dos should be checked/unchecked. When the second button is clicked, we'll emit a <code>removeCompleted</code> event to signal that all of the completed to-dos should be removed. Put the following content into your <code>MoreActions.svelte</code> file:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n import { createEventDispatcher } from \"svelte\";\n const dispatch = createEventDispatcher();\n\n let completed = true;\n\n const checkAll = () =&gt; {\n dispatch(\"checkAll\", completed);\n completed = !completed;\n };\n\n const removeCompleted = () =&gt; dispatch(\"removeCompleted\");\n&lt;/script&gt;\n\n&lt;div class=\"btn-group\"&gt;\n &lt;button type=\"button\" class=\"btn btn__primary\" on:click={checkAll}&gt;{completed ? 'Check' : 'Uncheck'} all&lt;/button&gt;\n &lt;button type=\"button\" class=\"btn btn__primary\" on:click={removeCompleted}&gt;Remove completed&lt;/button&gt;\n&lt;/div&gt;\n</code></pre></div>\n <p>We've also included a <code>completed</code> variable to toggle between checking and unchecking all tasks.</p>\n </li>\n <li>\n <p>Back over in <code>Todos.svelte</code>, we are going to import our <code>MoreActions</code> component and create two functions to handle the events emitted by the <code>MoreActions</code> component.</p>\n <p>Add the following import statement below the existing ones:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>import MoreActions from \"./MoreActions.svelte\";\n</code></pre></div>\n </li>\n <li>\n <p>Then add the described functions at the end of the <code>&lt;script&gt;</code> section:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const checkAllTodos = (completed) =&gt;\n todos.forEach((t) =&gt; (t.completed = completed));\n\nconst removeCompletedTodos = () =&gt;\n (todos = todos.filter((t) =&gt; !t.completed));\n</code></pre></div>\n </li>\n <li>\n <p>Now go to the bottom of the <code>Todos.svelte</code> markup section and replace the <code>&lt;div class=\"btn-group\"&gt;</code> element that we copied into <code>MoreActions.svelte</code> with a call to the <code>MoreActions</code> component, like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;!-- MoreActions --&gt;\n&lt;MoreActions\n on:checkAll={(e) =&gt; checkAllTodos(e.detail)}\n on:removeCompleted={removeCompletedTodos}\n/&gt;\n</code></pre></div>\n </li>\n <li>\n <p>OK, let's go back into the app and try it out. You'll find that the <em>Remove Completed</em> button works fine, but the <em>Check All</em>/<em>Uncheck All</em> button just silently fails.</p>\n </li>\n</ol>\n<p>To find out what is happening here, we'll have to dig a little deeper into Svelte reactivity.</p>"}},{"type":"prose","value":{"id":"reactivity_gotchas_updating_objects_and_arrays","title":"Reactivity gotchas: updating objects and arrays","isH3":false,"content":"<p>To see what's happening we can log the <code>todos</code> array from the <code>checkAllTodos()</code> function to the console.</p>\n<ol>\n <li>\n <p>Update your existing <code>checkAllTodos()</code> function to the following:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const checkAllTodos = (completed) =&gt; {\n todos.forEach((t) =&gt; (t.completed = completed));\n console.log(\"todos\", todos);\n};\n</code></pre></div>\n </li>\n <li>\n <p>Go back to your browser, open your DevTools console, and click <em>Check All</em>/<em>Uncheck All</em> a few times.</p>\n </li>\n</ol>\n<p>You'll notice that the array is successfully updated every time you press the button (the <code>todo</code> objects' <code>completed</code> properties are toggled between <code>true</code> and <code>false</code>), but Svelte is not aware of that. This also means that in this case, a reactive statement like <code>$: console.log('todos', todos)</code> won't be very useful.</p>\n<p>To find out why this is happening, we need to understand how reactivity works in Svelte when updating arrays and objects.</p>\n<p>Many web frameworks use the virtual DOM technique to update the page. Basically, the virtual DOM is an in-memory copy of the contents of the web page. The framework updates this virtual representation, which is then synced with the \"real\" DOM. This is much faster than directly updating the DOM and allows the framework to apply many optimization techniques.</p>\n<p>These frameworks, by default, basically re-run all our JavaScript on every change against this virtual DOM, and apply different methods to cache expensive calculations and optimize execution. They make little to no attempt to understand what our JavaScript code is doing.</p>\n<p>Svelte doesn't use a virtual DOM representation. Instead, it parses and analyzes our code, creates a dependency tree, and then generates the required JavaScript to update only the parts of the DOM that need to be updated. This approach usually generates optimal JavaScript code with minimal overhead, but it also has its limitations.</p>\n<p>Sometimes Svelte cannot detect changes to variables being watched. Remember that to tell Svelte that a variable has changed, you have to assign it a new value. A simple rule to keep in mind is that <strong>The name of the updated variable must appear on the left-hand side of the assignment.</strong></p>\n<p>For example, in the following piece of code:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const foo = obj.foo;\nfoo.bar = \"baz\";\n</code></pre></div>\n<p>Svelte won't update references to <code>obj.foo.bar</code>, unless you follow it up with <code>obj = obj</code>. That's because Svelte can't track object references, so we have to explicitly tell it that <code>obj</code> has changed by issuing an assignment.</p>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> If <code>foo</code> is a top-level variable, you can easily tell Svelte to update <code>obj</code> whenever <code>foo</code> is changed with the following reactive statement: <code>$: foo, obj = obj</code>. With this we are defining <code>foo</code> as a dependency, and whenever it changes Svelte will run <code>obj = obj</code>.</p>\n</div>\n<p>In our <code>checkAllTodos()</code> function, when we run:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>todos.forEach((t) =&gt; (t.completed = completed));\n</code></pre></div>\n<p>Svelte will not mark <code>todos</code> as changed because it does not know that when we update our <code>t</code> variable inside the <code>forEach()</code> method, we are also modifying the <code>todos</code> array. And that makes sense, because otherwise Svelte would be aware of the inner workings of the <code>forEach()</code> method; the same would therefore be true for any method attached to any object or array.</p>\n<p>Nevertheless, there are different techniques that we can apply to solve this problem, and all of them involve assigning a new value to the variable being watched.</p>\n<p>As we already saw, we could just tell Svelte to update the variable with a self-assignment, like this:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const checkAllTodos = (completed) =&gt; {\n todos.forEach((t) =&gt; (t.completed = completed));\n todos = todos;\n};\n</code></pre></div>\n<p>This will solve the problem. Internally Svelte will flag <code>todos</code> as changed and remove the apparently redundant self-assignment. Apart from the fact that it looks weird, it's perfectly OK to use this technique, and sometimes it's the most concise way to do it.</p>\n<p>We could also access the <code>todos</code> array by index, like this:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const checkAllTodos = (completed) =&gt; {\n todos.forEach((t, i) =&gt; (todos[i].completed = completed));\n};\n</code></pre></div>\n<p>Assignments to properties of arrays and objects — e.g. <code>obj.foo += 1</code> or <code>array[i] = x</code> — work the same way as assignments to the values themselves. When Svelte analyzes this code, it can detect that the <code>todos</code> array is being modified.</p>\n<p>Another solution is to assign a new array to <code>todos</code> containing a copy of all the to-dos with the <code>completed</code> property updated accordingly, like this:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const checkAllTodos = (completed) =&gt; {\n todos = todos.map((t) =&gt; ({ ...t, completed }));\n};\n</code></pre></div>\n<p>In this case we are using the <a href=\"/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map\"><code>map()</code></a> method, which returns a new array with the results of executing the provided function for each item. The function returns a copy of each to-do using <a href=\"/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax\">spread syntax</a> and overwrites the property of the completed value accordingly. This solution has the added benefit of returning a new array with new objects, completely avoiding mutating the original <code>todos</code> array.</p>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> Svelte allows us to specify different options that affect how the compiler works. The <code>&lt;svelte:options immutable={true}/&gt;</code> option tells the compiler that you promise not to mutate any objects. This allows it to be less conservative about checking whether values have changed and generate simpler and more performant code. For more information on <code>&lt;svelte:options&gt;</code>, check the <a href=\"https://svelte.dev/docs/special-elements#svelte-options\" class=\"external\" target=\"_blank\">Svelte options documentation</a>.</p>\n</div>\n<p>All of these solutions involve an assignment in which the updated variable is on the left side of the equation. Any of this techniques will allow Svelte to notice that our <code>todos</code> array has been modified.</p>\n<p><strong>Choose one, and update your <code>checkAllTodos()</code> function as required. Now you should be able to check and uncheck all your to-dos at once. Try it!</strong></p>"}},{"type":"prose","value":{"id":"finishing_our_moreactions_component","title":"Finishing our MoreActions component","isH3":false,"content":"<p>We will add one usability detail to our component. We'll disable the buttons when there are no tasks to be processed. To create this, we'll receive the <code>todos</code> array as a prop, and set the <code>disabled</code> property of each button accordingly.</p>\n<ol>\n <li>\n <p>Update your <code>MoreActions.svelte</code> component like this:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n import { createEventDispatcher } from 'svelte';\n const dispatch = createEventDispatcher();\n\n export let todos;\n\n let completed = true;\n\n const checkAll = () =&gt; {\n dispatch('checkAll', completed);\n completed = !completed;\n }\n\n const removeCompleted = () =&gt; dispatch('removeCompleted');\n\n $: completedTodos = todos.filter((t) =&gt; t.completed).length;\n&lt;/script&gt;\n\n&lt;div class=\"btn-group\"&gt;\n &lt;button type=\"button\" class=\"btn btn__primary\"\n disabled={todos.length === 0} on:click={checkAll}&gt;{completed ? 'Check' : 'Uncheck'} all&lt;/button&gt;\n &lt;button type=\"button\" class=\"btn btn__primary\"\n disabled={completedTodos === 0} on:click={removeCompleted}&gt;Remove completed&lt;/button&gt;\n&lt;/div&gt;\n</code></pre></div>\n <p>We've also declared a reactive <code>completedTodos</code> variable to enable or disable the <em>Remove Completed</em> button.</p>\n </li>\n <li>\n <p>Don't forget to pass the prop into <code>MoreActions</code> from inside <code>Todos.svelte</code>, where the component is called:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;MoreActions {todos}\n on:checkAll={(e) =&gt; checkAllTodos(e.detail)}\n on:removeCompleted={removeCompletedTodos}\n /&gt;\n</code></pre></div>\n </li>\n</ol>"}},{"type":"prose","value":{"id":"working_with_the_dom_focusing_on_the_details","title":"Working with the DOM: focusing on the details","isH3":false,"content":"<p>Now that we have completed all of the app's required functionality, we'll concentrate on some accessibility features that will improve the usability of our app for both keyboard-only and screen reader users.</p>\n<p>In its current state our app has a couple of keyboard accessibility problems involving focus management. Let's have a look at these issues.</p>"}},{"type":"prose","value":{"id":"exploring_keyboard_accessibility_issues_in_our_to-do_app","title":"Exploring keyboard accessibility issues in our to-do app","isH3":false,"content":"<p>Right now, keyboard users will find out that the focus flow of our app is not very predictable or coherent.</p>\n<p>If you click on the input at the top of our app, you'll see a thick, dashed outline around that input. This outline is your visual indicator that the browser is currently focused on this element.</p>\n<p>If you are a mouse user, you might have skipped this visual hint. But if you are working exclusively with the keyboard, knowing which control has focus is of vital importance. It tells us which control is going to receive our keystrokes.</p>\n<p>If you press the <kbd>Tab</kbd> key repeatedly, you'll see the dashed focus indicator cycling between all the focusable elements on the page. If you move the focus to the <em>Edit</em> button and press <kbd>Enter</kbd>, suddenly the focus disappears, and you can no longer tell which control will receive our keystrokes.</p>\n<p>Moreover, if you press the <kbd>Escape</kbd> or <kbd>Enter</kbd> key, nothing happens. And if you click on <em>Cancel</em> or <em>Save</em>, the focus disappears again. For a user working with the keyboard, this behavior will be confusing at best.</p>\n<p>We'd also like to add some usability features, like disabling the <em>Save</em> button when required fields are empty, giving focus to certain HTML elements or auto-selecting contents when a text input receives focus.</p>\n<p>To implement all these features, we'll need programmatic access to DOM nodes to run functions like <a href=\"/en-US/docs/Web/API/HTMLElement/focus\"><code>focus()</code></a> and <a href=\"/en-US/docs/Web/API/HTMLInputElement/select\"><code>select()</code></a>. We will also have to use <a href=\"/en-US/docs/Web/API/EventTarget/addEventListener\"><code>addEventListener()</code></a> and <a href=\"/en-US/docs/Web/API/EventTarget/removeEventListener\"><code>removeEventListener()</code></a> to run specific tasks when the control receives focus.</p>\n<p>The problem is that all these DOM nodes are dynamically created by Svelte at runtime. So we'll have to wait for them to be created and added to the DOM in order to use them. To do so, we'll have to learn about the <a href=\"https://learn.svelte.dev/tutorial/onmount\" class=\"external\" target=\"_blank\">component lifecycle</a> to understand when we can access them — more on this later.</p>"}},{"type":"prose","value":{"id":"creating_a_newtodo_component","title":"Creating a NewTodo component","isH3":false,"content":"<p>Let's begin by extracting our new to-do form out to its own component. With what we know so far we can create a new component file and adjust the code to emit an <code>addTodo</code> event, passing the name of the new to-do in with the additional details.</p>\n<ol>\n <li>\n <p>Create a new file, <code>components/NewTodo.svelte</code>.</p>\n </li>\n <li>\n <p>Put the following contents inside it:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n import { createEventDispatcher } from 'svelte';\n const dispatch = createEventDispatcher();\n\n let name = '';\n\n const addTodo = () =&gt; {\n dispatch('addTodo', name);\n name = '';\n }\n\n const onCancel = () =&gt; name = '';\n\n&lt;/script&gt;\n\n&lt;form on:submit|preventDefault={addTodo} on:keydown={(e) =&gt; e.key === 'Escape' &amp;&amp; onCancel()}&gt;\n &lt;h2 class=\"label-wrapper\"&gt;\n &lt;label for=\"todo-0\" class=\"label__lg\"&gt;What needs to be done?&lt;/label&gt;\n &lt;/h2&gt;\n &lt;input bind:value={name} type=\"text\" id=\"todo-0\" autoComplete=\"off\" class=\"input input__lg\" /&gt;\n &lt;button type=\"submit\" disabled={!name} class=\"btn btn__primary btn__lg\"&gt;Add&lt;/button&gt;\n&lt;/form&gt;\n</code></pre></div>\n <p>Here we are binding the <code>&lt;input&gt;</code> to the <code>name</code> variable with <code>bind:value={name}</code> and disabling the <em>Add</em> button when it is empty (i.e. no text content) using <code>disabled={!name}</code>. We are also taking care of the <kbd>Escape</kbd> key with <code>on:keydown={(e) =&gt; e.key === 'Escape' &amp;&amp; onCancel()}</code>. Whenever the <kbd>Escape</kbd> key is pressed we run <code>onCancel()</code>, which just clears up the <code>name</code> variable.</p>\n </li>\n <li>\n <p>Now we have to <code>import</code> and use it from inside the <code>Todos</code> component, and update the <code>addTodo()</code> function to receive the name of the new todo.</p>\n <p>Add the following <code>import</code> statement below the others inside <code>Todos.svelte</code>:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>import NewTodo from \"./NewTodo.svelte\";\n</code></pre></div>\n </li>\n <li>\n <p>And update the <code>addTodo()</code> function like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function addTodo(name) {\n todos = [...todos, { id: newTodoId, name, completed: false }];\n}\n</code></pre></div>\n <p><code>addTodo()</code> now receives the name of the new to-do directly, so we no longer need the <code>newTodoName</code> variable to give it its value. Our <code>NewTodo</code> component takes care of that.</p>\n <div class=\"notecard note\">\n <p><strong>Note:</strong> The <code>{ name }</code> syntax is just a shorthand for <code>{ name: name }</code>. This one comes from JavaScript itself and has nothing to do with Svelte, besides providing some inspiration for Svelte's own shorthands.</p>\n </div>\n </li>\n <li>\n <p>Finally for this section, replace the NewTodo form markup with a call to <code>NewTodo</code> component, like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;!-- NewTodo --&gt;\n&lt;NewTodo on:addTodo={(e) =&gt; addTodo(e.detail)} /&gt;\n</code></pre></div>\n </li>\n</ol>"}},{"type":"prose","value":{"id":"working_with_dom_nodes_using_the_bindthisdom_node_directive","title":"Working with DOM nodes using the <code>bind:this={dom_node}</code> directive","isH3":false,"content":"<p>Now we want the <code>&lt;input&gt;</code> element of the <code>NewTodo</code> component to re-gain focus every time the <em>Add</em> button is pressed. For that we'll need a reference to the DOM node of the input. Svelte provides a way to do this with the <code>bind:this={dom_node}</code> directive. When specified, as soon as the component is mounted and the DOM node is created, Svelte assigns a reference to the DOM node to the specified variable.</p>\n<p>We'll create a <code>nameEl</code> variable and bind it to the input using <code>bind:this={nameEl}</code>. Then inside <code>addTodo()</code>, after adding the new to-do we will call <code>nameEl.focus()</code> to refocus the <code>&lt;input&gt;</code> again. We will do the same when the user presses the <kbd>Escape</kbd> key, with the <code>onCancel()</code> function.</p>\n<p>Update the contents of <code>NewTodo.svelte</code> like so:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n import { createEventDispatcher } from 'svelte';\n const dispatch = createEventDispatcher();\n\n let name = '';\n let nameEl; // reference to the name input DOM node\n\n const addTodo = () =&gt; {\n dispatch('addTodo', name);\n name = '';\n nameEl.focus(); // give focus to the name input\n }\n\n const onCancel = () =&gt; {\n name = '';\n nameEl.focus(); // give focus to the name input\n }\n&lt;/script&gt;\n\n&lt;form on:submit|preventDefault={addTodo} on:keydown={(e) =&gt; e.key === 'Escape' &amp;&amp; onCancel()}&gt;\n &lt;h2 class=\"label-wrapper\"&gt;\n &lt;label for=\"todo-0\" class=\"label__lg\"&gt;What needs to be done?&lt;/label&gt;\n &lt;/h2&gt;\n &lt;input bind:value={name} bind:this={nameEl} type=\"text\" id=\"todo-0\" autoComplete=\"off\" class=\"input input__lg\" /&gt;\n &lt;button type=\"submit\" disabled={!name} class=\"btn btn__primary btn__lg\"&gt;Add&lt;/button&gt;\n&lt;/form&gt;\n</code></pre></div>\n<p>Try the app out: type a new to-do name in to the <code>&lt;input&gt;</code> field, press <kbd>tab</kbd> to give focus to the <em>Add</em> button, and then hit <kbd>Enter</kbd> or <kbd>Escape</kbd> to see how the input recovers focus.</p>"}},{"type":"prose","value":{"id":"autofocusing_our_input","title":"Autofocusing our input","isH3":true,"content":"<p>The next feature will add to our <code>NewTodo</code> component will be an <code>autofocus</code> prop, which will allow us to specify that we want the <code>&lt;input&gt;</code> field to be focused on page load.</p>\n<ol>\n <li>\n <p>Our first attempt is as follows: let's try adding the <code>autofocus</code> prop and just call <code>nameEl.focus()</code> from the <code>&lt;script&gt;</code> block. Update the first part of the <code>&lt;script&gt;</code> section of <code>NewTodo.svelte</code> (the first four lines) to look like this:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n import { createEventDispatcher } from 'svelte';\n const dispatch = createEventDispatcher();\n\n export let autofocus = false;\n\n let name = '';\n let nameEl; // reference to the name input DOM node\n\n if (autofocus) nameEl.focus();\n</code></pre></div>\n </li>\n <li>\n <p>Now go back to the <code>Todos</code> component and pass the <code>autofocus</code> prop into the <code>&lt;NewTodo&gt;</code> component call, like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;!-- NewTodo --&gt;\n&lt;NewTodo autofocus on:addTodo={(e) =&gt; addTodo(e.detail)} /&gt;\n</code></pre></div>\n </li>\n <li>\n <p>If you try your app out now, you'll see that the page is now blank, and in your DevTools web console you'll see an error along the lines of: <code>TypeError: nameEl is undefined</code>.</p>\n </li>\n</ol>\n<p>To understand what's happening here, let's talk some more about that <a href=\"https://learn.svelte.dev/tutorial/onmount\" class=\"external\" target=\"_blank\">component lifecycle</a> we mentioned earlier.</p>"}},{"type":"prose","value":{"id":"component_lifecycle_and_the_onmount_function","title":"Component lifecycle, and the <code>onMount()</code> function","isH3":false,"content":"<p>When a component is instantiated, Svelte runs the initialization code (that is, the <code>&lt;script&gt;</code> section of the component). But at that moment, all the nodes that comprise the component are not attached to the DOM, in fact, they don't even exist.</p>\n<p>So how can you know when the component has already been created and mounted on the DOM? The answer is that every component has a lifecycle that starts when it is created and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle.</p>\n<p>The one you'll use most frequently is <code>onMount()</code>, which lets us run a callback as soon as the component has been mounted on the DOM. Let's give it a try and see what happens to the <code>nameEl</code> variable.</p>\n<ol>\n <li>\n <p>To start with, add the following line at the beginning of the <code>&lt;script&gt;</code> section of <code>NewTodo.svelte</code>:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>import { onMount } from \"svelte\";\n</code></pre></div>\n </li>\n <li>\n <p>And these lines at the end of it:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>console.log(\"initializing:\", nameEl);\nonMount(() =&gt; {\n console.log(\"mounted:\", nameEl);\n});\n</code></pre></div>\n </li>\n <li>\n <p>Now remove the <code>if (autofocus) nameEl.focus()</code> line to avoid throwing the error we were seeing before.</p>\n </li>\n <li>\n <p>The app will now work again, and you'll see the following in your console:</p>\n <pre class=\"brush: plain notranslate\">initializing: undefined\nmounted: &lt;input id=\"todo-0\" class=\"input input__lg\" type=\"text\" autocomplete=\"off\"&gt;\n</pre>\n <p>As you can see, while the component is initializing, <code>nameEl</code> is undefined, which makes sense because the <code>&lt;input&gt;</code> node doesn't even exist yet. After the component has been mounted, Svelte assigned a reference to the <code>&lt;input&gt;</code> DOM node to the <code>nameEl</code> variable, thanks to the <code>bind:this={nameEl} directive</code>.</p>\n </li>\n <li>\n <p>To get the autofocus functionality working, replace the previous <code>console.log()</code>/<code>onMount()</code> block you added with this:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>onMount(() =&gt; autofocus &amp;&amp; nameEl.focus()); // if autofocus is true, we run nameEl.focus()\n</code></pre></div>\n </li>\n <li>\n <p>Go to your app again, and you'll now see the <code>&lt;input&gt;</code> field is focused on page load.</p>\n </li>\n</ol>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> You can have a look at the other <a href=\"https://svelte.dev/docs/svelte\" class=\"external\" target=\"_blank\">lifecycle functions in the Svelte docs</a>, and you can see them in action in the <a href=\"https://learn.svelte.dev/tutorial/onmount\" class=\"external\" target=\"_blank\">interactive tutorial</a>.</p>\n</div>"}},{"type":"prose","value":{"id":"waiting_for_the_dom_to_be_updated_with_the_tick_function","title":"Waiting for the DOM to be updated with the <code>tick()</code> function","isH3":false,"content":"<p>Now we will take care of the <code>Todo</code> component's focus management details. First of all, we want a <code>Todo</code> component's edit <code>&lt;input&gt;</code> to receive focus when we enter editing mode by pressing its <em>Edit</em> button. In the same fashion as we saw earlier, we'll create a <code>nameEl</code> variable inside <code>Todo.svelte</code> and call <code>nameEl.focus()</code> after setting the <code>editing</code> variable to <code>true</code>.</p>\n<ol>\n <li>\n <p>Open the file <code>components/Todo.svelte</code> and add a <code>nameEl</code> variable declaration just below your editing and name declarations:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let nameEl; // reference to the name input DOM node\n</code></pre></div>\n </li>\n <li>\n <p>Now update your <code>onEdit()</code> function like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function onEdit() {\n editing = true; // enter editing mode\n nameEl.focus(); // set focus to name input\n}\n</code></pre></div>\n </li>\n <li>\n <p>And finally, bind <code>nameEl</code> to the <code>&lt;input&gt;</code> field by updating it like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;input\n bind:value={name}\n bind:this={nameEl}\n type=\"text\"\n id=\"todo-{todo.id}\"\n autocomplete=\"off\"\n class=\"todo-text\" /&gt;\n</code></pre></div>\n </li>\n <li>\n <p>However, when you try the updated app, you'll get an error along the lines of \"TypeError: nameEl is undefined\" in the console when you press a to-do's <em>Edit</em> button.</p>\n </li>\n</ol>\n<p>So, what is happening here? When you update a component's state in Svelte, it doesn't update the DOM immediately. Instead, it waits until the next microtask to see if there are any other changes that need to be applied, including in other components. Doing so avoids unnecessary work and allows the browser to batch things more effectively.</p>\n<p>In this case, when <code>editing</code> is <code>false</code>, the edit <code>&lt;input&gt;</code> is not visible because it does not exist in the DOM. Inside the <code>onEdit()</code> function we set <code>editing = true</code> and immediately afterwards try to access the <code>nameEl</code> variable and execute <code>nameEl.focus()</code>. The problem here is that Svelte hasn't yet updated the DOM.</p>\n<p>One way to solve this problem is to use <a href=\"/en-US/docs/Web/API/Window/setTimeout\" title=\"setTimeout()\"><code>setTimeout()</code></a> to delay the call to <code>nameEl.focus()</code> until the next event cycle, and give Svelte the opportunity to update the DOM.</p>\n<p>Try this now:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function onEdit() {\n editing = true; // enter editing mode\n setTimeout(() =&gt; nameEl.focus(), 0); // asynchronous call to set focus to name input\n}\n</code></pre></div>\n<p>The above solution works, but it is rather inelegant. Svelte provides a better way to handle these cases. The <a href=\"https://learn.svelte.dev/tutorial/tick\" class=\"external\" target=\"_blank\"><code>tick()</code> function</a> returns a promise that resolves as soon as any pending state changes have been applied to the DOM (or immediately, if there are no pending state changes). Let's try it now.</p>\n<ol>\n <li>\n <p>First of all, import <code>tick</code> at the top of the <code>&lt;script&gt;</code> section alongside your existing import:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>import { tick } from \"svelte\";\n</code></pre></div>\n </li>\n <li>\n <p>Next, call <code>tick()</code> with <a href=\"/en-US/docs/Web/JavaScript/Reference/Operators/await\"><code>await</code></a> from an <a href=\"/en-US/docs/Web/JavaScript/Reference/Statements/async_function\">async function</a>; update <code>onEdit()</code> like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>async function onEdit() {\n editing = true; // enter editing mode\n await tick();\n nameEl.focus();\n}\n</code></pre></div>\n </li>\n <li>\n <p>If you try it now you'll see that everything works as expected.</p>\n </li>\n</ol>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> To see another example using <code>tick()</code>, visit the <a href=\"https://learn.svelte.dev/tutorial/tick\" class=\"external\" target=\"_blank\">Svelte tutorial</a>.</p>\n</div>"}},{"type":"prose","value":{"id":"adding_functionality_to_html_elements_with_the_useaction_directive","title":"Adding functionality to HTML elements with the <code>use:action</code> directive","isH3":false,"content":"<p>Next up, we want the name <code>&lt;input&gt;</code> to automatically select all text on focus. Moreover, we want to develop this in such a way that it could be easily reused on any HTML <code>&lt;input&gt;</code> and applied in a declarative way. We will use this requirement as an excuse to show a very powerful feature that Svelte provides us to add functionality to regular HTML elements: <a href=\"https://svelte.dev/docs/svelte-action\" class=\"external\" target=\"_blank\">actions</a>.</p>\n<p>To select the text of a DOM input node, we have to call <a href=\"/en-US/docs/Web/API/HTMLInputElement/select\"><code>select()</code></a>. To get this function called whenever the node gets focused, we need an event listener along these lines:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>node.addEventListener(\"focus\", (event) =&gt; node.select());\n</code></pre></div>\n<p>And, in order to avoid memory leaks, we should also call the <a href=\"/en-US/docs/Web/API/EventTarget/removeEventListener\"><code>removeEventListener()</code></a> function when the node is destroyed.</p>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> All this is just standard WebAPI functionality; nothing here is specific to Svelte.</p>\n</div>\n<p>We could achieve all this in our <code>Todo</code> component whenever we add or remove the <code>&lt;input&gt;</code> from the DOM, but we would have to be very careful to add the event listener after the node has been added to the DOM, and remove the listener before the node is removed from the DOM. In addition, our solution would not be very reusable.</p>\n<p>That's where Svelte actions come into play. Basically they let us run a function whenever an element has been added to the DOM, and after removal from the DOM.</p>\n<p>In our immediate use case, we will define a function called <code>selectOnFocus()</code> that will receive a node as a parameter. The function will add an event listener to that node so that whenever it gets focused it will select the text. Then it will return an object with a <code>destroy</code> property. The <code>destroy</code> property is what Svelte will execute after removing the node from the DOM. Here we will remove the listener to make sure we don't leave any memory leak behind.</p>\n<ol>\n <li>\n <p>Let's create the function <code>selectOnFocus()</code>. Add the following to the bottom of the <code>&lt;script&gt;</code> section of <code>Todo.svelte</code>:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function selectOnFocus(node) {\n if (node &amp;&amp; typeof node.select === \"function\") {\n // make sure node is defined and has a select() method\n const onFocus = (event) =&gt; node.select(); // event handler\n node.addEventListener(\"focus\", onFocus); // when node gets focus call onFocus()\n return {\n destroy: () =&gt; node.removeEventListener(\"focus\", onFocus), // this will be executed when the node is removed from the DOM\n };\n }\n}\n</code></pre></div>\n </li>\n <li>\n <p>Now we need to tell the <code>&lt;input&gt;</code> to use that function with the <a href=\"https://svelte.dev/docs/element-directives#use-action\" class=\"external\" target=\"_blank\"><code>use:action</code></a> directive:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;input use:selectOnFocus /&gt;\n</code></pre></div>\n <p>With this directive we are telling Svelte to run this function, passing the DOM node of the <code>&lt;input&gt;</code> as a parameter, as soon as the component is mounted on the DOM. It will also be in charge of executing the <code>destroy</code> function when the component is removed from DOM. So with the <code>use</code> directive, Svelte takes care of the component's lifecycle for us.</p>\n <p>In our case, our <code>&lt;input&gt;</code> would end up like so: update the component's first label/input pair (inside the edit template) as follows:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;label for=\"todo-{todo.id}\" class=\"todo-label\"&gt;New name for '{todo.name}'&lt;/label&gt;\n&lt;input\n bind:value={name}\n bind:this={nameEl}\n use:selectOnFocus\n type=\"text\"\n id=\"todo-{todo.id}\"\n autocomplete=\"off\"\n class=\"todo-text\" /&gt;\n</code></pre></div>\n </li>\n <li>\n <p>Let's try it out. Go to your app, press a to-do's <em>Edit</em> button, then <kbd>Tab</kbd> to take focus away from the <code>&lt;input&gt;</code>. Now click on the <code>&lt;input&gt;</code>, and you'll see that the entire input text is selected.</p>\n </li>\n</ol>"}},{"type":"prose","value":{"id":"making_the_action_reusable","title":"Making the action reusable","isH3":true,"content":"<p>Now let's make this function truly reusable across components. <code>selectOnFocus()</code> is just a function without any dependency on the <code>Todo.svelte</code> component, so we can just extract it to a file and use it from there.</p>\n<ol>\n <li>\n <p>Create a new file, <code>actions.js</code>, inside the <code>src</code> folder.</p>\n </li>\n <li>\n <p>Give it the following content:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>export function selectOnFocus(node) {\n if (node &amp;&amp; typeof node.select === \"function\") {\n // make sure node is defined and has a select() method\n const onFocus = (event) =&gt; node.select(); // event handler\n node.addEventListener(\"focus\", onFocus); // when node gets focus call onFocus()\n return {\n destroy: () =&gt; node.removeEventListener(\"focus\", onFocus), // this will be executed when the node is removed from the DOM\n };\n }\n}\n</code></pre></div>\n </li>\n <li>\n <p>Now import it from inside <code>Todo.svelte</code>; add the following import statement just below the others:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>import { selectOnFocus } from \"../actions.js\";\n</code></pre></div>\n </li>\n <li>\n <p>And remove the <code>selectOnFocus()</code> definition from <code>Todo.svelte</code>, since we no longer need it there.</p>\n </li>\n</ol>"}},{"type":"prose","value":{"id":"reusing_our_action","title":"Reusing our action","isH3":true,"content":"<p>To demonstrate our action's reusability, let's make use of it in <code>NewTodo.svelte</code>.</p>\n<ol>\n <li>\n <p>Import <code>selectOnFocus()</code> from <code>actions.js</code> in this file too, as before:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>import { selectOnFocus } from \"../actions.js\";\n</code></pre></div>\n </li>\n <li>\n <p>Add the <code>use:selectOnFocus</code> directive to the <code>&lt;input&gt;</code>, like this:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;input\n bind:value={name}\n bind:this={nameEl}\n use:selectOnFocus\n type=\"text\"\n id=\"todo-0\"\n autocomplete=\"off\"\n class=\"input input__lg\" /&gt;\n</code></pre></div>\n </li>\n</ol>\n<p>With a few lines of code we can add functionality to regular HTML elements in a very reusable and declarative way. It just takes an <code>import</code> and a short directive like <code>use:selectOnFocus</code> that clearly describes its purpose. And we can achieve this without the need to create a custom wrapper element like <code>TextInput</code>, <code>MyInput</code>, or similar. Moreover, you can add as many <code>use:action</code> directives as you want to an element.</p>\n<p>Also, we didn't have to struggle with <code>onMount()</code>, <code>onDestroy()</code>, or <code>tick()</code> — the <code>use</code> directive takes care of the component lifecycle for us.</p>"}},{"type":"prose","value":{"id":"other_actions_improvements","title":"Other actions improvements","isH3":true,"content":"<p>In the previous section, while working with the <code>Todo</code> components, we had to deal with <code>bind:this</code>, <code>tick()</code>, and <code>async</code> functions just to give focus to our <code>&lt;input&gt;</code> as soon as it was added to the DOM.</p>\n<ol>\n <li>\n <p>This is how we can implement it with actions instead:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const focusOnInit = (node) =&gt;\n node &amp;&amp; typeof node.focus === \"function\" &amp;&amp; node.focus();\n</code></pre></div>\n </li>\n <li>\n <p>And then in our markup we just need to add another <code>use:</code> directive:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;input bind:value={name} use:selectOnFocus use:focusOnInit /&gt;\n</code></pre></div>\n </li>\n <li>\n <p>Our <code>onEdit()</code> function can now be much simpler:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function onEdit() {\n editing = true; // enter editing mode\n}\n</code></pre></div>\n </li>\n</ol>\n<p>As a last example before we move on, let's go back to our <code>Todo.svelte</code> component and give focus to the <em>Edit</em> button after the user presses <em>Save</em> or <em>Cancel</em>.</p>\n<p>We could try just reusing our <code>focusOnInit</code> action again, adding <code>use:focusOnInit</code> to the <em>Edit</em> button. But we'd be introducing a subtle bug. When you add a new to-do, the focus will be put on the <em>Edit</em> button of the recently added to-do. That's because the <code>focusOnInit</code> action is running when the component is created.</p>\n<p>That's not what we want — we want the <em>Edit</em> button to receive focus only when the user has pressed <em>Save</em> or <em>Cancel</em>.</p>\n<ol>\n <li>\n <p>So, go back to your <code>Todo.svelte</code> file.</p>\n </li>\n <li>\n <p>First we'll create a flag named <code>editButtonPressed</code> and initialize it to <code>false</code>. Add this just below your other variable definitions:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let editButtonPressed = false; // track if edit button has been pressed, to give focus to it after cancel or save\n</code></pre></div>\n </li>\n <li>\n <p>Next we'll modify the <em>Edit</em> button's functionality to save this flag, and create the action for it. Update the <code>onEdit()</code> function like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function onEdit() {\n editButtonPressed = true; // user pressed the Edit button, focus will come back to the Edit button\n editing = true; // enter editing mode\n}\n</code></pre></div>\n </li>\n <li>\n <p>Below it, add the following definition for <code>focusEditButton()</code>:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const focusEditButton = (node) =&gt; editButtonPressed &amp;&amp; node.focus();\n</code></pre></div>\n </li>\n <li>\n <p>Finally we <code>use</code> the <code>focusEditButton</code> action on the <em>Edit</em> button, like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;button type=\"button\" class=\"btn\" on:click={onEdit} use:focusEditButton&gt;\n Edit&lt;span class=\"visually-hidden\"&gt; {todo.name}&lt;/span&gt;\n&lt;/button&gt;\n</code></pre></div>\n </li>\n <li>\n <p>Go back and try your app again. At this point, every time the <em>Edit</em> button is added to the DOM, the <code>focusEditButton</code> action is executed, but it will only give focus to the button if the <code>editButtonPressed</code> flag is <code>true</code>.</p>\n </li>\n</ol>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> We have barely scratched the surface of actions here. Actions can also have reactive parameters, and Svelte lets us detect when any of those parameters change. So we can add functionality that integrates nicely with the Svelte reactive system. For a more detailed introduction to actions, consider checking out the <a href=\"https://learn.svelte.dev/tutorial/actions\" class=\"external\" target=\"_blank\">Svelte Interactive tutorial</a> or the <a href=\"https://svelte.dev/docs/element-directives#use-action\" class=\"external\" target=\"_blank\">Svelte <code>use:action</code> documentation</a>.</p>\n</div>"}},{"type":"prose","value":{"id":"component_binding_exposing_component_methods_and_variables_using_the_bindthiscomponent_directive","title":"Component binding: exposing component methods and variables using the <code>bind:this={component}</code> directive","isH3":false,"content":"<p>There's still one accessibility annoyance left. When the user presses the <em>Delete</em> button, the focus vanishes.</p>\n<p>The last feature we will be looking at in this article involves setting the focus on the status heading after a to-do has been deleted.</p>\n<p>Why the status heading? In this case, the element that had the focus has been deleted, so there's not a clear candidate to receive focus. We've picked the status heading because it's near the list of to-dos, and it's a way to give a visual feedback about the removal of the task, as well as indicating what's happened to screen reader users.</p>\n<p>First we'll extract the status heading to its own component.</p>\n<ol>\n <li>\n <p>Create a new file, <code>components/TodosStatus.svelte</code>.</p>\n </li>\n <li>\n <p>Add the following contents to it:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n export let todos;\n\n $: totalTodos = todos.length;\n $: completedTodos = todos.filter((todo) =&gt; todo.completed).length;\n&lt;/script&gt;\n\n&lt;h2 id=\"list-heading\"&gt;\n {completedTodos} out of {totalTodos} items completed\n&lt;/h2&gt;\n</code></pre></div>\n </li>\n <li>\n <p>Import the file at the beginning of <code>Todos.svelte</code>, adding the following <code>import</code> statement below the others:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>import TodosStatus from \"./TodosStatus.svelte\";\n</code></pre></div>\n </li>\n <li>\n <p>Replace the <code>&lt;h2&gt;</code> status heading inside <code>Todos.svelte</code> with a call to the <code>TodosStatus</code> component, passing <code>todos</code> to it as a prop, like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;TodosStatus {todos} /&gt;\n</code></pre></div>\n </li>\n <li>\n <p>You can also do a bit of cleanup, removing the <code>totalTodos</code> and <code>completedTodos</code> variables from <code>Todos.svelte</code>. Just remove the <code>$: totalTodos = …</code> and the <code>$: completedTodos = …</code> lines, and also remove the reference to <code>totalTodos</code> when we calculate <code>newTodoId</code> and use <code>todos.length</code> instead. To do this, replace the block that begins with <code>let newTodoId</code> with this:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>$: newTodoId = todos.length ? Math.max(...todos.map((t) =&gt; t.id)) + 1 : 1;\n</code></pre></div>\n </li>\n <li>\n <p>Everything works as expected — we just extracted the last piece of markup to its own component.</p>\n </li>\n</ol>\n<p>Now we need to find a way to give focus to the <code>&lt;h2&gt;</code> status label after a to-do has been removed.</p>\n<p>So far we saw how to send information to a component via props, and how a component can communicate with its parent by emitting events or using two-way data binding. The child component could get a reference to the <code>&lt;h2&gt;</code> node <code>using bind:this={dom_node}</code> and expose it to the outside using two-way data binding. But doing so would break the component encapsulation; setting focus on it should be its own responsibility.</p>\n<p>So we need the <code>TodosStatus</code> component to expose a method that its parent can call to give focus to it. It's a very common scenario that a component needs to expose some behavior or information to the consumer; let's see how to achieve it with Svelte.</p>\n<p>We've already seen that Svelte uses <code>export let varname = …</code> to <a href=\"https://svelte.dev/docs/svelte-components#script-1-export-creates-a-component-prop\" class=\"external\" target=\"_blank\">declare props</a>. But if instead of using <code>let</code> you export a <code>const</code>, <code>class</code>, or <code>function</code>, it is read-only outside the component. Function expressions are valid props, however. In the following example, the first three declarations are props, and the rest are exported values:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n export let bar = \"optional default initial value\"; // prop\n export let baz = undefined; // prop\n export let format = (n) =&gt; n.toFixed(2); // prop\n\n // these are readonly\n export const thisIs = \"readonly\"; // read-only export\n\n export function greet(name) {\n // read-only export\n alert(`Hello, ${name}!`);\n }\n\n export const greet = (name) =&gt; alert(`Hello, ${name}!`); // read-only export\n&lt;/script&gt;\n</code></pre></div>\n<p>With this in mind, let's go back to our use case. We'll create a function called <code>focus()</code> that gives focus to the <code>&lt;h2&gt;</code> heading. For that we'll need a <code>headingEl</code> variable to hold the reference to the DOM node, and we'll have to bind it to the <code>&lt;h2&gt;</code> element using <code>bind:this={headingEl}</code>. Our focus method will just run <code>headingEl.focus()</code>.</p>\n<ol>\n <li>\n <p>Update the contents of <code>TodosStatus.svelte</code> like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;script&gt;\n export let todos;\n\n $: totalTodos = todos.length;\n $: completedTodos = todos.filter((todo) =&gt; todo.completed).length;\n\n let headingEl;\n\n export function focus() {\n // shorter version: export const focus = () =&gt; headingEl.focus()\n headingEl.focus();\n }\n&lt;/script&gt;\n\n&lt;h2 id=\"list-heading\" bind:this={headingEl} tabindex=\"-1\"&gt;\n {completedTodos} out of {totalTodos} items completed\n&lt;/h2&gt;\n</code></pre></div>\n <p>Note that we've added a <code>tabindex</code> attribute to the <code>&lt;h2&gt;</code> to allow the element to receive focus programmatically.</p>\n <p>As we saw earlier, using the <code>bind:this={headingEl}</code> directive gives us a reference to the DOM node in the variable <code>headingEl</code>. Then we use <code>export function focus()</code> to expose a function that gives focus to the <code>&lt;h2&gt;</code> heading.</p>\n <p>How can we access those exported values from the parent? Just as you can bind to DOM elements with the <code>bind:this={dom_node}</code> directive, you can also bind to component instances themselves with <code>bind:this={component}</code>. So, when you use <code>bind:this</code> on an HTML element, you get a reference to the DOM node, and when you do it on a Svelte component, you get a reference to the instance of that component.</p>\n </li>\n <li>\n <p>So to bind to the instance of <code>TodosStatus</code>, we'll first create a <code>todosStatus</code> variable in <code>Todos.svelte</code>. Add the following line below your <code>import</code> statements:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let todosStatus; // reference to TodosStatus instance\n</code></pre></div>\n </li>\n <li>\n <p>Next, add a <code>bind:this={todosStatus}</code> directive to the call, as follows:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">svelte</span></div><pre class=\"brush: svelte notranslate\"><code>&lt;!-- TodosStatus --&gt;\n&lt;TodosStatus bind:this={todosStatus} {todos} /&gt;\n</code></pre></div>\n </li>\n <li>\n <p>Now we can call the <code>exported focus()</code> method from our <code>removeTodo()</code> function:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function removeTodo(todo) {\n todos = todos.filter((t) =&gt; t.id !== todo.id);\n todosStatus.focus(); // give focus to status heading\n}\n</code></pre></div>\n </li>\n <li>\n <p>Go back to your app. Now if you delete any to-do, the status heading will be focussed. This is useful to highlight the change in numbers of to-dos, both to sighted users and screen reader users.</p>\n </li>\n</ol>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> You might be wondering why we need to declare a new variable for component binding. Why can't we just call <code>TodosStatus.focus()</code>? You might have multiple <code>TodosStatus</code> instances active, so you need a way to reference each particular instance. That's why you have to specify a variable to bind each specific instance to.</p>\n</div>"}},{"type":"prose","value":{"id":"the_code_so_far","title":"The code so far","isH3":false,"content":""}},{"type":"prose","value":{"id":"git_2","title":"Git","isH3":true,"content":"<p>To see the state of the code as it should be at the end of this article, access your copy of our repo like this:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>cd mdn-svelte-tutorial/06-stores\n</code></pre></div>\n<p>Or directly download the folder's content:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>npx degit opensas/mdn-svelte-tutorial/06-stores\n</code></pre></div>\n<p>Remember to run <code>npm install &amp;&amp; npm run dev</code> to start your app in development mode.</p>"}},{"type":"prose","value":{"id":"repl_2","title":"REPL","isH3":true,"content":"<p>To see the current state of the code in a REPL, visit:</p>\n<p><a href=\"https://svelte.dev/repl/d1fa84a5a4494366b179c87395940039?version=3.23.2\" class=\"external\" target=\"_blank\">https://svelte.dev/repl/d1fa84a5a4494366b179c87395940039?version=3.23.2</a></p>"}},{"type":"prose","value":{"id":"summary","title":"Summary","isH3":false,"content":"<p>In this article we have finished adding all the required functionality to our app, plus we've taken care of a number of accessibility and usability issues. We also finished splitting our app into manageable components, each one with a unique responsibility.</p>\n<p>In the meantime, we saw a few advanced Svelte techniques, like:</p>\n<ul>\n <li>Dealing with reactivity gotchas when updating objects and arrays</li>\n <li>Working with DOM nodes using <code>bind:this={dom_node}</code> (binding DOM elements)</li>\n <li>Using the component lifecycle <code>onMount()</code> function</li>\n <li>Forcing Svelte to resolve pending state changes with the <code>tick()</code> function</li>\n <li>Adding functionality to HTML elements in a reusable and declarative way with the <code>use:action</code> directive</li>\n <li>Accessing component methods using <code>bind:this={component}</code> (binding components)</li>\n</ul>\n<p>In the next article we will see how to use stores to communicate between components, and add animations to our components.</p><ul class=\"prev-next\">\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components\"><span class=\"button-wrap\"> Previous </span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks\"><span class=\"button-wrap\"> Overview: Understanding client-side JavaScript frameworks</span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores\"><span class=\"button-wrap\"> Next </span></a></li>\n</ul>"}}],"toc":[{"text":"Code along with us","id":"code_along_with_us"},{"text":"Working on the MoreActions component","id":"working_on_the_moreactions_component"},{"text":"Reactivity gotchas: updating objects and arrays","id":"reactivity_gotchas_updating_objects_and_arrays"},{"text":"Finishing our MoreActions component","id":"finishing_our_moreactions_component"},{"text":"Working with the DOM: focusing on the details","id":"working_with_the_dom_focusing_on_the_details"},{"text":"Exploring keyboard accessibility issues in our to-do app","id":"exploring_keyboard_accessibility_issues_in_our_to-do_app"},{"text":"Creating a NewTodo component","id":"creating_a_newtodo_component"},{"text":"Working with DOM nodes using the <code>bind:this={dom_node}</code> directive","id":"working_with_dom_nodes_using_the_bindthisdom_node_directive"},{"text":"Component lifecycle, and the <code>onMount()</code> function","id":"component_lifecycle_and_the_onmount_function"},{"text":"Waiting for the DOM to be updated with the <code>tick()</code> function","id":"waiting_for_the_dom_to_be_updated_with_the_tick_function"},{"text":"Adding functionality to HTML elements with the <code>use:action</code> directive","id":"adding_functionality_to_html_elements_with_the_useaction_directive"},{"text":"Component binding: exposing component methods and variables using the <code>bind:this={component}</code> directive","id":"component_binding_exposing_component_methods_and_variables_using_the_bindthiscomponent_directive"},{"text":"The code so far","id":"the_code_so_far"},{"text":"Summary","id":"summary"}],"summary":"In this article we have finished adding all the required functionality to our app, plus we've taken care of a number of accessibility and usability issues. We also finished splitting our app into manageable components, each one with a unique responsibility.","popularity":0,"modified":"2024-10-07T09:37:56.000Z","other_translations":[{"locale":"de","title":"Fortgeschrittenes Svelte: Reaktivität, Lebenszyklus, Barrierefreiheit","native":"Deutsch"},{"locale":"zh-CN","title":"Svelte 进阶:响应式、生命周期以及无障碍","native":"中文 (简体)"}],"pageType":"learn-module-chapter","source":{"folder":"en-us/learn/tools_and_testing/client-side_javascript_frameworks/svelte_reactivity_lifecycle_accessibility","github_url":"https://github.com/mdn/content/blob/main/files/en-us/learn/tools_and_testing/client-side_javascript_frameworks/svelte_reactivity_lifecycle_accessibility/index.md","last_commit_url":"https://github.com/mdn/content/commit/1b4e6d1156e8471d38deeea1567c35ef412c5f42","filename":"index.md"},"short_title":"Advanced Svelte: Reactivity, lifecycle, accessibility","parents":[{"uri":"/en-US/docs/Learn","title":"Guides"},{"uri":"/en-US/docs/Learn/Tools_and_testing","title":"Tools and testing"},{"uri":"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks","title":"Understanding client-side JavaScript frameworks"},{"uri":"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility","title":"Advanced Svelte: Reactivity, lifecycle, accessibility"}],"pageTitle":"Advanced Svelte: Reactivity, lifecycle, accessibility - Learn web development | MDN","noIndexing":false}}</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10