Yes, Your Browser Can Do That (Probably)

A presentation at DDD Brisbane in December 2022 in Brisbane QLD, Australia by Julian Burr

Slide 1

Slide 1

Slide 2

Slide 2

@jburr90 / Julian Burr DDD BRISBANE Yes, Your Browser Can do That! (Probably)

Slide 3

Slide 3

Why do Browser APIs matter? A very brief history of frontend development and the constant desire to build native

Slide 4

Slide 4

stopme.io The ultimate SaaS (Stopwatch as a Service) product for everyone stop me.io

Slide 5

Slide 5

01 Observe

Slide 6

Slide 6

01 Intersection Observer Keep track of when DOM elements enter or leave the users viewport

Slide 7

Slide 7

01 Intersection Observer Keep track of when DOM elements enter or leave the users viewport

Slide 8

Slide 8

01 Intersection Observer Keep track of when DOM elements enter or leave the users viewport const callback = (entries, observer) => { entries.forEach(entry => { // … }) } const observer = new IntersectionObserver( callback, options ) observer.observe(element) observer.unobserve(element)

Slide 9

Slide 9

01 Intersection Observer Keep track of when DOM elements enter or leave the users viewport const callback = (entries, observer) => { entries.forEach(entry => { // … }) } const observer = new IntersectionObserver( callback, options ) observer.observe(element) observer.unobserve(element)

Slide 10

Slide 10

01 Intersection Observer Keep track of when DOM elements enter or leave the users viewport const callback = (entries, observer) => { entries.forEach(entry => { // … }) } const observer = new IntersectionObserver( callback, options ) observer.observe(element) observer.unobserve(element)

Slide 11

Slide 11

01 Intersection Observer Keep track of when DOM elements enter or leave the users viewport const callback = (entries, observer) => { entries.forEach(entry => { // … }) } const observer = new IntersectionObserver( callback, options ) observer.observe(element) observer.unobserve(element)

Slide 12

Slide 12

01 Intersection Observer Keep track of when DOM elements enter or leave the users viewport

Slide 13

Slide 13

01 Resize Observer Observe DOM elements and run callbacks whenever their content box changes

Slide 14

Slide 14

01 Resize Observer Observe DOM elements and run callbacks whenever their content box changes const callback = (entries, observer) => { entries.forEach(entry => { // … }) } const observer = new ResizeObserver( callback, options ) observer.observe(element) observer.unobserve(element)

Slide 15

Slide 15

01 Resize Observer Observe DOM elements and run callbacks whenever their content box changes const callback = (entries, observer) => { entries.forEach(entry => { // … }) } const observer = new ResizeObserver( callback, options ) observer.observe(element) observer.unobserve(element)

Slide 16

Slide 16

01 Resize Observer Observe DOM elements and run callbacks whenever their content box changes

Slide 17

Slide 17

02 Even observe the user’s device

Slide 18

Slide 18

02 Page Visibility API Detect when the tab of the current page is active or in the background

Slide 19

Slide 19

02 Page Visibility API Detect when the tab of the current page is active or in the background

Slide 20

Slide 20

02 Page Visibility API Detect when the tab of the current page is active or in the background function handleChange () { if (document.visibilityState === “hidden”) { // do or stop something when tab is hidden } else { // do or stop something when it’s visible } } document.addEventListener( “visibilitychange”, handleChange )

Slide 21

Slide 21

02 Page Visibility API Detect when the tab of the current page is active or in the background

Slide 22

Slide 22

02 Network Information API Get information about the users network connection

Slide 23

Slide 23

02 Network Information API Get information about the users network connection

Slide 24

Slide 24

02 Network Information API Get information about the users network connection function handleChange () { // navigator.connection.effectiveType // *.type // *.downlink / *.downlinkMax // *.rtt // *.saveData if (!navigator.onLine) { // user is offline } } navigator.connection.addEventListener( “change”, handleChange );

Slide 25

Slide 25

02 Network Information API Get information about the users network connection function handleChange () { // navigator.connection.effectiveType // *.type // *.downlink / *.downlinkMax // *.rtt // *.saveData if (!navigator.onLine) { // user is offline } } navigator.connection.addEventListener( “change”, handleChange );

Slide 26

Slide 26

02 Network Information API Get information about the users network connection function handleChange () { // navigator.connection.effectiveType // *.type // *.downlink / *.downlinkMax // *.rtt // *.saveData if (!navigator.onLine) { // user is offline } } navigator.connection.addEventListener( “change”, handleChange );

Slide 27

Slide 27

02 Network Information API Get information about the users network connection function handleChange () { // navigator.connection.effectiveType // *.type // *.downlink / *.downlinkMax // *.rtt // *.saveData if (!navigator.onLine) { // user is offline } } navigator.connection.addEventListener( “change”, handleChange );

Slide 28

Slide 28

02 Network Information API Get information about the users network connection

Slide 29

Slide 29

02 Battery Status API Get details about the devices battery status

Slide 30

Slide 30

02 Battery Status API Get details about the devices battery status

Slide 31

Slide 31

02 Battery Status API Get details about the devices battery status const battery = await navigator.getBattery() function handleChange () { // battery.charging // *.level // *.chargingTime // *.dischargingTime } battery.addEventListener( “chargingchange” handleChange ) // “levelchange” // “chargingtimechange” // “dischargingtimechange”

Slide 32

Slide 32

02 Battery Status API Get details about the devices battery status const battery = await navigator.getBattery() function handleChange () { // battery.charging // *.level // *.chargingTime // *.dischargingTime } battery.addEventListener( “chargingchange” handleChange ) // “levelchange” // “chargingtimechange” // “dischargingtimechange”

Slide 33

Slide 33

02 Battery Status API Get details about the devices battery status const battery = await navigator.getBattery() function handleChange () { // battery.charging // *.level // *.chargingTime // *.dischargingTime } battery.addEventListener( “chargingchange” handleChange ) // “levelchange” // “chargingtimechange” // “dischargingtimechange”

Slide 34

Slide 34

02 Battery Status API Get details about the devices battery status const battery = await navigator.getBattery() function handleChange () { // battery.charging // *.level // *.chargingTime // *.dischargingTime } battery.addEventListener( “chargingchange” handleChange ) // “levelchange” // “chargingtimechange” // “dischargingtimechange”

Slide 35

Slide 35

02 Battery Status API Get details about the devices battery status

Slide 36

Slide 36

03 Enhance your Components

Slide 37

Slide 37

03 I18n API Internationalisation helpers and utilities

Slide 38

Slide 38

03 const options = { dateStyle: “full”, timeStyle: “long”, timeZone: “Australia/Sydney” }) new Intl.DateTimeFormat(“en-US”, options) .format(date) // Friday, December 2, 2022 at 12:21:40 PM // GMT+11 Intl API Internationalisation helpers and utilities

Slide 39

Slide 39

03 const options = { dateStyle: “full”, timeStyle: “long”, timeZone: “Australia/Sydney” }) new Intl.DateTimeFormat(“en-US”, options) .format(date) // Friday, December 2, 2022 at 12:21:40 PM // GMT+11 Intl Internationalisation helpers and utilities

Slide 40

Slide 40

03 const fmt = new Intl.RelativeTimeFormat( “en”, { style: “narrow” } ) fmt.format(3, “day”) // in 3 days fmt.format(-2, “year”) // 2 years ago intl API Internationalisation helpers and utilities

Slide 41

Slide 41

03 const formatter = new Intl.RelativeTimeFormat( “en”, { style: “narrow” } ) formatter.format(3, “day”) // in 3 days formatter.format(-2, “year”) // 2 years ago Intl API Internationalisation helpers and utilities

Slide 42

Slide 42

03 intl API Internationalisation helpers and utilities const au = new Intl.NumberFormat(“en-AU”) au.format(123_456.79); // 123,456.79 const de = new Intl.NumberFormat(“de-DE”) de.format(123_456.79); // 123.456,79

Slide 43

Slide 43

03 const fmt = new Intl.NumberFormat( “de-DE”, { style: “currency”, currency: “EUR” } ) fmt.format(123_456.79) // 123.456,79 € intl API Internationalisation helpers and utilities

Slide 44

Slide 44

03 const fmt = new Intl.NumberFormat(“en-AU”, { style: “unit”, unit: “liter”, unitDisplay: “long” }) fmt.format(123) // 123 litres Intl API Internationalisation helpers and utilities

Slide 45

Slide 45

03 const fmt = new Intl.NumberFormat(“en-AU”, { style: “unit”, unit: “liter”, unitDisplay: “long” }) fmt.format(123) // 123 litres intl API Internationalisation helpers and utilities

Slide 46

Slide 46

03 Screen Wake Lock API Let the users device know that you don’t want the screen to lock due to inactivity

Slide 47

Slide 47

03 Screen Wake Lock API Let the users device know that you don’t want the screen to lock due to inactivity try { const wakeLock = await navigator .wakeLock .request(“screen”) } catch (e) { // Request failed, e.g. for system // related reasons like low battery } wakeLock.release()

Slide 48

Slide 48

03 Screen Wake Lock API Let the users device know that you don’t want the screen to lock due to inactivity try { const wakeLock = await navigator .wakeLock .request(“screen”) } catch (e) { // Request failed, e.g. for system // related reasons like low battery } wakeLock.release()

Slide 49

Slide 49

03 Screen Wake Lock API Let the users device know that you don’t want the screen to lock due to inactivity try { const wakeLock = await navigator .wakeLock .request(“screen”) } catch (e) { // Request failed, e.g. for system // related reasons like low battery } wakeLock.release()

Slide 50

Slide 50

03 Screen Wake Lock API Let the users device know that you don’t want the screen to lock due to inactivity

Slide 51

Slide 51

03 EyeDropper API Allow your users to pick a colour from anywhere on their screen

Slide 52

Slide 52

03 EyeDropper API Allow your users to pick a colour from anywhere on their screen

Slide 53

Slide 53

03 EyeDropper API Allow your users to pick a colour from anywhere on their screen const eyeDropper = new EyeDropper() try { const result = await eyeDropper.open() // *.sRGBHex } catch (e) { // failure, also triggered when user // cancels }

Slide 54

Slide 54

03 EyeDropper API Allow your users to pick a colour from anywhere on their screen

Slide 55

Slide 55

04 Almost as good as native

Slide 56

Slide 56

04 Vibration API Control the devices vibration for haptic feedback

Slide 57

Slide 57

04 Vibration API Control the devices vibration for haptic feedback

Slide 58

Slide 58

04 Vibration API Control the devices vibration for haptic feedback navigator.vibrate(200) // Pattern navigator.vibrate([200, 100, 200])

Slide 59

Slide 59

04 Vibration API Control the devices vibration for haptic feedback // https://github.com/hjdesigner/vibration-api // Super Mario navigator.vibrate([ 87, 89, 104, 176, 96, 176, 88, 88, 79, 241, 176, 377, 191 ]) // Game of Thrones navigator.vibrate([ 950, 50, 530, 80, 100, 100, 100, 60, 930, 50, 530, 80, 100, 100, 100, 60, 980 ])

Slide 60

Slide 60

04 Vibration API Control the devices vibration for haptic feedback

Slide 61

Slide 61

04 Contact Picker API Let users select from their device contacts list with the native picker

Slide 62

Slide 62

04 Contact Picker API Let users select from their device contacts list with the native picker

Slide 63

Slide 63

04 Contact Picker API Let users select from their device contacts list with the native picker const supportedProperties = await navigator.contacts.getProperties() // [“name”, “email”, “tel”, “address”, “icon”] try { const props = [“name”, “email”] const options = { multiple: true } const contacts = await navigator .contacts .select(props, opts) } catch (e) { // Failure or cancellation }

Slide 64

Slide 64

04 Contact Picker API Let users select from their device contacts list with the native picker const supportedProperties = await navigator.contacts.getProperties() // [“name”, “email”, “tel”, “address”, “icon”] try { const props = [“name”, “email”] const options = { multiple: true } const contacts = await navigator .contacts .select(props, opts) } catch (e) { // Failure or cancellation }

Slide 65

Slide 65

04 Contact Picker API Let users select from their device contacts list with the native picker const supportedProperties = await navigator.contacts.getProperties() // [“name”, “email”, “tel”, “address”, “icon”] try { const props = [“name”, “email”] const options = { multiple: true } const contacts = await navigator .contacts .select(props, opts) } catch (e) { // Failure or cancellation }

Slide 66

Slide 66

Let users select from their device contacts list with the native picker Mobile Contact Picker API Desktop 04

Slide 67

Slide 67

04 Web Share API Allow users to share content through any of their installed apps using the native picker

Slide 68

Slide 68

04 Web Share API Allow users to share content through any of their installed apps using the native picker

Slide 69

Slide 69

04 Web Share API Allow users to share content through any of their installed apps using the native picker const shareData = { title: “stopme.io”, text: “The best SaaS product in the world”, url: “https://stopme.io” } // Must be triggered by user interaction btn.addEventListener(“click”, async () => { try { await navigator.share(shareData); } catch (e) { // failed or cancelled } });

Slide 70

Slide 70

04 Web Share API Allow users to share content through any of their installed apps using the native picker const shareData = { title: “stopme.io”, text: “The best SaaS product in the world”, url: “https://stopme.io” } // Must be triggered by user interaction btn.addEventListener(“click”, async () => { try { await navigator.share(shareData); } catch (e) { // failed or cancelled } });

Slide 71

Slide 71

04 Web Share API Allow users to share content through any of their installed apps using the native picker const shareData = { title: “stopme.io”, text: “The best SaaS product in the world”, url: “https://stopme.io” } // Must be triggered by user interaction btn.addEventListener(“click”, async () => { try { await navigator.share(shareData); } catch (e) { // failed or cancelled } });

Slide 72

Slide 72

04 Web Share API Allow users to share content through any of their installed apps using the native picker

Slide 73

Slide 73

End Honorable Mentions

Slide 74

Slide 74

End // Bluetooth API const devices = await navigator.bluetooth.getDevices() const device = await navigator.bluetooth .requestDevice({ filters: [{ services: [A, B] }] }) // USB API const devices = await navigator.usb.getDevices() Honorable Mentions const device = await navigator.usb. .requestDevice({ filters: [{ vendorId: id }] })

Slide 75

Slide 75

End // Geolocation API // Old but gold navigator.geolocation .getCurrentPosition(pos => { // pos.coords.latitude // *.coords.longitude }) const watchId = navigator.geolocation .watchPosition(pos => { // … }) navigator.geolocation.clearWatch(watchId) Honorable Mentions

Slide 76

Slide 76

End // File System Access API const options = { types: [ A, B ] } const [handle] = await window.showOpenFilePicker(options) const fileData = await handle.getFile() // Handling directories const dirHandle = await window.showDirectoryPicker() Honorable Mentions // Saving files const options = { types: [{ description: “Text file”, accept: { “text/plain”: [“.txt”] }, }] } await window.showSaveFilePicker(options)

Slide 77

Slide 77

End Honorable Mentions // Clipboard API await navigator.clipboard.writeText(txt) const text = await navigator.clipboard.readText()

Slide 78

Slide 78

End // Presentation API const urls = [ presentationUrl, altUrl ] const request = new PresentationRequest(urls) const availability = await request.getAvailability() availability.onchange = () => { // availability.value }) const connection = await request.start() connection.close() Honorable Mentions

Slide 79

Slide 79

End // Web Speech API // Recognition const recognition = new SpeechRecognition() recognition.start() recognition.onresult = (event) => { const word = event.results[0][0].transcript // event.results[0][0].confidence }) Honorable Mentions // Synthesis const synth = window.speechSynthesis const words = new SpeechSynthesisUtterance(txt) synth.speak(words)

Slide 80

Slide 80

End Honorable Mentions // And soo many more… // Check out: // https://developer.mozilla.org/enUS/docs/Web/API

Slide 81

Slide 81

Thanks! @jburr90 / julian burr https://www.julianburr.de/ ddd-brisbane-2022-slides.pdf