video.js icon indicating copy to clipboard operation
video.js copied to clipboard

Provide an option to keep dom element when dispose

Open intijk opened this issue 7 years ago • 15 comments

Hi, I want to know could we add an option to keep the dom element when dispose is called?

My dom is generated by a template system, and then bind videojs to the video tag. Now I want to do some dynamic loading of the data, so I have to remove the videojs object, and create videojs object again, so the side effect of videojs makes this impossible. Because my tag generated by the template system disappear.

Not only this issue, but also the other one. https://github.com/videojs/video.js/issues/4878

Could we provide an option to let this possible?

intijk avatar Feb 22 '18 01:02 intijk

This is a bit tricky because we don't really store the original tag. The main thing we could probably do is restore a video element with the id of the player. It also gets a bit tricky because we have 3 embed options currently and technically, an audio element is available as well. And we probably shouldn't store the original embed as a string to re-use on dispose.

However, I'm thinking that for your situation, the template system should probably generate a container and the javascript should place the video player inside that container.

@videojs/core-committers thoughts on this feature request?

gkatsev avatar Feb 27 '18 22:02 gkatsev

I'd like to add another use-case to the feature request.

Summary:

  • On mobile web, I have a <video> tag which is inserted into the dom based on a user's click.
  • I would like to display a playlist of videos to the user.
  • Some of the videos in the playlist will be displayed with videojs but some will not.
  • Only one video will be "active" at a time.
  • I would like to use the same <video> tag across all those videos, because the browser will have "blessed" it as a video the user opted in to (for audio).

Here is a sample application, https://jsfiddle.net/hmjr001r/46/, showing:

  • Insert a bare bones <video>
  • Load a non-videojs video into that <video>
  • unload it
  • Load a videojs video into that <video>
  • dispose it
  • Load a non-videojs video into that <video> <==== issue

Notes: I am already using the player div ingest (data-vjs-player), so that the initial <video> tag stays in the dom as I initialize it into a videojs player. Which was super helpful, :).

Similar threads: #4397, #1023

tommyh avatar Mar 13 '18 23:03 tommyh

The core of the conflict is that video.js assume the tag is one time used. But the fact is that modern front-end is build upon the responsive idea.

video.js heavily build on top of the side effect , like change the tag id, remove the tag after videojs clean.

Those are not good practice. User will assume when an object clean, the environment would be recover to previous status, the side effect breaks the agreement.

It works fine before because less responsive environment it met, now when all the responsive components has the re-entry logic existed, keep yourself side-effect free is more and more import to work with other components.

intijk avatar Mar 21 '18 17:03 intijk

@gkatsev Dear developers, could you give some thoughts here?

intijk avatar Apr 02 '18 21:04 intijk

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Aug 10 '18 21:08 stale[bot]

Running into problems similar to those of the OP. Would love to see a solution here :). Keep up the good work!

rin avatar Sep 18 '18 07:09 rin

I'm also running into this issue with videojs lately. Specifically my use case is with that of elementor.

thsbrown avatar Feb 28 '19 00:02 thsbrown

To anyone coming here from google searching for how to get this to work with React, this was the solution I ended up with. I was trying to figure out how to quickly change the source for a videojs player and dispose wasn't working

function VideoViewer(props: Props) {
  const videoRef = useRef();
  const [requireRedraw, setRequireRedraw] = useState(false);

  // Handle any other effects separately to avoid re-mounting the video player when props change
  useEffect(() => {
    const videoNode = videoRef.current;
    const videoJsOptions = {
      ...VIDEO_JS_OPTIONS,
      sources: [
        {
          src: source,
          type: contentType,
        },
      ],
    };

    let player;
    if (!requireRedraw) {
      player = videojs(videoNode, videoJsOptions);
    }

    return () => {
      if (!player) {
        return;
      }

      // Video.js has a player.dispose() function that is meant to cleanup a previous video
      // We can't use this because it does some weird stuff to remove the video element from the page
      // This makes it really hard to use because the ref we keep still thinks it's on the page
      // requireRedraw just makes it so the video component is removed from the page _by react_
      // Then it's set to false immediately after so we can re-mount a new player
      setRequireRedraw(true);
    };
  }, [videoRef, source, contentType, hasFileInfo, setRequireRedraw, requireRedraw]);

  useEffect(() => {
    if (requireRedraw) {
      setRequireRedraw(false);
    }
  }, [requireRedraw]);

  return (
    <div>
      {!requireRedraw && (
        <div data-vjs-player>
          <video ref={videoRef} className="video-js" />
        </div>
      )}
    </div>
  );
}

neb-b avatar Aug 12 '19 20:08 neb-b

Big thank to @seanyesmunt <3

hmtri1011 avatar Sep 11 '19 10:09 hmtri1011

Is there any update to this issue of using dispose? I followed the DOCS on using videojs is a modal to show/hide video, but dispose STILL removes the video element from the DOM.

How about something that will just disconnect the player or stop the stream when the modal is closed? Honestly, I should only need to create the player once, then set the src when needed. The player shouldn't have to worry about being visible or not (that's up to the modal[jQuery]).

I might just have to switch back to hls.js even though it doesn't have nice styling.

ezbDoubleZero avatar Jan 05 '20 16:01 ezbDoubleZero

Recently started using videojs but also ran into this issue already. Would be really nice to have a cleanup function that doesn't remove the video element.

Cookiezzz avatar Jan 13 '20 19:01 Cookiezzz

I recently also ran into this problem, my fix was the following using React 17.0.1 and a functional component, this works in my React App, so hopefully somebody else has use of it. Better versions/revisions are always appreciated.

import React from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";

export const VideoJS = ( props ) => {
  const videoRef = React.useRef(null);
  const { options } = props;

  // making the video Element a seperate React Component 
  // it will get re-added/re-rendered every render of it's parent Component
  // fixes the dispose() method removing the element from DOM
  const VideoHtml = ( props ) => (
    <div data-vjs-player>
      <video ref={videoRef} className="video-js vjs-big-play-centered" />
    </div>
  );
  
  React.useEffect( () => {
    const videoElement = videoRef.current;
    let player;
    if( videoElement ) {
      player = videojs( videoElement, options, () => {
        console.log("player is ready");
      });
    }
    return () => {
      if( player ) {
        player.dispose();
      }
    }
  }, [options]);

  return (<VideoHtml />);
}
export default VideoJS;

hcbd avatar Apr 23 '21 12:04 hcbd

Also ran into this problem too, and hope videojs provide an option to keep dom element when dispose

SunShinewyf avatar Dec 28 '21 02:12 SunShinewyf

This works for me:

import React, {  useEffect, useState  } from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";

export const VideoJS = ({ videoJsOptions, ...props }) => {
  const [videoContainerEl, setVideoContainerEl] = useState(null);

  useEffect( () => {
    if (!videoContainerEl) return;

    videoContainerEl.innerHTML = `
      <div data-vjs-player>
        <video class="video-js vjs-big-play-centered" playsInline />
      </div>
    `;

    const videoEl = videoContainerEl.querySelector('video');
    const player = videojs(videoEl, videoJsOptions);

    return () => {
      player.dispose();
    }
  }, [videoJsOptions]);

  return <div {...props} ref={setVideoContainerEl} />;
}

export default VideoJS;

aravindanve avatar Feb 23 '22 11:02 aravindanve

Hi all, I have just published version 3.0.0 of my react-hook-videojs package that should support React 18 Strict Mode. It required a fair bit of DOM manipulation to undo the DOM destruction caused by videojs dispose. Let me know if it works for you! https://github.com/jimmycallin/react-hook-videojs/

jimmycallin avatar Jul 20 '22 13:07 jimmycallin