Performant & GDPR-friendly Iframe Embed JavaScript Library – iframemanager

Category: Javascript , Loading , Recommended | September 8, 2024
Author:orestbida
Views Total:818 views
Official Page:Go to website
Last Update:September 8, 2024
License:MIT

Preview:

Performant & GDPR-friendly Iframe Embed JavaScript Library – iframemanager

Description:

A lightweight, GDPR-compliant iframe manager that delays the loading of iframe content (like Youtube videos) until you agree to certain Privacy Policy and Terms.

More Features:

  • Auto resize the iframe to fit its container.
  • Custom thumbnail before loading the iframe content.
  • Supports any service which uses iframes: Youtube, Vimeo, Twitch, etc.
  • Easy to integrate with any cookie consent solution.
  • Lazily load iframes & thumbnails to improve page speed.

How to use it:

1. Load the iframemanager’s JavaScript and Stylesheet in the document.

<link rel="stylesheet" href="dist/iframemanager.css" />
<script src="dist/iframemanager.js"></script>

2. Initialize the iframe manager.

var manager = iframemanager();

3. Add services to the iframe manager.

iframemanager.run({
  services : {
    // Youtube
    youtube : {
      embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',
      iframe : {
        allow : 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',
      },
      cookie : {            
        name : 'cc_youtube'
      },
      languages : {
        'en' : {
          notice: 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer" href="https://www.youtube.com/t/terms" title="Terms and conditions" target="_blank">terms and conditions</a> of youtube.com.',
          loadBtn: 'Load video',
          loadAllBtn: 'Don\'t ask again'
        }
      }
    },
    // Vimeo
    vimeo: {
      embedUrl: 'https://player.vimeo.com/video/{data-id}',
      iframe: {
        allow : 'fullscreen; picture-in-picture, allowfullscreen;',
      },
      thumbnailUrl: async (dataId, setThumbnail) => {
        const url = `https://vimeo.com/api/v2/video/${dataId}.json`;
        const response = await (await fetch(url)).json();
        const thumbnailUrl = response[0]?.thumbnail_large;
        thumbnailUrl && setThumbnail(thumbnailUrl);
      },
      languages: {
        en: {
          notice: 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer noopener" href="https://vimeo.com/terms" target="_blank">terms and conditions</a> of vimeo.com.',
          loadBtn: 'Load video',
          loadAllBtn: "Don't ask again"
        }
      }
    },
    // Daily Motion
    dailymotion : {
      embedUrl: 'https://www.dailymotion.com/embed/video/{data-id}',
      // Use dailymotion api to obtain thumbnail
      thumbnailUrl: function(id, callback){
        var url = "https://api.dailymotion.com/video/" + id + "?fields=thumbnail_large_url";
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
          if (this.readyState == 4 && this.status == 200) {
            var src = JSON.parse(this.response).thumbnail_large_url;
            callback(src);
          }
        };
        xhttp.open("GET", url, true);
        xhttp.send();
      },
      iframe : {
        allow : 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;'
      },
      cookie : {            
        name : 'cc_dailymotion'
      },
      languages : {
        'en' : {
          notice: 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer" href="#link_dailymotion" title="Terms and conditions" target="_blank">terms and conditions</a> of dailymotion.com.',
          loadBtn: 'Load video',
          loadAllBtn: 'Don\'t ask again'
          
        }
      }
    },
    // Google maps
    googlemaps: {
      embedUrl: 'https://www.google.com/maps/embed/v1/place?key=API_KEY&q={data-id}',
      iframe: {
        allow: 'picture-in-picture; fullscreen;'
      },
      languages: {
        en: {
          notice: 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer noopener" href="https://cloud.google.com/maps-platform/terms" target="_blank">terms and conditions</a> of Google Maps.',
          loadBtn: 'Load map',
          loadAllBtn: "Don't ask again"
        }
      }
    },
    // Twitch
    "twitch" : {
      embedUrl: 'https://player.twitch.tv/?{data-id}&parent=localhost',
      iframe : {
        allow : 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',
        params: '',// optional
      },
      cookie : {            
        name : 'cc_twitch'
      },
      languages : {
        'en' : {
          notice: 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer" href="#link_twitch" title="Terms and conditions" target="_blank">terms and conditions</a> of twitch.com.',
          loadBtn: 'Load stream',
          loadAllBtn: 'Don\'t ask again'
        }
      }
    }
  }
});

4. Create a DIV container to hold the iframe manager and config the iframe content with the following data attributes:

  • data-service: Service name
  • data-id: Video ID
  • data-params: Additional parameters
  • data-thumbnail: Path to the thumbnail image
  • data-thumbnailpreload: Determine whether to preload the thumbnail image
  • data-autoscale: Determine whether to auto resize the iframe content
  • data-ratio: Custom ratio: 1:1, 2:1, 3:2, 5:2, 4:3, 16:9, 16:10, 20:9, 21:9, 9:16, 9:20
  • data-widget: Ignore the default aspect ratio; specify when implementing a custom widget with explicit width and height
  • data-iframe-id: Iframe ID attribute
  • data-iframe-loading: Iframe loading attribute
  • data-iframe-frameborder: Iframe border attribute
<div data-service="youtube"
     data-id="VIDEO ID"
     data-params="start=21&mute=1"
     data-thumbnail="/path/to/poster.jpg"
     data-thumbnailpreload
     data-autoscale >
</div>

5. Available configuration options.

iframemanager.run({
  currLang: 'en', 
  autoLang: false, 
  services : {
    myservice : {
      embedUrl: 'https://myservice_embed_url>/{data-id}',
      // set valid url for automatic thumbnails   [OPTIONAL]
      thumbnailUrl: 'https://<myservice_embed_thumbnail_url>/{data-id}',  
      // global iframe settings (apply to all iframes relative to current service) [OPTIONAL]
        iframe : {
          allow : 'fullscreen', 
          params : 'mute=1&start=21'
          // function run for each iframe configured with current service
          onload : function(data_id, callback){
            console.log("loaded iframe with data-id=" + data_id);
          }
        },
        // cookie is set if the current service is accepted
        cookie : {
          name : 'cc_youtube',
          path : '/',
          samesite : 'lax', 
          domain : location.hostname
        },
        languages : {
          en : {
            notice: 'Html <b>notice</b> message',
            loadBtn: 'Load video',
            loadAllBtn: 'Don\'t ask again'
          }
        }
    },
      anotherservice : {
          ...
      }
    }
});

6. API methods.

// accept specific service
manager.acceptService('youtube');
// accept all services
manager.acceptService('all');
// reject specific service
manager.rejectService('youtube');
// reject all services
manager.rejectService('all');
// get the entire config object
manager.getConfig();
// get the current state (enabled/disabled services)
manager.getState();

7. Add a custom widget.

<div
  data-service="twitter"
  data-widget
  style="width: 300px; height: 501px"
>
  <div data-placeholder>
    <blockquote class="twitter-tweet"><p lang="en" dir="ltr">Sunsets don&#39;t get much better than this one over <a href="https://twitter.com/GrandTetonNPS?ref_src=twsrc%5Etfw">@GrandTetonNPS</a>. <a href="https://twitter.com/hashtag/nature?src=hash&amp;ref_src=twsrc%5Etfw">#nature</a> <a href="https://twitter.com/hashtag/sunset?src=hash&amp;ref_src=twsrc%5Etfw">#sunset</a> <a href="http://t.co/YuKy2rcjyU">pic.twitter.com/YuKy2rcjyU</a></p>&mdash; US Department of the Interior (@Interior) <a href="https://twitter.com/Interior/status/463440424141459456?ref_src=twsrc%5Etfw">May 5, 2014</a></blockquote>
  </div>
</div>
manager.run({
  services: {
    twitter: {
      onAccept: async (div, setIframe) => {
        // Using cookieconsent v3
        await CookieConsent.loadScript('https://platform.twitter.com/widgets.js');
        // Make sure the "window.twttr" property exists
        await im.childExists({childProperty: 'twttr'}) && await twttr.widgets.load(div);
        // Make sure the "iframe" element exists
        await im.childExists({parent: div}) && setIframe(div.querySelector('iframe'));
      },
      onReject: (iframe) => {
          iframe && iframe.parentElement.remove();
      }
    }
  }
})

Changelog:

v1.3.0 (09/08/2024)

  • Add .reset() function to fix re-render issues (react/svelte …)
  • Allow usage of the {data-id} placeholder inside the notice text
  • Allow usage of optional callback in embedUrl
  • Bug Fixes

v1.2.5 (05/21/2023)

  • Fix issue with visibility in v1.2.4 where buttons/links remain clickable despite notice being hidden

v1.2.4 (05/20/2023)

  • Fix compatibility issues with Safari 10-13
  • Use em/rem units instead of px

v1.2.3 (04/03/2023)

  • Fix iframes outside viewport are not reset via rejectService(‘<specific_service>’)

v1.2.2 (03/31/2023)

  • Fix uncaught exception when service div is no longer present

v1.2.1 (02/08/2023)

  • Bugfixes

v1.2.0 (01/19/2023)

  • Add .getState() to retrieve the current state such as accepted/rejected services
  • Add .getConfig() to retrieve any config. value/prop.
  • Add onChange callback, executed whenever a service’s state is changed
  • Add support for custom widgets generated by external API (twitter, facebook, instagram …)
  • Add css variables for easier customization
  • Improve support for custom placeholders when javascript is not enabled
  • Make buttons optional (you can now have 0, 1 or 2 buttons)
  • Use service name as default cookie name if no explicit cookie.name is provided: ‘im_<service>’
  • Bug Fixes

v1.1.0 (11/15/2022)

  • Add support for custom aspect ratios via data-ratio=”x:y” 8680851
  • Allow any attribute on the iframe element via data-iframe-<attribute>
  • Allow any attribute on the iframe element via the internal iframe object
  • Bugfixes

You Might Be Interested In:


Leave a Reply