skip to Main Content

For this html:

<html>
<div id="vids">
   <video id="vid-1" src="vid1.mp4">
   </video>
   <video id="vid-2" src="vid2.mp4">
   </video>
</div>
</html>

The videos show up correctly.
I want to add an overlay text (caption) on top of each video like:
"Video 1 – Greenery"
"Video 2 – Island"

How can I do this using Javascript alone without modifying the HTML?
If not overlay, how do I display caption right above or below each video.

I have tried this:

var vid = document.getElementById("vid-1");
vid.insertAdjacentHTML("aftereend","Video 1 - Greenery");

But the text "Video 1 – Greenery", appears on right side of video.
If it appeared directly above or below video that would work too.

2

Answers


  1. var customStyle="z-index: 2;    background: red;    position: absolute;    top: 50%;    right: 50%;";
        var vid = document.querySelector("video");
        vid.insertAdjacentHTML("afterend",`<p style="${customStyle}">Video 1 - Greenery</p>`);
    

    Style is just for example purposes.

    Naturallly better is to use file styles.

    insertAdjacentHTML renders an Element, so if You put a raw text, then it creates a textNode.

    Better to wrap it in some tag – then style has access to it.

    using ${customStyle} You make it easier to maintain.

    It’s easier to set dynamic elements with it’s inline styes than to operate on window stylesheets.
    This solution makes styling Your text possible.

    Login or Signup to reply.
  2. TL;DR:


    We can map video IDs to captions. Then for all entries, we can create and insert the captions before their corresponding videos:

    const idToCaption = new Map();
    idToCaption.set("vid-1", "Video 1 - Greenery");
    idToCaption.set("vid-2", "Video 2 - Island");
    
    for (const [id, caption] of idToCaption.entries()) {
      const p = document.createElement("p");
      p.textContent = caption;
      
      const video = document.getElementById(id);
      
      // Assign caption as label
      p.id = `${id}-label`;
      video.setAttribute("aria-labelledby", p.id);
      
      video.parentElement.insertBefore(p, video);
    }
    <div id="vids">
      <video id="vid-1" src="vid1.mp4"></video>
      <video id="vid-2" src="vid2.mp4"></video>
    </div>

    Note:

    • To place the captions after their videos, reference the video’s next sibling.
    • For accessibility reasons we should declare the captions to label the videos, e.g. via aria-labelledby. (Captions labelling content is done implicitly when using <figcaption> as below.)

    Without styling, the captions will come before their videos. To place them above their videos we need styling (CSS).

    Placing elements above others can be achieved by taking them out of the flow, e.g. with position: absolute.

    We want to position the captions relative to the videos. But the captions are siblings and not children of the videos, which makes relative positioning more difficult.

    We can wrap each caption and its video in another element, which will effectively represent the video. That way, the caption is a child of the "video" (the wrapper), which makes positioning it correctly easier:

    const idToCaption = new Map();
    idToCaption.set("vid-1", "Video 1 - Greenery");
    idToCaption.set("vid-2", "Video 2 - Island");
    
    for (const [id, caption] of idToCaption.entries()) {
      const p = document.createElement("p");
      p.textContent = caption;
      
      const video = document.getElementById(id);
      
      p.id = `${id}-label`;
      video.setAttribute("aria-labelledby", p.id);
      
      // New code starts here
      const wrapper = document.createElement("span");
      wrapper.classList.add("wrapper");
      
      p.classList.add("caption");
      
      video.replaceWith(wrapper);
      wrapper.append(p, video);
    }
    .caption {
      position: absolute;
      /*Example: Position at top-left*/
      top: 0;
      left: 0;
    }
    
    .wrapper {
      /* `position:absolute` elements are relative to
       * first non-`static` ancestor.
       * Make wrapper non-`static`. */
      position: relative;
      
      /* `display:inline-block` makes
       * `.wrapper` behave like <video>,
       * but contain it (here: inherit its size). */
      display: inline-block;
    }
    
    /*Ignore; presentational*/
    .caption {
      margin: 0;
      color: white;
      background-color: black;
    }
    video {border: 1px solid black}
    <div id="vids">
      <video id="vid-1" src="vid1.mp4"></video>
      <video id="vid-2" src="vid2.mp4"></video>
    </div>

    There already exist elements for captions and the wrapper: <figcaption> and <figure>. You should prefer these where applicable, e.g. here:

    const idToCaption = new Map();
    idToCaption.set("vid-1", "Video 1 - Greenery");
    idToCaption.set("vid-2", "Video 2 - Island");
    
    for (const [id, caption] of idToCaption.entries()) {
      const figcaption = document.createElement("figcaption");
      figcaption.textContent = caption;
      
      const video = document.getElementById(id);
      
      /* Notice that <figcaption> automatically applies to its
       * <figure>'s other content. No "aria-labelledby" required anymore! */
      
      const figure = document.createElement("figure");
      
      video.replaceWith(figure);
      figure.append(figcaption, video);
    }
    figcaption {
      position: absolute;
      top: 0;
      left: 0;
    }
    
    figure {position: relative}
    
    /*Ignore; presentational*/
    figcaption {
      margin: 0;
      color: white;
      background-color: black;
    }
    video {border: 1px solid black}
    <div id="vids">
      <video id="vid-1" src="vid1.mp4"></video>
      <video id="vid-2" src="vid2.mp4"></video>
    </div>

    Note: The <figure> element is display: block whereas <video> is display: inline by default. The stylesheet should fix this inconsistency.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search