Flexible Share Feature


Adding a share button to a website can be more difficult than it should be. For one reason or another, this is one of those features that still requires a lot of feature detection. I recently implemented a share button and wanted to share my implementation. You can see a working example below, along with the JS, HTML, and CSS.

The implementation is a bit on the verbose side and could definitely be improved in various ways. However, it uses only native APIs and should be easy to customize to fit your needs. It also uses newer APIs if they are available and falls back to older APIs when neccessary.

JavaScript

The JavaScript loops over a set of "share" objects so that more than one can be added to the page. In each case the logic is to use the navigator.share if available, or if not fall back to copying the url. If the navigator.clipboard is available, that is used, otherwise the execCommand method is used. Text is hidden and shown so that browsers with tigher security do not block the copy attempt. Classes are toggled in order to add some basic animations.

const shareButtons = document.querySelectorAll('.share');

shareButtons.forEach(shareButton => {
  shareButton.querySelector('.inner-share').addEventListener('click', e => {
    e.preventDefault();
    if (navigator && navigator.share) {
      navigator.share({
        url: shareButton.dataset.shareUrl,
        title: shareButton.dataset.shareTitle,
        description: shareButton.dataset.shareDescription
      });
    } else {
      const shareText = shareButton.querySelector('.share-text');
      const shareVisibleText = shareButton.querySelector('.share-visibile-text');

      if (shareText) shareText.classList.add('hidden');
      shareVisibleText.classList.remove('closed');
      shareVisibleText.innerText = 'Copied: ' + shareButton.dataset.shareUrl;

      if (navigator && navigator.clipboard) {
        navigator.clipboard.writeText(shareButton.dataset.shareUrl);
      } else {
        shareVisibleText.select();
        shareVisibleText.setSelectionRange(0, 99999);
        document.execCommand('copy');
      }

      setTimeout(() => {
        shareVisibleText.classList.add('closed')
        setTimeout(() => {
          if (shareText) shareText.classList.remove('hidden');
        }, 200);
      }, 2500);
    }
  });
});

HTML

The html is provided in a lit-html template but could easily be converted into another format. The structure is quite simple, using a .share outer container and a .inner-share inner container that is set to display flex with align items set.

<div class="share" data-share-title="${title}" date-share-description="${description}" data-share-url="${url}">
   <a class="inner-share">
      <div class="share-copied-message hidden">URL Copied!</div>
      <div class="share-visibile-text closed"></div>
      ${ showText ? html`
      <div class="share-text">${text}</div>
      `: ''}
      <svg class="share-img" height="512pt" viewBox="-21 0 512 512" width="512pt" xmlns="http://www.w3.org/2000/svg">...</svg>
   </a>
</div>

CSS

The CSS is semantic, targeting classes that are prefixed with "share-".

.share {
  width: 100%;
  height: 1rem;
  position: relative;
}

.inner-share {
  display: flex;
  position: absolute;
  right: 0;
  top: 0;
  align-items: center;
  cursor: pointer;
}

.share-text {
  font-size: 1rem;
  line-height: 1rem;
  margin-right: 0.25rem;
  transition: color 300ms, text-shadow 300ms;
}

.share-visibile-text {
  border-radius: 3px;
  overflow: hidden;
  height: 1rem;
  font-size: 1rem;
  line-height: 1rem;
  white-space: nowrap;
  box-sizing: border-box;
  transition: width 200ms;
  padding: 0;
  border: none;
  width: 300px;
}

.share-visibile-text.closed {
  width: 0px;
}

.share-copied-message {
  font-size: 1rem;
  line-height: 1rem;
}

.inner-share:hover .share-img {
  color: white;
  filter: drop-shadow( 0px 0px 1px rgba(0, 0, 0, 1)) drop-shadow( 0px 0px 2px rgba(0, 0, 0, 1));
}

.share-img {
  width: 1rem;
  height: 1rem;
  fill: currentColor;
  transition: color 300ms, text-shadow 300ms, filter 300ms;
}

Comments

Popular Posts