An open API service indexing awesome lists of open source software.

https://github.com/orestbida/iframemanager

🍪 GDPR friendly iframe manager written in vanilla js
https://github.com/orestbida/iframemanager

gdpr gdpr-consent iframe iframe-manager video vimeo youtube

Last synced: 8 months ago
JSON representation

🍪 GDPR friendly iframe manager written in vanilla js

Awesome Lists containing this project

README

          


IframeManager Logo

[Demo](https://orestbida.com/demo-projects/iframemanager/demo1/)   |   [Features](#features)   |   [Installation](#installation)   

[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
![Size](https://img.shields.io/github/size/orestbida/iframemanager/dist/iframemanager.js)
[![Stable version](https://img.shields.io/github/v/release/orestbida/iframemanager)](https://github.com/orestbida/iframemanager/releases)


**IframeMananger** is a lightweight javascript plugin which helps you **comply with `GDPR`** by completely removing iframes initially and setting a notice relative to that service. **Iframes are loaded only after consent**.


The plugin was mainly developed to aid [**CookieConsent**](https://github.com/orestbida/cookieconsent) with iframe management.


## Table of Contents

- [**Key features**](#features)
- [**Installation**](#installation)
- [**Configuration options & API**](#configuration-options)
- [**Configuration examples**](#configuration-examples)
- [youtube](#configuration-examples)
- [dailymotion](#configuration-examples)
- [vimeo](#configuration-examples)
- [twitch](#configuration-examples)
- [google maps](#configuration-examples)
- [**Usage with CookieConsent**](#usage-with-cookieconsent-v120)
- [**License**](#license)

## Features
- Lightweight
- Complies with **GDPR**
- **Multilanguage** support
- Automatic/custom thumbnail [support *](#note)
- Allows to integrate any service which uses iframes
- Improves website **performance**:
- lazy-load thumbnails
- lazy-load iframes
- Can be integrated with any consent solution

## Installation
1. #### Download the [latest release](https://github.com/orestbida/iframemanager/releases/latest) or use via CDN/NPM:

```bash
https://cdn.jsdelivr.net/gh/orestbida/iframemanager@1.3.0/dist/iframemanager.js
https://cdn.jsdelivr.net/gh/orestbida/iframemanager@1.3.0/dist/iframemanager.css
```
using [`npm`](https://www.npmjs.com/package/@orestbida/iframemanager):

```bash
npm i @orestbida/iframemanager
```

2. #### Import script + stylesheet:

```html


...



...



```

3. #### Configure and run:
- As external script

- Create a .js file (e.g. `app.js`) and import it in your html markup:

```html


...



```

- Configure iframemanager inside `app.js`:

```javascript
(function(){

const im = iframemanager();

// Example with youtube embed
im.run({
currLang: 'en',
services : {
youtube : {
embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',
thumbnailUrl: 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',
iframe : {
allow : 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;'
},
languages : {
en : {
notice: 'This content is hosted by a third party. By showing the external content you accept the terms and conditions of youtube.com.',
loadBtn: 'Load video',
loadAllBtn: "Don't ask again"
}
}
}
}
});
})();
```


- As inline script

```html


...



window.addEventListener('load', function(){

const im = iframemanager();

// Example with youtube embed
im.run({
currLang: 'en',
services : {
youtube : {
embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',
thumbnailUrl: 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',
iframe : {
allow : 'accelerometer; encrypted-media; gyroscope; 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://www.youtube.com/t/terms" target="_blank">terms and conditions</a> of youtube.com.',
loadBtn: 'Load video',
loadAllBtn: "Don't ask again"
}
}
}
}
});
});


```

4. #### Create a div with `data-service` and `data-id` attributes:

```html


```

## Configuration options
All available options for the `

` element:
```html


```

- `data-service` : [String, Required] name of the service (must also be defined in the config. object)
- `data-id` : [String, Required] unique id of the resource (example: video id)
- `data-title` : [String] notice title
- `data-params` : [String] iframe query parameters
- `data-thumbnail` : [String] path to custom thumbnail
- `data-ratio` : [String] custom aspect ratio ([Available values.](#available-data-ratio))[v1.1.0]
- `data-autoscale` : specify for **responsive iframe** (fill parent width + scale proportionally)
- `data-widget` : ignore the default aspect ratio; specify when implementing a custom widget with explicit width and height (twitter, facebook, instagram ...)[v1.2.0]

### How to set attributes on the `iframe` element
You can set any attribute by using the following syntax:
- `data-iframe-` [String] note: replace `` with a valid attribute name. [v1.1.0]

Example:
```html



```


All available options for the config. object:
```javascript
{
currLang: 'en', // current language of the notice (must also be defined in the "languages" object below)
autoLang: false, // if enabled => use current client's browser language
// instead of currLang [OPTIONAL]

// callback fired when state changes (a new service is accepted/rejected)
onChange: ({changedServices, eventSource}) => {
// changedServices: string[]
// eventSource.type: 'api' | 'click'
// eventSource.service: string
// eventSource.action: 'accept' | 'reject'
},

services : {
myservice : {

embedUrl: 'https://',

// set valid url for automatic thumbnails [OPTIONAL]
thumbnailUrl: 'https://',

// global iframe settings (apply to all iframes relative to current service) [OPTIONAL]
iframe: {
allow: 'fullscreen', // iframe's allow attribute
params: 'mute=1&start=21', // iframe's url query parameters

// function run for each iframe configured with current service
onload: (dataId, setThumbnail) => {
console.log(`loaded iframe with data-id=${dataId}`);
}
},

// cookie is set if the current service is accepted
cookie: {
name: 'cc_youtube', // cookie name
path: '/', // cookie path [OPTIONAL]
samesite: 'lax', // cookie samesite [OPTIONAL]
domain: location.hostname // cookie domain [OPTIONAL]
},

languages: {
en: {
notice: 'Html notice message',
loadBtn: 'Load video', // Load only current iframe
loadAllBtn: "Don't ask again" // Load all iframes configured with this service + set cookie
}
}
},

anotherservice: {
// ...
}
}
}
```

Any other property specified inside the `iframe` object, will be set directly to the `iframe` element as attribute.

Example: add `frameborder` and `style` attributes:
```javascript
{
// ...

services: {
myservice: {
// ...

iframe: {
// ...

frameborder: '0',
style: 'border: 4px solid red;'
}
}
}
}
```

Note: `thumbnailUrl` can be static string, dynamic string or a function:

- `static string` : "https://path_to_image/image.png"
- `dynamic string` : "https://myservice_embed_url/{data-id}"
- `function` :
```javascript
thumbnailUrl: (dataId, setThumbnail) => {
// fetch thumbnail url here based on dataId of the current element ...
let url = 'fetched_url';

// pass obtained url to the setThumbnail function
setThumbnail(url);
}
```

## Custom Widgets
Some services (e.g. twitter) have their own markup and API to generate the iframe.

Note: this is an example with twitter's widget. Each widget/service will have a slightly different implementation.

1. Place the markup inside a special `data-placeholder` div. Remove any `script` tag that comes with the markup. Example:

```html




```

2. Create a new service and dynamically load and initialize the widget inside the `onAccept` callback:

```javascript
im.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();
}
}
}
})
```

It is highly recommended to set a fixed `width` and `height` to the main `data-service` div, to avoid the (awful) content jump effect when the iframe is loaded.

## Placeholder for non-js browsers
You can set a placeholder visible only if javascript is disabled via a special div:
```html


```

Example:
```html


I'm visible only if js is disabled



```

## APIs
The plugin exposes the following methods:
- `.run()`
- `.acceptService()`
- `.rejectService()`
- `.getState()` [v1.2.0+]
- `.getConfig()` [v1.2.0+]
- `.reset()` [v1.3.0+]

Example usage:

```javascript
// accept specific service only
im.acceptService('youtube');

// accept all services (for example if user has given full consent to cookies)
im.acceptService('all');

// reject specific service
im.rejectService('youtube');

// reject all services (for example when user opts out of cookies)
im.rejectService('all');

// get entire config object
const config = im.getConfig();

// get current state (enabled/disabled services)
const state = im.getState();

// state.services: Map
// state.acceptedServices: string[]

// soft reset, removes internal event listeners
im.reset();

// hard reset, same as above, but also resets each div to its original state (for react frameworks)
im.reset(true);
```

Both `acceptService` and `rejectService` work the same way:
1. set/erase cookie
2. create/remove iframes

## Configuration examples
- Youtube

```javascript
im.run({
currLang: 'en',
services: {
youtube: {
embedUrl: 'https://www.youtube-nocookie.com/embed/{data-id}',

thumbnailUrl: 'https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg',

iframe: {
allow: 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',
},

languages: {
en: {
notice: 'This content is hosted by a third party. By showing the external content you accept the terms and conditions of youtube.com.',
loadBtn: 'Load video',
loadAllBtn: "Don't ask again"
}
}
}
}
});
```
Example:
```html



```


- Dailymotion

```javascript
im.run({
currLang: 'en',
services: {
dailymotion: {
embedUrl: 'https://www.dailymotion.com/embed/video/{data-id}',

thumbnailUrl: async (dataId, setThumbnail) => {
// Use dailymotion's API to fetch the thumbnail
const url = `https://api.dailymotion.com/video/${dataId}?fields=thumbnail_large_url`;
const response = await (await fetch(url)).json();
const thumbnailUlr = response?.thumbnail_large_url;
thumbnailUlr && setThumbnail(thumbnailUlr);
},

iframe: {
allow: 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',
},

languages: {
en: {
notice: 'This content is hosted by a third party. By showing the external content you accept the terms and conditions of dailymotion.com.',
loadBtn: 'Load video',
loadAllBtn: "Don't ask again"
}
}
}
}
});
```



- Vimeo

```javascript
im.run({
currLang: 'en',
services: {
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 terms and conditions of vimeo.com.',
loadBtn: 'Load video',
loadAllBtn: "Don't ask again"
}
}
}
}
});
```



- Twitch

```javascript
im.run({
currLang: 'en',
services: {
twitch: {
embedUrl: `https://player.twitch.tv/?{data-id}&parent=${location.hostname}`,

iframe: {
allow: 'accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;',
},

languages: {
en: {
notice: 'This content is hosted by a third party. By showing the external content you accept the terms and conditions of twitch.com.',
loadBtn: 'Load stream',
loadAllBtn: "Don't ask again"
}
}
}
}
});
```



- Google Maps

- With API key

```javascript
im.run({
currLang: 'en',
services: {
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 terms and conditions of Google Maps.',
loadBtn: 'Load map',
loadAllBtn: "Don't ask again"
}
}
}
}
});
```

Example:
```html


```


- Without API key

```javascript
im.run({
currLang: 'en',
services : {
googlemaps : {
embedUrl: 'https://www.google.com/maps/embed?pb={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 terms and conditions of Google Maps.',
loadBtn: 'Load map',
loadAllBtn: "Don't ask again"
}
}
}
}
});
```

Example usage:
```html



```



## Usage with CookieConsent [v1.2.0+]
You can use the `onChange` callback to detect when an iframe is loaded by the `loadAllBtn` button click event and notify CookieConsent to also update its state.

Example:
```javascript
im.run({
currLang: 'en',

onChange: ({changedServices, eventSource}) => {

if(eventSource.type === 'click') {
// Retrieve all accepted services:
// const allAcceptedServices = im.getState().acceptedServices;

/**
* Retrieve array of already accepted services
* and add the new service
*/
const servicesToAccept = [
...CookieConsent.getUserPreferences().acceptedServices['analytics'], //cookieconsent v3
...changedServices
];

CookieConsent.acceptService(servicesToAccept, 'analytics');
}
},

services: {
// ...
}
});
```

Note: the above example assumes that all services belong to the `analytics` category.

### Available `data-ratio`

Horizontal aspect ratio:

* `1:1`, `2:1`, `3:2`, `5:2`, `4:3`, `16:9`, `16:10`, `20:9`, `21:9`

Vertical aspect ratio:
* `9:16`, `9:20`

## License
Distributed under the MIT License. See [LICENSE](https://github.com/orestbida/iframemanager/blob/master/LICENSE) for more information.


#### Note
Not all services (example: twitch) allow automatic/easy thumbnail fetch.