How Spammers Avoid the Gmail Spam Filter through Google Forms

Gmail is very effective at filtering spam emails but spammers seem to have figured out a new way to bypass the spam filters and send emails that land right in the user’s inbox. The emails are sent through Google Forms and because the messages originate from Google’s own email servers, they do not get caught in the spam filters.

Google Forms - Spam Emails

Here’s how spam emails are sent through Google Forms.

  1. A public form is created with Google Forms.
  2. The form creator uploads images for the various question fields and also adds links to spam websites in the form.
  3. Inside the Form settings, they turn on the option to “Collect Email Addresses” including the option to send “Response receipts” when a new form is submitted.

Google Form Email

Now the spammers can simply open the Google Form, fill in the recipient’s email address and hit the submit button.

Google Forms will automatically email a copy of the form response, including all the pictures and links contained in the original form, to the email address that was entered in the form.

Here’s a screenshot of one such email from Google Forms that easily tricked the spam filters.

Google Forms

How to Block Spam from Google Forms

If you would like to prevent spam emails from Google Forms from landing in your inbox, Gmail filters can help.

All pre-filled Google Forms emails have the sender’s email address as below:

forms-receipts-noreply@google.com

You can create a filter in Gmail that will automatically delete emails that have Google Forms as the sender.

Google Forms Filter

Alternatively, you may open the form link from the email and click the “Report Abuse” button to report the form to Google. That is not likely to be a very effective strategy though as spammers can always switch to a different Google account.

Learning Vim for Beginners

Vim, or Vi Improved, is a powerful text editor that lets you do almost everything using keyboard shortcuts. You can replace text in a document, move or delete lines, automate edit actions, and more, without ever reaching for the mouse. Vim is the favorite source code editor of programmers but there’s no reason why you cannot use this editor for writing documents or composing long emails.

Vim can be confusing for people who are used to working inside visual editors like Microsoft Word or Google Docs but spend some time with Vim and it will be difficult for you to go back. Entire books have been devoted to teaching Vim but if you can manage to learn the basic commands, you’ll find yourself more efficient and productive.

Vim Logo

How to Learn Vi

Here’s a list of online tutorials and other helpful resources to help you learn Vim.

1. OpenVim - An interactive tutorial for learning the basics of Vim. Switch to the Practice page to test your existing Vim skills.

2. Vim Adventures - An online puzzle game for learning and memorizing Vim commands. You play the character of a blinking cursor that has to navigate the maze with the letter keys. If you are stuck, you can always type :help for a hint.

3. Vim Genius - This a flashcard style game to help you learn the basics of Vim. There are dedicated lessons for learning the motion keys (h,j,k,l) and for mastering copy-paste in vim.

4. Learn to Love Vim - The Linux Voice magazine has put together a quick video tutorial to get you started with Vim.

5. Vim Basics - Derek Wyatt has produced a bunch of video tutorials (screencasts) around teaching Vim. A good resource for novice users who prefer learning Vim by watching than reading.

6. Learning Vim - Mike Coutermarsh covers getting up and running, and eventually productive with Vim.

7. Vim - Precision Editing - Drew Neil, author of the popular Practical Vim title and Vimcasts.org, walks you through Vim and how the text editor is optimized for mouseless operations.

8. Vim Tutorial - The official Vim documentation is excellent and includes a step-by-step tutorial. You can also access this tutorial inside the Vim program through the :vimtutor command.

9. Vim Cheat Sheet - Print this because you’ll need it later.

10. Vim Masterclass - This Udemy course will help you master all the concepts of Vim and how to ‘think’ in Vim.

If you spend a lot of time typing text, learning Vim will be totally worth the effort. I wrote this article inside Visual Studio Code with Vim key bindings.

How to Move Files Uploads from Google Forms to a Specific Folder in Google Drive

The File Upload feature of Google Forms lets you receive files from form respondents directly in your Google Drive. You may add the File Upload question in your Google Form to receive PDF assignments from students, résumé applications, portfolio images from contestants, and so on. When a respondent uploads a file through Google Forms, the file are stored in a fixed folder of your Google Drive. All files are uploaded in the same folder and, thus looking at the file in your Google Drive, it is difficult to determine which respondent has uploaded which set of files. We can however use Google Apps Script with Google Form triggers to instantly organize files in Google Drive as soon as they are uploaded by the form respondent. You can change the destination folder where files are stored or create custom folders based on the form response.

Organiza File Uploads in Google Drive

    In the following example, we will create a parent folder in Google Drive to house all the uploaded files. Each form response will have its own subfolder and all files for that specific form entry will go in the same folder.

Create Parent Folder

To get started, go to your Google Drive and create a new folder (or use an existing folder). Open the folder and grab the ID of the folder from the browser’s address bar as shown in the screenshot.

Add the Google Script

Next, go to your Google Form that is accepting File Uploads and choose Script Editor from the 3-dot menu.   Inside the script editor, remove all the existing code and copy-paste the following snippet. Remember to replace the Folder Id in line #1 with the Id of the folder that you’ve created in the previous step.
const PARENT_FOLDER_ID = "<<Folder ID here>>";

const initialize = () => {
  const form = FormApp.getActiveForm();
  ScriptApp.newTrigger("onFormSubmit").forForm(form).onFormSubmit().create();
};

const onFormSubmit = ({ response } = {}) => {
  try {
    // Get a list of all files uploaded with the response
    const files = response
      .getItemResponses()
      // We are only interested in File Upload type of questions
      .filter(
        (itemResponse) =>
          itemResponse.getItem().getType().toString() === "FILE_UPLOAD"
      )
      .map((itemResponse) => itemResponse.getResponse())
      // The response includes the file ids in an array that we can flatten
      .reduce((a, b) => [...a, ...b], []);

    if (files.length > 0) {
      // Each form response has a unique Id
      const subfolderName = response.getId();
      const parentFolder = DriveApp.getFolderById(PARENT_FOLDER_ID);
      const subfolder = parentFolder.createFolder(subfolderName);
      files.forEach((fileId) => {
        // Move each file into the custom folder
        DriveApp.getFileById(fileId).moveTo(subfolder);
      });
    }
  } catch (f) {
    Logger.log(f);
  }
};
Tip: The script can also be enhanced to create custom folder names based on the user’s answers in the form response.

Create OnFormSubmit Trigger

Inside the script editor, select initialize from the function drop-down and click the Run button to create the OnFormSubmit trigger for your current Google Form. This will essentially run the Apps Script code whenever someone submits a new form entry and upload files to a specific folder in Google Drive.     That’s it. Go to your Google Form and submit a new test entry. You should now see all the uploaded files neatly organized in a custom folder under the parent folder. The name of the custom folder is the unique Response Id that Google Forms automatically assigns to every form submission.   PS: You can combine File Uploads in Google Forms with Document Studio to generate customized PDFs (certificates, employee ID cards, etc) from the uploaded images

How to Send a File to Participants After they Submit your Google Form

When someone fills out your Google Form, you may want to send a file or two to the form respondent. For instance:

  1. A school teacher can send a Word document containing a copy of the answers to students who have completed the quiz.
  2. A musician can send her latest composition as an MP3 file to users who have completed her website survey built with Google Forms.
  3. An author can email a preview copy of her upcoming book in PDF to fans who have subscribed to her newsletter.
  4. An online store may send their catalog PDF to customers who have placed orders through Google Forms.

Demo - Send Files with Google Forms

Open this Google Form, type your email address and hit the Submit button. Check your inbox and you should immediately find a PDF copy of our famous 101 useful websites collection.

How to Attach Files to Form Emails

The Email Notifications add-on for Google Forms lets you send customized emails to form respondents after a new entry is received. The same notification can also be customized to include one or more file attachments that are picked from your Google Drive and sent as an attachment in the auto-responder email.

Add File to Google Drive

  1. Go to the Google Drive website and upload any file that you wish to send via Google Forms.
  2. Change the sharing settings on the file so that it is accessible to anyone with a link. You cannot attach private files to form emails.
  3. Right-click the file in Google Drive and choose Get Link to copy the full URL of the file to the clipboard.

File Attachments in Google Drive

Configure Google Forms

  1. Go to your Google Form, and create a new email rule.
  2. Check the Notify Form Submitter option and select the form field where you ask for the email address of the form submitter.
  3. Go to the Attach Files section and paste the URL of the Google Drive file that you have copied in the previous step.

Save the rule to activate it. Submit a new form and when new users submit the form, they’ll automatically receive a copy of your file as an attachment.

Send File Attachment

PS: You may use this workflow to send files in any format including PDF, Images, Audio, Video, GIFs, PowerPoint, Excel and Word documents. The file size should be less than 5 MB. Also, it is not possible to attach native Google file formats like Google Docs, Sheets and Google Slides.

JavaScript Design Patterns

This article is a summary of the various design patterns in JavaScript that help us create clean, easier to maintain code without polluting the global namespace.

Object Literal Design Pattern

To avoid the possibility of collusion with other variables of the same name in the global namespace, take all your variables and functionn and make them part of an object with a unique name.

var com = com || {};
com.digitalinspiration = com.digitalinspiration || {};
com.digitalinspiration.person = {
  _name: "Amit Agarwal",
  _country: "",
  setCountry: function (country) {
    this._country = country;
  },
  printCountry: function () {
    console.log(this._name + " lives in " + this._country);
  },
};
com.digitalinspiration.person.setCountry("India");
com.digitalinspiration.person.printCountry();

Module Design Pattern

This pattern helps create private variables in JavaScript that cannot be accessed from the global scope as everything is wrapped inside an IIFE. We create a module that returns an object containing all the public functions. The variable are not accessible outside the module.

var personModule = (function () {
  // private variables and methods
  var _name = "Amit Agarwal";
  var _country = "";
  var print = function () {
    console.log(_name + " lives in " + _country);
  };
  return {
    setCountry: function (country) {
      _country = country;
    },
    printCountry: function () {
      console.log("Calling private method to print " + _country);
      print();
    },
  };
})();

personModule.setCountry("India");
personModule.printCountry();

Module Reveal Pattern

The Reveal Module design pattern makes it easy for the private methods and properties to communicate with the public methods. All methods and variables are hidden unless deliberately exposed inside the returning object.

var personModule = (function () {
  var _name = "Amit Agarwal";
  var _interests = [];
  function _printInterests() {
    console.log(_name + " likes " + _interests.join(", "));
  }
  function addInterest(interest) {
    _interests.push(interest);
  }
  function printInterests() {
    console.log("Calling private method");
    _printInterests();
  }
  return {
    printInterests: printInterests,
    addInterest: addInterest,
  };
})();

personModule.addInterest("Travel");
personModule.addInterest("Reading");
personModule.printInterests();

Avoid the Global Scope

Here we conditionally add our module to the global scope and make everyithng private by wrapping our entire module in an IIFE. The advantage with the pattern is that we are not immediately adding elements to the global scope but performing checks to avoid overriding names.

(function (win) {
  var personModule = (function () {
    var _name = "Amit Agarwal";
    function printName() {
      console.log(_name);
    }
    return {
      printName: printName,
    };
  })();
  if (!win.personModule) {
    win.personModule = personModule;
  } else {
    throw new Error("Cannot initialize application");
  }
})(window);

window.personModule.printName();

YouTube Email Alerts – Monitor Videos around your favorite Topics

The Google Alerts service makes it easy for you to monitor brand mentions and your favorite topics on the Internet. Just specify one or more keywords and Google will send an email notification when new web pages matching your search keywords are found on the web.

The YouTube Email Alerts service is similar to Google Alerts but instead of scanning the whole worldwide web, it limits the searches to videos uploaded on the YouTube website. It then sends automatic email notifications when new videos are uploaded on YouTube around your topics of interest.

Here’s a sample email notification sent by the YouTube Alert system that, in this example, is configured to track new video uploads around three topics - Tesla Model Y, The Queen’s Gambit, and Minecraft tutorials.

YouTube Email Alerts

Setup YouTube Email Alerts

Here’s a step by step guide on how to set up your own YouTube email alert system for monitoring videos around your topics of interest.

  1. Click here to make a copy of the YouTube alert script in your Google Account.

  2. Inside the Google Script, go to line #12 and update the default configuration. You need to specify the email address where the alerts should arrive, the topic or keywords that you wish to track, and the list of negative words. If a matching video contains any of the negative words, they’ll be filtered out of the email notification.

  3. Go to the Run menu and choose the initialize option. Authorize the Google Script and your email system is deployed instantly. Go to your Gmail sent items and you should see an email alert with the matching videos.

The system is now deployed and it will send one email digest per day with a list of matching videos. The email is sent around 11 AM GMT but you can change this time by updating the value of the emailAlertHour property in the configuration.

Configure Video Alerts

How YouTube Email Alerts Work

The alert system is built using the official YouTube API with Google Apps Script. You can find the complete source code on Github.

When you run the initialize function, it creates a cron job that will automatically run once per day around the specified hour. It then uses the YouTube API to find all matching videos that have been uploaded on YouTube since the last execution of the script. It takes the 10 most relevant videos and adds them to the notification email.

const fetchYouTubeVideos = (query = "cats") => {
  const date = new Date();
  date.setDate(date.getDate() - 3);
  const dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'";
  const publishedAfter = Utilities.formatDate(date, "UTC", dateFormat);
  const { items = [] } = YouTube.Search.list(["snippet"], {
    maxResults: 10,
    regionCode: "US",
    publishedAfter: publishedAfter,
    relevanceLanguage: "en",
    q: query,
    type: ["video"],
    fields: "items(id(videoId),snippet(title, channelTitle, channelId))",
  });
  return items.map((item) => {
    const {
      id: { videoId },
      snippet: { title, channelTitle, channelId },
    } = item;
    return { videoId, title, channelTitle, channelId };
  });
};

The preferredLanguage parameter in the configuration is, by default, set to en (English) and instructs YouTube to return videos that are most relevant to the specified language. You can use your two-letter language ISO code (like fr from French or hi for Hindi) here.

Similarly, the regionCode parameter (default is US) helps you restrict search results to videos that can be viewed in your country.

Also see: Google Drive File Monitor

How to Share User Properties between Google Apps Script Projects

The Properties Service of Google Apps Script is used by developers to store app configuration and user specific settings. The properties data is scoped to a specific user, or a specific project, and cannot be shared between different projects.

The Email Form Notifications add-on also uses the Properties Service to store rules that are defined by the user. The rules created by User A are not accessible to User B.

However, in some specific cases, we may want to give access to our store data to another user so they may build upon the existing configuration rather than having to build everything from scratch.

The new import export option allows the user to export properties data as a plain text file that can be imported into the property store of another user.

Access the Property Store

On the server side (Google Script), we define two methods - one for exporting data as a JSON file and the other method for importing data from the property store of another user into our own.

/* Choose DocProperties for editor add-ons */
const getStore = () => {
  return PropertiesService.getUserProperties();
};

/* Export user settings */
const exportUserData = () => {
  const data = getStore().getProperties();
  return JSON.stringify(data);
};

/* Import user settings */
const importUserData = (data) => {
  const json = JSON.parse(data);
  getStore().setProperties(json);
  return 'OK';
};

Export User Properties as a Text File

For exporting data, the HTML file contains a simple download button that connects to the server, fetches the data and allows the user to save this data as a text file on their computer.

<p>Export Data</p>
<button onclick="downloadFile();return false;" href="#">Export</button>

<script>
  function downloadFile() {
    google.script
      .withSuccessHandler(function (data) {
        var a = document.createElement('a');
        var blob = new Blob([data], {
          type: 'text/plain',
        });
        var url = URL.createObjectURL(blob);
        a.setAttribute('href', url);
        a.setAttribute('download', 'file.txt');
        a.click();
      })
      .exportUserData();
  }
</script>

Import User Properties from a Text File

For importing data into the property store, the user can upload a text (JSON) file that contains data as key-value pairs. These files are easily readable in any text editor and you can also add define new properties by adding new keys to the JSON file.

<p>Import data</p>
<input type="file" id="file" accept="text/plain" />

<script>
  document.getElementById('file').addEventListener(
    'change',
    function (event) {
      var file = event.target.files[0];
      if (file.type !== 'text/plain') {
        window.alert('Unsupported file');
        return;
      }
      var reader = new FileReader();
      reader.onload = function (e) {
        google.script.run
          .withSuccessHandler(function (success) {
            window.alert(success);
          })
          .withFailureHandler(function (err) {
            window.alert(err);
          })
          .importUserData(e.target.result);
      };
      reader.readAsText(file);
    },
    false
  );
</script>

The File Reader API of JavaScript is used to read the contents of the selected text file. The onload event gets fired when the file has been successfully read in memory.

The readAsText method of File Reader will read the file as a string but you may also use the readAsDataURL method should be wish to upload file in base64 encoded format that can be decoded on the server.

Useful JavaScript Functions

The code snippets are from a JavaScript course on Udemy.

  1. Create a function that can be invoked only once.
const once = (fn, ...args) => {
  let called = false;
  return () => {
    if (called === false) {
      called = true;
      return fn(...args);
    }
    return 'Cannot call again';
  };
};

const printName = (text, time) => console.log(`${text} at ${time}`);
const fn = once(printName, 'Google', new Date().toString());

console.log(fn());
console.log(fn());
  1. Measure the time it takes for a JavaScript function to run.
const getUserData = async (user) => {
  const response = await fetch(`https://api.github.com/users/${user}`);
  const json = await response.json();
  return json;
};

const time = (fn, ...args) => {
  console.time('time');
  const result = fn(...args);
  console.timeEnd('time');
  return result;
};

time(() => getUserData('labnol'));
  1. A debounce function that delays invocation until a certain amount of time has passed since the last time that debounce function was invoked.
const debounce = (fn, waitInMs) => {
  let debounced = false;
  return (...args) => {
    if (debounced) clearTimeout(debounced);
    debounced = setTimeout(() => fn(...args), waitInMs);
  };
};

const getWindowLayout = (event) => {
  console.log(event, window.innerHeight, window.innerWidth);
};

window.addEventListener('resize', debounce(getWindowLayout, 500));

How to Create JSON Web Token (JWT) with Google Apps Script

You can use Google Script to create JSON Web Tokens (JWT) that can be provided to secure routes so that only authenticated requests that contain a valid token can connect to the APIs (e.g., the Zoom API).

All JSON Web Tokens have three parts:

  1. The header that specifies the hash algorithm that is used for signing and decrypting the JWT.
  2. The payload in JSON format that contains all the user data. The iat and exp properties represent the issue date and the expiration time respectively but you can pass any data to the payload.
  3. The signature data that allows APIs to establish the authenticity of the access token.

The parts are joined with a dot (period) and data is encoded in Base64 using the Utilities.base64EncodeWebSafe method of Apps Script.

Create JSON Web Token

const createJwt = ({ privateKey, expiresInHours, data = {} }) => {
  // Sign token using HMAC with SHA-256 algorithm
  const header = {
    alg: 'HS256',
    typ: 'JWT',
  };

  const now = Date.now();
  const expires = new Date(now);
  expires.setHours(expires.getHours() + expiresInHours);

  // iat = issued time, exp = expiration time
  const payload = {
    exp: Math.round(expires.getTime() / 1000),
    iat: Math.round(now / 1000),
  };

  // add user payload
  Object.keys(data).forEach(function (key) {
    payload[key] = data[key];
  });

  const base64Encode = (text, json = true) => {
    const data = json ? JSON.stringify(text) : text;
    return Utilities.base64EncodeWebSafe(data).replace(/=+$/, '');
  };

  const toSign = `${base64Encode(header)}.${base64Encode(payload)}`;
  const signatureBytes = Utilities.computeHmacSha256Signature(
    toSign,
    privateKey
  );
  const signature = base64Encode(signatureBytes, false);
  return `${toSign}.${signature}`;
};

Generate Token with your Private Key & Payload

const generateAccessToken = () => {
  // Your super secret private key
  const privateKey =
    'ZPYu33tz8QYU3hwJQXgHpZsKfYn0r2poopBx7x1n3rmeIvuGU4wf65kk6rV1DrN';
  const accessToken = createJwt({
    privateKey,
    expiresInHours: 6, // expires in 6 hours
    data: {
      iss: Session.getActiveUser().getEmail(),
      userId: 123,
      name: 'Amit Agarwal',
    },
  });
  Logger.log(accessToken);
};

You can paste the generated access token in jwt.io and you’ll be able to see the content (payload) of the decoded token. Please note that if the token has invalid signature data, the payload may still be decoded as it is encoded in Base64.

JSON Web Token with Google Apps Script

Decoding JWT Payload with Google Apps Script

const parseJwt = (jsonWebToken, privateKey) => {
  const [header, payload, signature] = jsonWebToken.split('.');
  const signatureBytes = Utilities.computeHmacSha256Signature(
    `${header}.${payload}`,
    privateKey
  );
  const validSignature = Utilities.base64EncodeWebSafe(signatureBytes);
  if (signature === validSignature.replace(/=+$/, '')) {
    const blob = Utilities.newBlob(
      Utilities.base64Decode(payload)
    ).getDataAsString();
    const { exp, ...data } = JSON.parse(blob);
    if (new Date(exp * 1000) < new Date()) {
      throw new Error('The token has expired');
    }
    Logger.log(data);
  } else {
    Logger.log('🔴', 'Invalid Signature');
  }
};

If you are new to JWT, the video tutorials by Kyle Cook here and here are a good place to start.

Lite YouTube Embeds – A Better Method for Embedding YouTube Videos on your Website

It is easy to embed a YouTube video but you’ll be surprised to know how much extra weight a single YouTube video embed can add to your web pages. The browser has to download ~800 kB of data (see screenshot) for rendering the YouTube video player alone. And these files are downloaded even before the visitor has clicked the play button.

Embed YouTube Video Player Size

The embedded YouTube video not only increases the byte size of your web pages but the browser has to make multiple HTTP requests to render the video player. This increases the overall loading time of your page thus affecting the page speed and the core vitals score of your website.

The other drawback with the default YouTube embed code is that it renders a video player of fixed dimensions and isn’t responsive. If people view your website on a mobile phone, the video player may not resize properly for the small screen.

Embed YouTube Videos without Increasing Page Size

The now defunct Google+ employed a very clever technique for embedding YouTube videos. When the page was initially loaded, Google+ would only embed the thumbnail image of the YouTube video and the actual video player was loaded only when the user clicked inside thumbnail.

The thumbnail frame image of YouTube videos are about 15 kB in size so we are able to reduce the size of web pages by almost an MB.

Lite YouTube Embed Demo

Open this demo page to view the Google+ technique in action. The first video the page is embedded using the default IFRAME code while the second video uses the lite mode that loads the YouTube video on demand only.

When a user clicks the play button, the thumbnail image is replaced with the standard YouTube video player with autoplay set to 1 so the video would play almost instantly. The big advantage is that the extra YouTube JavaScript gets loaded only when someone decides to watch the embedded video and not otherwise.

Light and Responsive YouTube Embeds

The standard embed code for YouTube uses the IFRAME tag where the width and height of the video player are fixed thus making the player non-responsive.

The new on-demand embed code for YouTube is responsive that adjusts the player dimensions automatically based on the screen size of the visitor.

YouTube Embed Code

Embed YouTube Videos Responsively - Tutorial

Step 1: Copy-paste the following HTML snippet anywhere in your web page where you would like the YouTube video to appear. Remember to replace VIDEO_ID with the actual ID of your YouTube video.

<div class="youtube-player" data-id="VIDEO_ID"></div>

We will not assign height and width since the video player will automatically occupy the width of the parent while the height is auto-calculated. You can also paste multiple DIV blocks with different video IDs if you need to embed multiple YouTube videos on the same page.

Step 2: Copy-paste the JavaScript anywhere in your web template. The script finds all embedded videos on a web page and then replaces the DIV elements with the video thumbnails and a play button (see demo).

<script>
  /*
   * Light YouTube Embeds by @labnol
   * Credit: https://www.labnol.org/
   */

  function labnolIframe(div) {
    var iframe = document.createElement('iframe');
    iframe.setAttribute(
      'src',
      'https://www.youtube.com/embed/' + div.dataset.id + '?autoplay=1&rel=0'
    );
    iframe.setAttribute('frameborder', '0');
    iframe.setAttribute('allowfullscreen', '1');
    iframe.setAttribute(
      'allow',
      'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
    );
    div.parentNode.replaceChild(iframe, div);
  }

  function initYouTubeVideos() {
    var playerElements = document.getElementsByClassName('youtube-player');
    for (var n = 0; n < playerElements.length; n++) {
      var videoId = playerElements[n].dataset.id;
      var div = document.createElement('div');
      div.setAttribute('data-id', videoId);
      var thumbNode = document.createElement('img');
      thumbNode.src = '//i.ytimg.com/vi/ID/hqdefault.jpg'.replace(
        'ID',
        videoId
      );
      div.appendChild(thumbNode);
      var playButton = document.createElement('div');
      playButton.setAttribute('class', 'play');
      div.appendChild(playButton);
      div.onclick = function () {
        labnolIframe(this);
      };
      playerElements[n].appendChild(div);
    }
  }

  document.addEventListener('DOMContentLoaded', initYouTubeVideos);
</script>

Step 3: Copy-paste the CSS before the closing head tag of your web template.

<style>
  .youtube-player {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    overflow: hidden;
    max-width: 100%;
    background: #000;
    margin: 5px;
  }

  .youtube-player iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 100;
    background: transparent;
  }

  .youtube-player img {
    object-fit: cover;
    display: block;
    left: 0;
    bottom: 0;
    margin: auto;
    max-width: 100%;
    width: 100%;
    position: absolute;
    right: 0;
    top: 0;
    border: none;
    height: auto;
    cursor: pointer;
    -webkit-transition: 0.4s all;
    -moz-transition: 0.4s all;
    transition: 0.4s all;
  }

  .youtube-player img:hover {
    -webkit-filter: brightness(75%);
  }

  .youtube-player .play {
    height: 72px;
    width: 72px;
    left: 50%;
    top: 50%;
    margin-left: -36px;
    margin-top: -36px;
    position: absolute;
    background: url('//i.imgur.com/TxzC70f.png') no-repeat;
    cursor: pointer;
  }
</style>

You can view the light YouTube embed technique in action on this Codepen page.

Please do note that Chrome and Safari browsers on iPhone and Android only allow playback of HTML5 video when initiated by a user interaction. They block embedded media from automatic playback to prevent unsolicited downloads over cellular networks.

YouTube Embed Tutorials

  1. Embed a YouTube Video with Sound Muted
  2. Place YouTube Video as your Webpage Background
  3. Embed Just a Portion of a YouTube Video