Web Share API - Progressively enhanced web component

How I implemented a share button at the bottom of my blog posts on this site using the Web Share API and web components. Scroll to the bottom and see if your browser supports the Web Share API... You'll see a share button.

Being able to hook into native functionality when its available is a great especially when you are able to do it in a progressively enhanced manner. A great example of this is the Web Share API. It is quite a new API that is still in working draft at the W3C. Even though it is still in working draft and not a common API available on all browsers. The main point is that it is available on some browsers. This allows you to start using it, and by allow the users of those devices and browsers to have an extra feature available to them. Aka - Progressive enhancement. While this allows you to use a new feature, its also a way for browsers and the W3C to get people testing the features, get excited and for it to become more than a working draft.

As with all technical web docs, a great starting point is MDN Web Docs, and the docs for the Web Share API is no different. The API is exposed to you via JavaScript as Navigator.share().

Broken down the Web Share API is one asynchronous method call, if its available in the users browsers. The function takes an object containing the data to share.

navigator.share({});

The data you pass to the function is

In its most basic example you can just pass the url and title to the share() method and it will work for supported browsers. (Try copy and pasting the following into console of dev tools of a supported browser).

navigator.share({
	url: "http://matthewroach.me/web-share-api-progressively-enhanced-web-component/",
	title: "Web Share API - Progressively enhanced web component",
});

While this is all great and all, I've mentioned progressively enhanced quite a few times already, and that this API is not available in all browsers. Being able to implement this as an enhanced feature is very neat, and if you pair it with web components you can have a fully isolated and truly enhanced feature for some of your users. Web components (customElements) pairs well here as they are available in the browsers that have support for the Web Share API.

The button

We are dealing with an action, so we use a button, and as the button should only be shown if the browser supports the Web Share API its default state is hidden. As the Web Share API is a JavaScript based API and we can only detect it in the users browser we will control the visibility of the button via JavaScript. This also means if you are browsing without JavaScript enabled you are none the wiser.

<button
	hidden
	is="s-hare"
	a-title="Web Share API - Progressively enhanced web component"
	a-url="http://matthewroach.me/web-share-api-progressively-enhanced-web-component/"
>
	Share
</button>

You maybe wondering what the extra attributes on the button are. the is and the a-title and a-url are.

The JavaScript

All the code that makes the button.. Pop.. Or well work in situations where it is able to. The progressive enhanced experience.

Below is the full JavaScript class and custom element define call to set up the button code from above to work.. Side note, view source to see it on my site.

As I used the is attribute on the button element our class extends the HTMLButtonElement

In the constructor I call a class method canWebShare - if this returns false. Browsers that don't support the Web Share API nothing more happens. But if your browsers has the Web Share API available navigator.share, the code will call showButton which actually shows the button and adds a click event listener to it.

Accessibility Tip: As I have extended the native HTML button you only need to set up the click event as the button element will fire this event when the user uses either the enter or space key on their keyboard. It will also catch the mouse click event too.

Now the event listener is added the button is all set for user interaction, and this is where the a-title and a-url attributes come into play. When a user activates the button the event listener function will use the values of these attributes to pass the data to the navigator.share method. Remember this method call returns a promise, you can use await here.. And guess what no need to polyfil for this as the browsers that support Web Share also support await

The share dialog is part of the users operating system (OS) and from when the user clicks the button you have passed the share off to the OS.

class Share extends HTMLButtonElement {
	constructor() {
		// Always call super first in constructor
		self = super();

		if (this.canWebShare()) {
			this.showButton();
		}
	}

	canWebShare() {
		return navigator.share !== undefined;
	}

	showButton() {
		self.removeAttribute("hidden");
		self.addEventListener("click", self.shareEvent);
	}

	async shareEvent() {
		try {
			await navigator.share(self.shareData());
		} catch (err) {
			alert("Sorry, sharing failed - you could try again");
		}
	}

	shareData() {
		const shareData = {
			title: self.getAttribute("a-title"),
			url: self.getAttribute("a-url") || window.location.href,
		};

		if (self.getAttribute("a-text")) {
			shareData.text = self.getAttribute("a-text");
		}

		return shareData;
	}
}

customElements.define("s-hare", Share, { extends: "button" });