Search code examples
reactjsreact-hooksjsxvideo.jsreact-dom

Render only child element in DOM instead of the whole JSX element


I copied this file from a user showing how to display a video using an npm library called videoJS:

import React, { useEffect, useRef } from 'react'
import VideoJs from 'video.js'


const videoJsOptions = {
  controls: true,
  autoplay: true,
  fluid: false,
  loop: true,
  width: '100%',
  aspectRatio: '4:3',
  children: [
      'MediaLoader'
  ],
}

const VideoPlayer = ({ url }) => {
  const videoContainer = useRef()

  //  Setup the player
  useEffect(() => {

    //  Setting content like this because player.dispose() remove also the html content
    videoContainer.current.innerHTML = `
      <div data-vjs-player>
        <video class="video-js" />
      </div>
    `

    //  Setting logger level to all for dev
    if (process.env.NODE_ENV === 'development') {
      VideoJs.log('all')
    }

    const player = VideoJs(videoContainer.current.querySelector('video'), videoJsOptions, async () => {
      player.src({ src: url, type: 'video/mp4' });
    })

    //  When destruct dispose the player
    return () => player.dispose()
  }, [url])

  console.log(videoContainer)
  
  return <div ref={videoContainer} />
}

export default VideoPlayer

the problem is that there is this parent div rendered which pushes the actual video far below the page: enter image description here

I'd like to remove this parent div while only keeping the video element. How do I go about doing this?


Solution

  • You need to load the video.js CSS files. For example:

    import 'video.js/dist/video-js.min.css';
    

    In the demo I load the CSS files from the CDN, and as you can see it looks fine.

    Note: the demo uses my implementation of video.js in React. The internal component Player exposes an imperative API to the parent, and since the JSX inside is not dependent on changing props it won't re-render the actual DOM. It will only dispose of the player when it's unmounted.

    const { useEffect, useRef, forwardRef, useImperativeHandle } = React;
    
    const videoJsOptions = {
      controls: true,
      autoplay: true,
      loop: true,
      width: '100%',
      aspectRatio: '16:9',
      children: [
          'MediaLoader'
      ],
    }
    
    const Player = forwardRef((_, ref) => {
      const video = useRef();
      const player = useRef();
      
      useEffect(() => {
        player.current = videojs(video.current, videoJsOptions);
        
        return () => {
          player.current.dispose();
        };
      }, []);
      
      useImperativeHandle(ref, () => ({
        play: (src, type) => {
          const p = player.current;
          
          p.ready(() => {
            p.src({ src, type });
          });
        }
      }));
      
      return (
        <div data-vjs-player>
          <video className="video-js" ref={video} />
        </div>
      );
    });
    
    const VideoPlayer = ({ url, type = 'video/mp4' }) => {
      const player = useRef();
      
      useEffect(() => {
        player.current.play(url, type);
      }, [url, type]);
      
      return <Player ref={player} />;
    }
    
    ReactDOM.render(
      <VideoPlayer url="https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4" />,
      root
    );
    html, body {
      margin: 0;
    }
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.0.0/video.min.js" integrity="sha512-LiILFcpZ9QKSH41UkK59Zag/7enHzqjr5lO2M0wGqGn8W19A/x2rV3iufAHkEtrln+Bt+Zv1U6NlLIp+lEqeWQ==" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.0.0/alt/video-js-cdn.min.css" integrity="sha512-zki7IiwYvLzVZouKA+V/vtIuW7cK8M2ug1kaRBItuBWG8TDvA6jrtjhKPOrz3RFGpt9OQC/xE7DUfsrHxoBXxg==" crossorigin="anonymous" />
    
    <div id="root"></div>