[{"data":1,"prerenderedAt":213},["ShallowReactive",2],{"DefaultLayouten":3,"language-blog-slug-building-a-progressively-enhanced-autocomplete-field-i18n-slugs":134,"language-blog-slug-en-building-a-progressively-enhanced-autocomplete-field":138},{"app":4,"menu":31,"footer":66},{"githubUrl":5,"youtubeUrl":6,"linkedinUrl":7,"phoneNumber":8,"emailAddress":9,"legal":10,"addresses":20},"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002F","https:\u002F\u002Fwww.youtube.com\u002Fchannel\u002FUCzHuhQVYFRixtQN2-swcuGg","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fde-voorhoede","+31 20 2610 954","post@voorhoede.nl",[11,14,17],{"title":12,"value":13},"KvK","56017235",{"title":15,"value":16},"BTW","NL851944620B01",{"title":18,"value":19},"IBAN","NL14TRIO0320142078",[21,26],{"address":22,"city":23,"googleMapsLink":24,"postalCode":25},"Koivistokade 70","Amsterdam","https:\u002F\u002Fwww.google.com\u002Fmaps\u002Fplace\u002FDe+Voorhoede+%7C+Front-end+Development\u002F@52.396847,4.8700823,17z\u002Fdata=!3m1!4b1!4m5!3m4!1s0x47c5e21d502d2d59:0xbf570944a96ebf45!8m2!3d52.347647!4d4.8502154","1013 BB",{"address":27,"city":28,"googleMapsLink":29,"postalCode":30},"Koornmarkt 22","Delft","https:\u002F\u002Fwww.google.nl\u002Fmaps\u002Fplace\u002FKoornmarkt+22,+2611+EG+Delft\u002F@52.0093477,4.3573054,17z\u002F","2611 EG",{"title":32,"callToActions":33,"links":39},"Site Menu",[34],{"id":35,"title":36,"link":37},"163140902","Contact",{"__typename":38},"ContactRecord",[40,46,51,56,61],{"id":41,"title":42,"link":43},"163140904","Impact",{"__typename":44,"slug":45},"PageRecord","impact",{"id":47,"title":48,"link":49},"163140905","Services",{"__typename":50},"ServiceOverviewRecord",{"id":52,"title":53,"link":54},"163140906","Cases",{"__typename":55},"CaseOverviewRecord",{"id":57,"title":58,"link":59},"163140908","About us",{"__typename":44,"slug":60},"about-us",{"id":62,"title":63,"link":64},"d6WdFJq2SOuc3dWtpibbXQ","Work at",{"__typename":44,"slug":65},"work-at",{"links":67,"copyrightTitle":93,"copyrightLabel":94,"copyrightLink":95,"privacyTitle":96,"privacyLabel":97,"privacyLink":98,"certificatesGrid":99},[68,71,74,77,82,85,88],{"id":69,"title":42,"link":70},"144185264",{"__typename":44,"slug":45},{"id":72,"title":48,"link":73},"144185265",{"__typename":50},{"id":75,"title":53,"link":76},"144185266",{"__typename":55},{"id":78,"title":79,"link":80},"144185267","Blog",{"__typename":81},"BlogPostOverviewRecord",{"id":83,"title":58,"link":84},"144185268",{"__typename":44,"slug":60},{"id":86,"title":36,"link":87},"144185269",{"__typename":38},{"id":89,"title":90,"link":91},"144185270","FAQ",{"__typename":44,"slug":92},"faq","Creative Commons licence and disclaimer","CC BY 4.0","https:\u002F\u002Fcreativecommons.org\u002Flicenses\u002Fby\u002F4.0\u002F","De Voorhoede privacy statement (pdf)","Privacy statement","https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1763455455-vh-isms-006-privacy-statement-de-voorhoede-en.pdf",[100,112,123],{"id":101,"image":102,"link":107},"Xq4bBfg_TZ6Fkjax9mkbLQ",{"url":103,"alt":104,"width":105,"height":106},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353463-b-corp-logo-black-rgb.png","B Corp logo",404,680,{"__typename":108,"id":109,"title":110,"url":111},"ExternalLinkRecord","fGW1ak8XQYaYDLkBSyncog","B Corp","https:\u002F\u002Fwww.bcorporation.net\u002Fen-us\u002Ffind-a-b-corp\u002Fcompany\u002Fde-voorhoede\u002F",{"id":113,"image":114,"link":119},"c5mCXRTiSraRIB25fw1p7Q",{"url":115,"alt":116,"width":117,"height":118},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353461-dda-boxlogo-black.png","Dutch Digital Agencies logo",627,480,{"__typename":108,"id":120,"title":121,"url":122},"P6Jh7B0cTv2cKyNEeKVWVQ","Dutch Digital Agencies","https:\u002F\u002Fdutchdigitalagencies.com\u002Fleden\u002Fde-voorhoede\u002F",{"id":124,"image":125,"link":129},"MT5SCyNxSTSr_v5eeATMZw",{"url":126,"alt":127,"width":128,"height":128},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1775730283-dnv.png","DNV logo",518,{"id":130,"title":131,"link":132},"BRtNB5HnT5i-7HkA8IYzBw","DIV",{"__typename":44,"slug":133},"impact\u002Fdigitale-producten-privacy-by-design",[135],{"locale":136,"value":137},"en","building-a-progressively-enhanced-autocomplete-field",{"page":139},{"slug":137,"i18nSlugs":140,"social":142,"title":143,"subtitle":79,"isArchived":146,"headerIllustration":145,"date":147,"authors":148,"introTitle":157,"items":158,"pivots":200,"relatedBlogPosts":211,"tags":212,"onMountedScript":151,"onUnmountedScript":151},[141],{"locale":136,"value":137},{"title":143,"description":144,"image":145},"Building a progressively enhanced autocomplete field","Progressive enhancement and accessibility in practice",null,false,"2017-04-07T02:00:00.000+02:00",[149],{"name":150,"lastName":151,"slug":152,"image":153},"Jeroen","","jeroen",{"url":154,"alt":145,"width":155,"height":156},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1683534636-placeholder.jpg",1235,1646,"Progressive enhancement and accessibility in practice: adding functionality without excluding anyone",[159,163,167,172,175,179,183,186,190,193,196],{"__typename":160,"id":161,"title":151,"body":162},"TextSectionRecord","406408","\u003Cp>We always like to build our projects with progressive enhancement in mind. Simply put we start with a basic experience and enhance it when possible. At one of our projects we got the request to create a search functionality which gives suggestions while the user starts typing a search query. This blog post describes how to do this\u003Cspan>&nbsp;\u003C\u002Fspan>\u003Cem>by the book\u003C\u002Fem>.\u003C\u002Fp>",{"__typename":160,"id":164,"title":165,"body":166},"406410","Start simple","\u003Cp>The autocomplete component is essentially an\u003Cspan>&nbsp;\u003C\u002Fspan>\u003Ccode>&lt;input type=\"search\"&gt;\u003C\u002Fcode> element. Only when the JavaScript features we need are supported, we add the extra functionality.\u003C\u002Fp>\n\u003Cp>In our case we test the availability of our DOM helpers, such as\u003Cspan>&nbsp;\u003C\u002Fspan>\u003Ccode>querySelectorAll\u003C\u002Fcode>, and the\u003Cspan>&nbsp;\u003C\u002Fspan>\u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen\u002Fdocs\u002FWeb\u002FAPI\u002FFetch_API\">Fetch API\u003C\u002Fa>\u003Cspan>&nbsp;\u003C\u002Fspan>like so:\u003C\u002Fp>",{"__typename":168,"id":169,"language":170,"body":171},"CodeBlockRecord","406411","javascript","const isSupported = dom().isSupported && ('fetch' in window);",{"__typename":160,"id":173,"title":151,"body":174},"406412","\u003Cp>\u003Cspan>Once this test passes we continue to instantiate the rest of the autocomplete component.\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":160,"id":176,"title":177,"body":178},"406414","Describe states and roles for accessibility","\u003Cp>\u003Cspan>To keep the autocomplete accessible we add various&nbsp;\u003C\u002Fspan>\u003Ca href=\"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fwai-aria-1.1\u002F#roles\">ARIA Roles\u003C\u002Fa>\u003Cspan>&nbsp;to help user who use assistive technologies like screen readers for example. The markup for the input has additional&nbsp;\u003C\u002Fspan>\u003Ca href=\"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fwai-aria-1.1\u002F#states_and_properties\">ARIA attributes\u003C\u002Fa>\u003Cspan>&nbsp;to give any assistive technology an indication something changed in another part of the page.\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":168,"id":180,"language":181,"body":182},"406415","markup","\u003Cinput type=\"search\"\n    name=\"autocomplete-query\"\n    role=\"combobox\"\n    aria-expanded=\"false\"\n    aria-autocomplete=\"list\"\n    aria-owns=\"autocomplete-list\"\n    aria-activedescendant=\"\"\n    ...>",{"__typename":160,"id":184,"title":151,"body":185},"406416","\u003Cp>\u003Cspan>The search results get displayed in an&nbsp;\u003C\u002Fspan>\u003Ccode>&lt;ol&gt;\u003C\u002Fcode>\u003Cspan>&nbsp;element. This element has an ID attribute of&nbsp;\u003C\u002Fspan>\u003Ccode>autocomplete-list\u003C\u002Fcode>\u003Cspan>&nbsp;which is the value of the&nbsp;\u003C\u002Fspan>\u003Ccode>aria-owns\u003C\u002Fcode>\u003Cspan>&nbsp;attribute, connecting these elements. When a user types a search query the results get displayed and the&nbsp;\u003C\u002Fspan>\u003Ccode>aria-expanded\u003C\u002Fcode>\u003Cspan>&nbsp;attribute will change to &ldquo;true&rdquo;. Lastly, the&nbsp;\u003C\u002Fspan>\u003Ccode>aria-activedescendant\u003C\u002Fcode>\u003Cspan>&nbsp;will get the ID of the currently active result in the results list.\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":160,"id":187,"title":188,"body":189},"406417","Add Keyboard support","\u003Cp>To make the autocomplete component feel intuitive we need to make sure the up, down, enter, and escape keys are working as expected. When the autocomplete list is filled with suggestions, pressing the up and down arrow keys should move the focus up and down, respectively. Pressing enter will close the results and put the selected result in the input field, while the escape key closes the result list and clears the input field.\u003C\u002Fp>\n\u003Cp>To accomplish this we use the keyup event on the input search field and check which keys are pressed with a switch statement.\u003C\u002Fp>",{"__typename":168,"id":191,"language":170,"body":192},"406424","dom(this.input).on('keyup', (event) => {\n    switch (event.keyCode) {\n        case KEY_CODE.UP:\n            this.selectPrevItem();\n            break;\n        case KEY_CODE.DOWN:\n            this.selectNextItem();\n            break;\n        case KEY_CODE.ENTER:\n            this.close();\n            break;\n        default:\n            this.open();\n            this.resetSelection();\n            this.search();\n    }\n});",{"__typename":160,"id":194,"title":151,"body":195},"406425","\u003Cp>Pressing the escape key when focusing an input field removes the content, which in our case hides the result list and yields the desired behaviour.\u003C\u002Fp>",{"__typename":160,"id":197,"title":198,"body":199},"406426","In conclusion","\u003Cp>\u003Cspan>By starting with the bare functionality any user can still complete their task. Only when feature tests point out that we can execute the code for our component do we instantiate it. Additionally we make sure screen readers and other assistive technologies can make sense of our component by using ARIA roles, states and properties. This way we leave no-one behind when adding more functionality.\u003C\u002Fspan>\u003C\u002Fp>",[201],{"title":202,"body":203,"links":204,"mailchimpValue":151,"mailchimpName":151,"mailchimpId":151,"formType":151,"contactPerson":145},"Also in love with the web?","\u003Cp>For us, that’s about technology and user experience. Fast, available for all, enjoyable to use. And fun to build. This is how our team bands together, adhering to the same values, to make sure we achieve a solid result for clients both large and small. Does that fit you?\u003C\u002Fp>\n",[205],{"__typename":206,"id":207,"title":208,"link":209},"InternalLinkRecord","163140992","Join our team",{"__typename":44,"slug":210},"jobs",[],[],1776256150624]