{"id":439,"date":"2024-06-20T18:00:00","date_gmt":"2024-06-20T18:00:00","guid":{"rendered":"https:\/\/fdswebdesign.com\/?p=439"},"modified":"2024-10-15T23:32:28","modified_gmt":"2024-10-15T23:32:28","slug":"uniting-web-and-native-apps-with-4-unknown-javascript-apis","status":"publish","type":"post","link":"https:\/\/fdswebdesign.com\/index.php\/2024\/06\/20\/uniting-web-and-native-apps-with-4-unknown-javascript-apis\/","title":{"rendered":"Uniting Web And Native Apps With 4 Unknown JavaScript APIs"},"content":{"rendered":"

Uniting Web And Native Apps With 4 Unknown JavaScript APIs<\/title><\/p>\n<article>\n<header>\n<h1>Uniting Web And Native Apps With 4 Unknown JavaScript APIs<\/h1>\n<address>Juan Diego Rodr\u00edguez<\/address>\n<p> 2024-06-20T18:00:00+00:00<br \/>\n 2024-10-15T23:05:45+00:00<br \/>\n <\/header>\n<p>A couple of years ago, <a href=\"https:\/\/www.smashingmagazine.com\/2022\/09\/javascript-api-guide\/\">four JavaScript APIs that landed at the bottom of awareness in the State of JavaScript survey<\/a>. I took an interest in those APIs because they have so much potential to be useful but don\u2019t get the credit they deserve. Even after a quick search, I was amazed at how many new web APIs have been added to the ECMAScript specification that aren\u2019t getting their dues and with a lack of awareness and browser support in browsers.<\/p>\n<p>That situation can be a \u201ccatch-22\u201d:<\/p>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aAn%20API%20is%20interesting%20but%20lacks%20awareness%20due%20to%20incomplete%20support,%20and%20there%20is%20no%20immediate%20need%20to%20support%20it%20due%20to%20low%20awareness.%0a&url=https:\/\/smashingmagazine.com%2f2024%2f06%2funiting-web-native-apps-unknown-javascript-apis%2f\"><\/p>\n<p>An API is interesting but lacks awareness due to incomplete support, and there is no immediate need to support it due to low awareness.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>Most of these APIs are designed to power progressive web apps (PWA) and close the gap between web and native apps. Bear in mind that creating a PWA involves more than just adding a <a href=\"https:\/\/css-tricks.com\/how-to-transition-to-manifest-v3-for-chrome-extensions\/\">manifest file<\/a>. Sure, it\u2019s a PWA by definition, but it functions like a bookmark on your home screen in practice. In reality, we need several APIs to achieve a fully native app experience on the web. And the four APIs I\u2019d like to shed light on are part of that PWA puzzle that brings to the web what we once thought was only possible in native apps.<\/p>\n<p>You can see all these <a href=\"https:\/\/monknow.github.io\/pwa-features-demo\/\">APIs in action in this demo<\/a> as we go along.<\/p>\n<h2 id=\"1-screen-orientation-api\">1. Screen Orientation API<\/h2>\n<p>The <a href=\"https:\/\/www.w3.org\/TR\/screen-orientation\/\">Screen Orientation API<\/a> can be used to sniff out the device\u2019s current orientation. Once we know whether a user is browsing in a portrait or landscape orientation, we can use it to <strong>enhance the UX for mobile devices<\/strong> by changing the UI accordingly. We can also use it to <strong>lock the screen in a certain position<\/strong>, which is useful for displaying videos and other full-screen elements that benefit from a wider viewport.<\/p>\n<p>Using the global <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window\/screen\"><code>screen<\/code><\/a> object, you can access various properties the screen uses to render a page, including the <code>screen.orientation<\/code> object. It has two properties:<\/p>\n<ul>\n<li><strong><code>type<\/code>:<\/strong> The current screen orientation. It can be: <code>"portrait-primary"<\/code>, <code>"portrait-secondary"<\/code>, <code>"landscape-primary"<\/code>, or <code>"landscape-secondary"<\/code>.<\/li>\n<li><strong><code>angle<\/code>:<\/strong> The current screen orientation angle. It can be any number from 0 to 360 degrees, but it\u2019s normally set in multiples of 90 degrees (e.g., <code>0<\/code>, <code>90<\/code>, <code>180<\/code>, or <code>270<\/code>).<\/li>\n<\/ul>\n<p>On mobile devices, if the <code>angle<\/code> is <code>0<\/code> degrees, the <code>type<\/code> is most often going to evaluate to <code>"portrait"<\/code> (vertical), but on desktop devices, it is typically <code>"landscape"<\/code> (horizontal). This makes the <code>type<\/code> property precise for knowing a device\u2019s true position.<\/p>\n<p>The <code>screen.orientation<\/code> object also has two methods:<\/p>\n<ul>\n<li><strong><code>.lock()<\/code>:<\/strong> This is an async method that takes a <code>type<\/code> value as an argument to lock the screen.<\/li>\n<li><strong><code>.unlock()<\/code>:<\/strong> This method unlocks the screen to its default orientation.<\/li>\n<\/ul>\n<p>And lastly, <code>screen.orientation<\/code> counts with an <code>"orientationchange"<\/code> event to know when the orientation has changed.<\/p>\n<div data-audience=\"non-subscriber\" data-remove=\"true\" class=\"feature-panel-container\">\n<aside class=\"feature-panel\">\n<div class=\"feature-panel-left-col\">\n<div class=\"feature-panel-description\">\n<p><strong>Web forms<\/strong> are at the center of every meaningful interaction. Meet Adam Silver’s <strong><a href=\"https:\/\/www.smashingmagazine.com\/printed-books\/form-design-patterns\/\">Form Design Patterns<\/a><\/strong>, a practical guide to <strong>designing and building forms<\/strong> for the web.<\/p>\n<p><a data-instant href=\"https:\/\/www.smashingmagazine.com\/printed-books\/form-design-patterns\/\" class=\"btn btn--green btn--large\">Jump to table of contents \u21ac<\/a><\/div>\n<\/div>\n<div class=\"feature-panel-right-col\"><a data-instant href=\"https:\/\/www.smashingmagazine.com\/printed-books\/form-design-patterns\/\" class=\"feature-panel-image-link\"><\/p>\n<div class=\"feature-panel-image\">\n<img decoding=\"async\" loading=\"lazy\" class=\"feature-panel-image-img lazyload\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Feature Panel\" width=\"481\" height=\"698\" data-src=\"https:\/\/archive.smashing.media\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/51e0f837-d85d-4b28-bfab-1c9a47f0ce33\/form-design-patterns-shop-image.png\"><\/p>\n<\/div>\n<p><\/a>\n<\/div>\n<\/aside>\n<\/div>\n<h3 id=\"browser-support\">Browser Support<\/h3>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/caniuse.com\/screen-orientation\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"289\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Browser Support on Screen Orientation API\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-api-support.jpg\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Source: <a href=\"https:\/\/caniuse.com\/screen-orientation\">Caniuse<\/a>. (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-api-support.jpg\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"finding-and-locking-screen-orientation\">Finding And Locking Screen Orientation<\/h3>\n<p>Let\u2019s code a short demo using the Screen Orientation API to know the device\u2019s orientation and lock it in its current position.<\/p>\n<p>This can be our HTML boilerplate:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-html\"><main>\n <p>\n Orientation Type: <span class=\"orientation-type\"><\/span>\n <br \/>\n Orientation Angle: <span class=\"orientation-angle\"><\/span>\n <\/p>\n\n <button type=\"button\" class=\"lock-button\">Lock Screen<\/button>\n\n <button type=\"button\" class=\"unlock-button\">Unlock Screen<\/button>\n\n <button type=\"button\" class=\"fullscreen-button\">Go Full Screen<\/button>\n<\/main>\n<\/code><\/pre>\n<\/div>\n<p>On the JavaScript side, we inject the screen orientation <code>type<\/code> and <code>angle<\/code> properties into our HTML.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">let currentOrientationType = document.querySelector(\".orientation-type\");\nlet currentOrientationAngle = document.querySelector(\".orientation-angle\");\n\ncurrentOrientationType.textContent = screen.orientation.type;\ncurrentOrientationAngle.textContent = screen.orientation.angle;\n<\/code><\/pre>\n<\/div>\n<p>Now, we can see the device\u2019s orientation and angle properties. On my laptop, they are <code>"landscape-primary"<\/code> and <code>0\u00b0<\/code>.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-1.png\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"367\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Screen Orientation type and angle being displayed\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-1.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-1.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>If we listen to the window\u2019s <code>orientationchange<\/code> event, we can see how the values are updated each time the screen rotates.<\/p>\n<pre><code class=\"language-javascript\">window.addEventListener(\"orientationchange\", () => {\n currentOrientationType.textContent = screen.orientation.type;\n currentOrientationAngle.textContent = screen.orientation.angle;\n});\n<\/code><\/pre>\n<figure class=\"\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-2.png\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"679\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Screen Orientation type and angle are displayed in portrait mode\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-2.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/screen-orientation-2.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>To lock the screen, we need to first be in full-screen mode, so we will use another extremely useful feature: the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Fullscreen_API\"><strong>Fullscreen API<\/strong><\/a>. Nobody wants a webpage to pop into full-screen mode without their consent, so we need <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Security\/User_activation#transient_activation\">transient activation<\/a> (i.e., a user click) from a DOM element to work.<\/p>\n<p>The Fullscreen API has two methods:<\/p>\n<ol>\n<li><code>Document.exitFullscreen()<\/code> is used from the global document object,<\/li>\n<li><code>Element.requestFullscreen()<\/code> makes the specified element and its descendants go full-screen.<\/li>\n<\/ol>\n<p>We want the entire page to be full-screen so we can invoke the method from the root element at the <code>document.documentElement<\/code> object:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const fullscreenButton = document.querySelector(\".fullscreen-button\");\n\nfullscreenButton.addEventListener(\"click\", async () => {\n \/\/ If it is already in full-screen, exit to normal view\n if (document.fullscreenElement) {\n await document.exitFullscreen();\n } else {\n await document.documentElement.requestFullscreen();\n }\n});\n<\/code><\/pre>\n<\/div>\n<p>Next, we can lock the screen in its current orientation:<\/p>\n<pre><code class=\"language-javascript\">const lockButton = document.querySelector(\".lock-button\");\n\nlockButton.addEventListener(\"click\", async () => {\n try {\n await screen.orientation.lock(screen.orientation.type);\n } catch (error) {\n console.error(error);\n }\n});\n<\/code><\/pre>\n<p>And do the opposite with the unlock button:<\/p>\n<pre><code class=\"language-javascript\">const unlockButton = document.querySelector(\".unlock-button\");\n\nunlockButton.addEventListener(\"click\", () => {\n screen.orientation.unlock();\n});\n<\/code><\/pre>\n<h3 id=\"can-t-we-check-orientation-with-a-media-query\">Can\u2019t We Check Orientation With a Media Query?<\/h3>\n<p>Yes! We can indeed check page orientation via the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/@media\/orientation\"><code>orientation<\/code> media feature<\/a> in a CSS media query. However, media queries compute the current orientation by checking if the width is \u201cbigger than the height\u201d for landscape or \u201csmaller\u201d for portrait. By contrast,<\/p>\n<blockquote class=\"pull-quote\">\n<p>\n <a class=\"pull-quote__link\" aria-label=\"Share on Twitter\" href=\"https:\/\/twitter.com\/share?text=%0aThe%20Screen%20Orientation%20API%20checks%20for%20the%20screen%20rendering%20the%20page%20regardless%20of%20the%20viewport%20dimensions,%20making%20it%20resistant%20to%20inconsistencies%20that%20may%20crop%20up%20with%20page%20resizing.%0a&url=https:\/\/smashingmagazine.com%2f2024%2f06%2funiting-web-native-apps-unknown-javascript-apis%2f\"><\/p>\n<p>The Screen Orientation API checks for the screen rendering the page regardless of the viewport dimensions, making it resistant to inconsistencies that may crop up with page resizing.<\/p>\n<p> <\/a>\n <\/p>\n<div class=\"pull-quote__quotation\">\n<div class=\"pull-quote__bg\">\n <span class=\"pull-quote__symbol\">\u201c<\/span><\/div>\n<\/p><\/div>\n<\/blockquote>\n<p>You may have noticed how PWAs like Instagram and X force the screen to be in portrait mode even when the native system orientation is unlocked. It is important to notice that this behavior isn\u2019t achieved through the Screen Orientation API, but by setting the <code>orientation<\/code> property on the <code>manifest.json<\/code> file to the desired orientation type.<\/p>\n<h2 id=\"2-device-orientation-api\">2. Device Orientation API<\/h2>\n<p>Another API I\u2019d like to poke at is the Device Orientation API. It provides access to a device\u2019s gyroscope sensors to read the device\u2019s orientation in space; something used all the time in mobile apps, mainly games. The API makes this happen with a <code>deviceorientation<\/code> event that triggers each time the device moves. It has the following properties:<\/p>\n<ul>\n<li><strong><code>event.alpha<\/code>:<\/strong> Orientation along the Z-axis, ranging from 0 to 360 degrees.<\/li>\n<li><strong><code>event.beta<\/code>:<\/strong> Orientation along the X-axis, ranging from -180 to 180 degrees.<\/li>\n<li><strong><code>event.gamma<\/code>:<\/strong> Orientation along the Y-axis, ranging from -90 to 90 degrees.<\/li>\n<\/ul>\n<h3 id=\"browser-support-1\">Browser Support<\/h3>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/caniuse.com\/deviceorientation\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"293\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Browser Support on Device Orientation API\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-orientation-api-support.jpg\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Source: <a href=\"https:\/\/caniuse.com\/deviceorientation\">Caniuse<\/a>. (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-orientation-api-support.jpg\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"moving-elements-with-your-device\">Moving Elements With Your Device<\/h3>\n<p>In this case, we will make a 3D cube with CSS that can be rotated with your device! The full instructions I used to make the initial CSS cube are credited to <a href=\"https:\/\/desandro.com\">David DeSandro<\/a> and can be <a href=\"https:\/\/3dtransforms.desandro.com\/cube\">found in his introduction to 3D transforms<\/a>.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"vYwdMNJ\" data-user=\"smashingmag\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Rotate cube [forked]](https:\/\/codepen.io\/smashingmag\/pen\/vYwdMNJ) by <a href=\"https:\/\/codepen.io\/desandro\">Dave DeSandro<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/vYwdMNJ\">Rotate cube [forked]<\/a> by <a href=\"https:\/\/codepen.io\/desandro\">Dave DeSandro<\/a>.<\/figcaption><\/figure>\n<p>You can see raw full HTML in the demo, but let\u2019s print it here for posterity:<\/p>\n<pre><code class=\"language-html\"><main>\n <div class=\"scene\">\n <div class=\"cube\">\n <div class=\"cube__face cube__face--front\">1<\/div>\n <div class=\"cube__face cube__face--back\">2<\/div>\n <div class=\"cube__face cube__face--right\">3<\/div>\n <div class=\"cube__face cube__face--left\">4<\/div>\n <div class=\"cube__face cube__face--top\">5<\/div>\n <div class=\"cube__face cube__face--bottom\">6<\/div>\n <\/div>\n <\/div>\n <h1>Device Orientation API<\/h1>\n <p>\n Alpha: <span class=\"currentAlpha\"><\/span>\n <br \/>\n Beta: <span class=\"currentBeta\"><\/span>\n <br \/>\n Gamma: <span class=\"currentGamma\"><\/span>\n <\/p>\n<\/main>\n<\/code><\/pre>\n<p>To keep this brief, I won\u2019t explain the CSS code here. Just keep in mind that it provides the necessary styles for the 3D cube, and it can be rotated through all axes using the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/transform-function\/rotate\">CSS <code>rotate()<\/code> function<\/a>.<\/p>\n<p>Now, with JavaScript, we listen to the window\u2019s <code>deviceorientation<\/code> event and access the event orientation data:<\/p>\n<pre><code class=\"language-javascript\">const currentAlpha = document.querySelector(\".currentAlpha\");\nconst currentBeta = document.querySelector(\".currentBeta\");\nconst currentGamma = document.querySelector(\".currentGamma\");\n\nwindow.addEventListener(\"deviceorientation\", (event) => {\n currentAlpha.textContent = event.alpha;\n currentBeta.textContent = event.beta;\n currentGamma.textContent = event.gamma;\n});\n<\/code><\/pre>\n<p>To see how the data changes on a desktop device, we can open Chrome\u2019s DevTools and access the Sensors Panel to emulate a rotating device.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-motion-1.png\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"601\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Emulating a device rotating on the Chrome DevTools Sensor Panel\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-motion-1.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-motion-1.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>To rotate the cube, we change its CSS <code>transform<\/code> properties according to the device orientation data:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const currentAlpha = document.querySelector(\".currentAlpha\");\nconst currentBeta = document.querySelector(\".currentBeta\");\nconst currentGamma = document.querySelector(\".currentGamma\");\n\nconst cube = document.querySelector(\".cube\");\n\nwindow.addEventListener(\"deviceorientation\", (event) => {\n currentAlpha.textContent = event.alpha;\n currentBeta.textContent = event.beta;\n currentGamma.textContent = event.gamma;\n\n cube.style.transform = `rotateX(${event.beta}deg) rotateY(${event.gamma}deg) rotateZ(${event.alpha}deg)`;\n});\n<\/code><\/pre>\n<\/div>\n<p>This is the result:<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-motion-2.jpg\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"392\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Cube rotated according to emulated device orientation\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-motion-2.jpg\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/device-motion-2.jpg\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h2 id=\"3-vibration-api\">3. Vibration API<\/h2>\n<p>Let\u2019s turn our attention to the Vibration API, which, unsurprisingly, allows access to a device\u2019s vibrating mechanism. This comes in handy when we need to alert users with in-app notifications, like when a process is finished or a message is received. That said, we have to use it sparingly; no one wants their phone blowing up with notifications.<\/p>\n<p>There\u2019s just one method that the Vibration API gives us, and it\u2019s all we need: <code>navigator.vibrate()<\/code>.<\/p>\n<p><code>vibrate()<\/code> is available globally from the <code>navigator<\/code> object and takes an argument for how long a vibration lasts in milliseconds. It can be either a number or an array of numbers representing a patron of vibrations and pauses.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">navigator.vibrate(200); \/\/ vibrate 200ms\nnavigator.vibrate([200, 100, 200]); \/\/ vibrate 200ms, wait 100, and vibrate 200ms.\n<\/code><\/pre>\n<\/div>\n<h3 id=\"browser-support-2\">Browser Support<\/h3>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/caniuse.com\/vibration\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"290\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Browser Support on Vibration API\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/vibration-api-support.jpg\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Source: <a href=\"https:\/\/caniuse.com\/vibration\">Caniuse<\/a>. (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/vibration-api-support.jpg\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"vibration-api-demo\">Vibration API Demo<\/h3>\n<p>Let\u2019s make a quick demo where the user inputs how many milliseconds they want their device to vibrate and buttons to start and stop the vibration, starting with the markup:<\/p>\n<pre><code class=\"language-html\"><main>\n <form>\n <label for=\"milliseconds-input\">Milliseconds:<\/label>\n <input type=\"number\" id=\"milliseconds-input\" value=\"0\" \/>\n <\/form>\n\n <button class=\"vibrate-button\">Vibrate<\/button>\n <button class=\"stop-vibrate-button\">Stop<\/button>\n<\/main>\n<\/code><\/pre>\n<p>We\u2019ll add an event listener for a click and invoke the <code>vibrate()<\/code> method:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const vibrateButton = document.querySelector(\".vibrate-button\");\nconst millisecondsInput = document.querySelector(\"#milliseconds-input\");\n\nvibrateButton.addEventListener(\"click\", () => {\n navigator.vibrate(millisecondsInput.value);\n});\n<\/code><\/pre>\n<\/div>\n<p>To stop vibrating, we override the current vibration with a zero-millisecond vibration.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const stopVibrateButton = document.querySelector(\".stop-vibrate-button\");\n\nstopVibrateButton.addEventListener(\"click\", () => {\n navigator.vibrate(0);\n});\n<\/code><\/pre>\n<\/div>\n<div class=\"partners__lead-place\"><\/div>\n<h2 id=\"4-contact-picker-api\">4. Contact Picker API<\/h2>\n<p>In the past, it used to be that only native apps could connect to a device\u2019s \u201ccontacts\u201d. But now we have the fourth and final API I want to look at: the <a href=\"https:\/\/w3c.github.io\/contact-picker\/\"><strong>Contact Picker API<\/strong><\/a>.<\/p>\n<p>The API grants web apps access to the device\u2019s contact lists. Specifically, we get the <code>contacts.select()<\/code> async method available through the <code>navigator<\/code> object, which takes the following two arguments:<\/p>\n<ul>\n<li><strong><code>properties<\/code>:<\/strong> This is an array containing the information we want to fetch from a contact card, e.g., <code>"name"<\/code>, <code>"address"<\/code>, <code>"email"<\/code>, <code>"tel"<\/code>, and <code>"icon"<\/code>.<\/li>\n<li><strong><code>options<\/code>:<\/strong> This is an object that can only contain the <code>multiple<\/code> boolean property to define whether or not the user can select one or multiple contacts at a time.<\/li>\n<\/ul>\n<h3 id=\"browser-support-3\">Browser Support<\/h3>\n<p>I\u2019m afraid that browser support is next to zilch on this one, limited to Chrome Android, Samsung Internet, and Android\u2019s native web browser at the time I\u2019m writing this.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/caniuse.com\/mdn-api_contactsmanager\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"301\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Browser Support on Contacts Manager API\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/contacts-manager-api-support.jpg\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n Source: <a href=\"https:\/\/caniuse.com\/mdn-api_contactsmanager\">Caniuse<\/a>. (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/contacts-manager-api-support.jpg\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<h3 id=\"selecting-user-s-contacts\">Selecting User\u2019s Contacts<\/h3>\n<p>We will make another demo to select and display the user\u2019s contacts on the page. Again, starting with the HTML:<\/p>\n<pre><code class=\"language-html\"><main>\n <button class=\"get-contacts\">Get Contacts<\/button>\n <p>Contacts:<\/p>\n <ul class=\"contact-list\">\n <!-- We\u2019ll inject a list of contacts -->\n <\/ul>\n<\/main>\n<\/code><\/pre>\n<p>Then, in JavaScript, we first construct our elements from the DOM and choose which properties we want to pick from the contacts.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const getContactsButton = document.querySelector(\".get-contacts\");\nconst contactList = document.querySelector(\".contact-list\");\n\nconst props = [\"name\", \"tel\", \"icon\"];\nconst options = {multiple: true};\n<\/code><\/pre>\n<\/div>\n<p>Now, we asynchronously pick the contacts when the user clicks the <code>getContactsButton<\/code>.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">\nconst getContacts = async () => {\n try {\n const contacts = await navigator.contacts.select(props, options);\n } catch (error) {\n console.error(error);\n }\n};\n\ngetContactsButton.addEventListener(\"click\", getContacts);\n<\/code><\/pre>\n<\/div>\n<p>Using DOM manipulation, we can then append a list item to each contact and an icon to the <code>contactList<\/code> element.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const appendContacts = (contacts) => {\n contacts.forEach(({name, tel, icon}) => {\n const contactElement = document.createElement(\"li\");\n\n contactElement.innerText = `${name}: ${tel}`;\n contactList.appendChild(contactElement);\n });\n};\n\nconst getContacts = async () => {\n try {\n const contacts = await navigator.contacts.select(props, options);\n appendContacts(contacts);\n } catch (error) {\n console.error(error);\n }\n};\n\ngetContactsButton.addEventListener(\"click\", getContacts);\n<\/code><\/pre>\n<\/div>\n<p>Appending an image is a little tricky since we will need to convert it into a URL and append it for each item in the list.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-javascript\">const getIcon = (icon) => {\n if (icon.length > 0) {\n const imageUrl = URL.createObjectURL(icon[0]);\n const imageElement = document.createElement(\"img\");\n imageElement.src = imageUrl;\n\n return imageElement;\n }\n};\n\nconst appendContacts = (contacts) => {\n contacts.forEach(({name, tel, icon}) => {\n const contactElement = document.createElement(\"li\");\n\n contactElement.innerText = `${name}: ${tel}`;\n contactList.appendChild(contactElement);\n\n const imageElement = getIcon(icon);\n contactElement.appendChild(imageElement);\n });\n};\n\nconst getContacts = async () => {\n try {\n const contacts = await navigator.contacts.select(props, options);\n appendContacts(contacts);\n } catch (error) {\n console.error(error);\n }\n};\n\ngetContactsButton.addEventListener(\"click\", getContacts);\n<\/code><\/pre>\n<\/div>\n<p>And here\u2019s the outcome:<\/p>\n<figure class=\"\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/contact-picker-1.png\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"780\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Contact Picker showing three mock contacts\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/contact-picker-1.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/uniting-web-native-apps-unknown-javascript-apis\/contact-picker-1.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p><strong>Note<\/strong>: <strong><em>The Contact Picker API will only work if the context is secure<\/em><\/strong>, <em>i.e., the page is served over <code>https:\/\/<\/code> or <code>wss:\/\/<\/code> URLs.<\/em><\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>There we go, four web APIs that I believe would empower us to <strong>build more useful and robust PWAs<\/strong> but have slipped under the radar for many of us. This is, of course, due to inconsistent browser support, so I hope this article can bring awareness to new APIs so we have a better chance to see them in future browser updates.<\/p>\n<p>Aren\u2019t they interesting? We saw how much control we have with the orientation of a device and its screen as well as the level of access we get to access a device\u2019s hardware features, i.e. vibration, and information from other apps to use in our own UI.<\/p>\n<p>But as I said much earlier, there\u2019s a sort of infinite loop where <strong>a lack of awareness begets a lack of browser support<\/strong>. So, while the four APIs we covered are super interesting, your mileage will inevitably vary when it comes to using them in a production environment. Please tread cautiously and refer to <a href=\"https:\/\/caniuse.com\">Caniuse<\/a> for the latest support information, or check for your own devices using <a href=\"https:\/\/webapicheck.com\/\">WebAPI Check<\/a>.<\/p>\n<div class=\"signature\">\n <img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" class=\"lazyload\" data-src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\"><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>Uniting Web And Native Apps With 4 Unknown JavaScript APIs Uniting Web And Native Apps With 4 Unknown JavaScript APIs Juan Diego Rodr\u00edguez 2024-06-20T18:00:00+00:00 2024-10-15T23:05:45+00:00 A couple of years ago, four JavaScript APIs that landed at the bottom of awareness in the State of JavaScript survey. I took an interest in those APIs because they…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[],"class_list":["post-439","post","type-post","status-publish","format-standard","hentry","category-javascript"],"_links":{"self":[{"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts\/439","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/comments?post=439"}],"version-history":[{"count":1,"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts\/439\/revisions"}],"predecessor-version":[{"id":440,"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts\/439\/revisions\/440"}],"wp:attachment":[{"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/media?parent=439"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/categories?post=439"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/tags?post=439"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}