HTML video tag - change source on viewport without media attribute












0















I'm trying to implement a video in HTML with react. So if the viewport is under 750px width, I would like to change the video source to a smaller resolution/ smaller file (because of high data transfer and so on).



So in a perfect HTML world, you would build this:



<video controls> 
<source src="video-small.mp4" type="video/mp4" media="all and (max-width: 750px)">
<source src="video.mp4" type="video/mp4">
</video>


Since media is not (or no more) specified in the source tag if it's inside of a video tag, I can't use this anymore. Because Chrome is displaying the first source, which is the mobile video. No matter if desktop or mobile.



Second solution was to wrap a container around and set display: none; to the not visible video:



import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import LazyLoad from 'react-lazyload'

const MobileVideo = styled.div`
width: 100%;
height: 100%;
object-fit: cover;
display: none;
${({theme}) => theme.media.mobile`
display: block;
`}
`

const DesktopVideo = styled.div`
width: 100%;
height: 100%;
object-fit: cover;
display: block;
${({theme}) => theme.media.mobile`
display: none;
`}
`

const VideoComponent = ({
srcWebm,
srcMp4,
mobileSrcWebm,
mobileSrcMp4,
poster,
className,
forwardedRef,
...rest
}) => (
<React.Fragment>
<MobileVideo>
<video
playsInline
poster={poster.url}
className={className}
ref={forwardedRef}
{...rest}
>
{mobileSrcWebm.url !== '' && (
<source src={mobileSrcWebm.url} type="video/webm" />
)}
{mobileSrcMp4.url !== '' && (
<source src={mobileSrcMp4.url} type="video/mp4" />
)}
</video>
</MobileVideo>
<DesktopVideo>
<video
playsInline
poster={poster.url}
className={className}
ref={forwardedRef}
{...rest}
>
{srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
{srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
</video>
</DesktopVideo>
</React.Fragment>
)



In this case the correct video is displayed, but both videos are downloaded (in Chrome). Hiding doesn't prevent the browser from downloading. SAD!



Third solution was to use the second solution and remove the invisible component from the DOM:



import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import LazyLoad from 'react-lazyload'

import {sizes} from '../../lib/ThemeProvider/media'

const MobileVideo = styled.div`
width: 100%;
height: 100%;
object-fit: cover;
display: none;
${({ theme }) => theme.media.mobile`
display: block;
`}
`

const DesktopVideo = styled.div`
width: 100%;
height: 100%;
object-fit: cover;
display: block;
${({ theme }) => theme.media.mobile`
display: none;
`}
`

class VideoComponent extends React.Component {
state = {
showMobileSrc: true
}

componentDidMount() {
this.resize()
window.addEventListener('resize', this.resize)
}

componentWillUnmount() {
window.removeEventListener('resize', this.resize)
}

resize = () => {
if (window.innerWidth >= sizes.mobile) {
this.setState({ showMobileSrc: false })
} else {
this.setState({ showMobileSrc: true })
}
}

render() {
const { srcWebm,
srcMp4,
mobileSrcWebm,
mobileSrcMp4,
poster,
className,
forwardedRef,
...rest
} = this.props
const {showMobileSrc} = this.state
return (
<React.Fragment>
{showMobileSrc && <MobileVideo>
<video
playsInline
poster={poster.url}
className={className}
ref={forwardedRef}
{...rest}
>
{mobileSrcWebm.url !== '' && (
<source src={mobileSrcWebm.url} type="video/webm" />
)}
{mobileSrcMp4.url !== '' && (
<source src={mobileSrcMp4.url} type="video/mp4" />
)}
</video>
</MobileVideo>}
{!showMobileSrc && <DesktopVideo>
<video
playsInline
poster={poster.url}
className={className}
ref={forwardedRef}
{...rest}
>
{srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
{srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
</video>
</DesktopVideo>}
</React.Fragment>
)
}
}



But chrome is downloading both videos anyway. HTML seems to be correct. No idea what chrome is doing there..



First i really don"t understand why they removed the media attribute from the source tag inside of a video tag. Thats not consistently implemented.



Anyway: How can I change the source at a defined viewport width and prevent downloading both videos?










share|improve this question





























    0















    I'm trying to implement a video in HTML with react. So if the viewport is under 750px width, I would like to change the video source to a smaller resolution/ smaller file (because of high data transfer and so on).



    So in a perfect HTML world, you would build this:



    <video controls> 
    <source src="video-small.mp4" type="video/mp4" media="all and (max-width: 750px)">
    <source src="video.mp4" type="video/mp4">
    </video>


    Since media is not (or no more) specified in the source tag if it's inside of a video tag, I can't use this anymore. Because Chrome is displaying the first source, which is the mobile video. No matter if desktop or mobile.



    Second solution was to wrap a container around and set display: none; to the not visible video:



    import React from 'react'
    import PropTypes from 'prop-types'
    import styled from 'styled-components'
    import LazyLoad from 'react-lazyload'

    const MobileVideo = styled.div`
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: none;
    ${({theme}) => theme.media.mobile`
    display: block;
    `}
    `

    const DesktopVideo = styled.div`
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    ${({theme}) => theme.media.mobile`
    display: none;
    `}
    `

    const VideoComponent = ({
    srcWebm,
    srcMp4,
    mobileSrcWebm,
    mobileSrcMp4,
    poster,
    className,
    forwardedRef,
    ...rest
    }) => (
    <React.Fragment>
    <MobileVideo>
    <video
    playsInline
    poster={poster.url}
    className={className}
    ref={forwardedRef}
    {...rest}
    >
    {mobileSrcWebm.url !== '' && (
    <source src={mobileSrcWebm.url} type="video/webm" />
    )}
    {mobileSrcMp4.url !== '' && (
    <source src={mobileSrcMp4.url} type="video/mp4" />
    )}
    </video>
    </MobileVideo>
    <DesktopVideo>
    <video
    playsInline
    poster={poster.url}
    className={className}
    ref={forwardedRef}
    {...rest}
    >
    {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
    {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
    </video>
    </DesktopVideo>
    </React.Fragment>
    )



    In this case the correct video is displayed, but both videos are downloaded (in Chrome). Hiding doesn't prevent the browser from downloading. SAD!



    Third solution was to use the second solution and remove the invisible component from the DOM:



    import React from 'react'
    import PropTypes from 'prop-types'
    import styled from 'styled-components'
    import LazyLoad from 'react-lazyload'

    import {sizes} from '../../lib/ThemeProvider/media'

    const MobileVideo = styled.div`
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: none;
    ${({ theme }) => theme.media.mobile`
    display: block;
    `}
    `

    const DesktopVideo = styled.div`
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    ${({ theme }) => theme.media.mobile`
    display: none;
    `}
    `

    class VideoComponent extends React.Component {
    state = {
    showMobileSrc: true
    }

    componentDidMount() {
    this.resize()
    window.addEventListener('resize', this.resize)
    }

    componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
    }

    resize = () => {
    if (window.innerWidth >= sizes.mobile) {
    this.setState({ showMobileSrc: false })
    } else {
    this.setState({ showMobileSrc: true })
    }
    }

    render() {
    const { srcWebm,
    srcMp4,
    mobileSrcWebm,
    mobileSrcMp4,
    poster,
    className,
    forwardedRef,
    ...rest
    } = this.props
    const {showMobileSrc} = this.state
    return (
    <React.Fragment>
    {showMobileSrc && <MobileVideo>
    <video
    playsInline
    poster={poster.url}
    className={className}
    ref={forwardedRef}
    {...rest}
    >
    {mobileSrcWebm.url !== '' && (
    <source src={mobileSrcWebm.url} type="video/webm" />
    )}
    {mobileSrcMp4.url !== '' && (
    <source src={mobileSrcMp4.url} type="video/mp4" />
    )}
    </video>
    </MobileVideo>}
    {!showMobileSrc && <DesktopVideo>
    <video
    playsInline
    poster={poster.url}
    className={className}
    ref={forwardedRef}
    {...rest}
    >
    {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
    {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
    </video>
    </DesktopVideo>}
    </React.Fragment>
    )
    }
    }



    But chrome is downloading both videos anyway. HTML seems to be correct. No idea what chrome is doing there..



    First i really don"t understand why they removed the media attribute from the source tag inside of a video tag. Thats not consistently implemented.



    Anyway: How can I change the source at a defined viewport width and prevent downloading both videos?










    share|improve this question



























      0












      0








      0








      I'm trying to implement a video in HTML with react. So if the viewport is under 750px width, I would like to change the video source to a smaller resolution/ smaller file (because of high data transfer and so on).



      So in a perfect HTML world, you would build this:



      <video controls> 
      <source src="video-small.mp4" type="video/mp4" media="all and (max-width: 750px)">
      <source src="video.mp4" type="video/mp4">
      </video>


      Since media is not (or no more) specified in the source tag if it's inside of a video tag, I can't use this anymore. Because Chrome is displaying the first source, which is the mobile video. No matter if desktop or mobile.



      Second solution was to wrap a container around and set display: none; to the not visible video:



      import React from 'react'
      import PropTypes from 'prop-types'
      import styled from 'styled-components'
      import LazyLoad from 'react-lazyload'

      const MobileVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: none;
      ${({theme}) => theme.media.mobile`
      display: block;
      `}
      `

      const DesktopVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      ${({theme}) => theme.media.mobile`
      display: none;
      `}
      `

      const VideoComponent = ({
      srcWebm,
      srcMp4,
      mobileSrcWebm,
      mobileSrcMp4,
      poster,
      className,
      forwardedRef,
      ...rest
      }) => (
      <React.Fragment>
      <MobileVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {mobileSrcWebm.url !== '' && (
      <source src={mobileSrcWebm.url} type="video/webm" />
      )}
      {mobileSrcMp4.url !== '' && (
      <source src={mobileSrcMp4.url} type="video/mp4" />
      )}
      </video>
      </MobileVideo>
      <DesktopVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
      {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
      </video>
      </DesktopVideo>
      </React.Fragment>
      )



      In this case the correct video is displayed, but both videos are downloaded (in Chrome). Hiding doesn't prevent the browser from downloading. SAD!



      Third solution was to use the second solution and remove the invisible component from the DOM:



      import React from 'react'
      import PropTypes from 'prop-types'
      import styled from 'styled-components'
      import LazyLoad from 'react-lazyload'

      import {sizes} from '../../lib/ThemeProvider/media'

      const MobileVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: none;
      ${({ theme }) => theme.media.mobile`
      display: block;
      `}
      `

      const DesktopVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      ${({ theme }) => theme.media.mobile`
      display: none;
      `}
      `

      class VideoComponent extends React.Component {
      state = {
      showMobileSrc: true
      }

      componentDidMount() {
      this.resize()
      window.addEventListener('resize', this.resize)
      }

      componentWillUnmount() {
      window.removeEventListener('resize', this.resize)
      }

      resize = () => {
      if (window.innerWidth >= sizes.mobile) {
      this.setState({ showMobileSrc: false })
      } else {
      this.setState({ showMobileSrc: true })
      }
      }

      render() {
      const { srcWebm,
      srcMp4,
      mobileSrcWebm,
      mobileSrcMp4,
      poster,
      className,
      forwardedRef,
      ...rest
      } = this.props
      const {showMobileSrc} = this.state
      return (
      <React.Fragment>
      {showMobileSrc && <MobileVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {mobileSrcWebm.url !== '' && (
      <source src={mobileSrcWebm.url} type="video/webm" />
      )}
      {mobileSrcMp4.url !== '' && (
      <source src={mobileSrcMp4.url} type="video/mp4" />
      )}
      </video>
      </MobileVideo>}
      {!showMobileSrc && <DesktopVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
      {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
      </video>
      </DesktopVideo>}
      </React.Fragment>
      )
      }
      }



      But chrome is downloading both videos anyway. HTML seems to be correct. No idea what chrome is doing there..



      First i really don"t understand why they removed the media attribute from the source tag inside of a video tag. Thats not consistently implemented.



      Anyway: How can I change the source at a defined viewport width and prevent downloading both videos?










      share|improve this question
















      I'm trying to implement a video in HTML with react. So if the viewport is under 750px width, I would like to change the video source to a smaller resolution/ smaller file (because of high data transfer and so on).



      So in a perfect HTML world, you would build this:



      <video controls> 
      <source src="video-small.mp4" type="video/mp4" media="all and (max-width: 750px)">
      <source src="video.mp4" type="video/mp4">
      </video>


      Since media is not (or no more) specified in the source tag if it's inside of a video tag, I can't use this anymore. Because Chrome is displaying the first source, which is the mobile video. No matter if desktop or mobile.



      Second solution was to wrap a container around and set display: none; to the not visible video:



      import React from 'react'
      import PropTypes from 'prop-types'
      import styled from 'styled-components'
      import LazyLoad from 'react-lazyload'

      const MobileVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: none;
      ${({theme}) => theme.media.mobile`
      display: block;
      `}
      `

      const DesktopVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      ${({theme}) => theme.media.mobile`
      display: none;
      `}
      `

      const VideoComponent = ({
      srcWebm,
      srcMp4,
      mobileSrcWebm,
      mobileSrcMp4,
      poster,
      className,
      forwardedRef,
      ...rest
      }) => (
      <React.Fragment>
      <MobileVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {mobileSrcWebm.url !== '' && (
      <source src={mobileSrcWebm.url} type="video/webm" />
      )}
      {mobileSrcMp4.url !== '' && (
      <source src={mobileSrcMp4.url} type="video/mp4" />
      )}
      </video>
      </MobileVideo>
      <DesktopVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
      {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
      </video>
      </DesktopVideo>
      </React.Fragment>
      )



      In this case the correct video is displayed, but both videos are downloaded (in Chrome). Hiding doesn't prevent the browser from downloading. SAD!



      Third solution was to use the second solution and remove the invisible component from the DOM:



      import React from 'react'
      import PropTypes from 'prop-types'
      import styled from 'styled-components'
      import LazyLoad from 'react-lazyload'

      import {sizes} from '../../lib/ThemeProvider/media'

      const MobileVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: none;
      ${({ theme }) => theme.media.mobile`
      display: block;
      `}
      `

      const DesktopVideo = styled.div`
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      ${({ theme }) => theme.media.mobile`
      display: none;
      `}
      `

      class VideoComponent extends React.Component {
      state = {
      showMobileSrc: true
      }

      componentDidMount() {
      this.resize()
      window.addEventListener('resize', this.resize)
      }

      componentWillUnmount() {
      window.removeEventListener('resize', this.resize)
      }

      resize = () => {
      if (window.innerWidth >= sizes.mobile) {
      this.setState({ showMobileSrc: false })
      } else {
      this.setState({ showMobileSrc: true })
      }
      }

      render() {
      const { srcWebm,
      srcMp4,
      mobileSrcWebm,
      mobileSrcMp4,
      poster,
      className,
      forwardedRef,
      ...rest
      } = this.props
      const {showMobileSrc} = this.state
      return (
      <React.Fragment>
      {showMobileSrc && <MobileVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {mobileSrcWebm.url !== '' && (
      <source src={mobileSrcWebm.url} type="video/webm" />
      )}
      {mobileSrcMp4.url !== '' && (
      <source src={mobileSrcMp4.url} type="video/mp4" />
      )}
      </video>
      </MobileVideo>}
      {!showMobileSrc && <DesktopVideo>
      <video
      playsInline
      poster={poster.url}
      className={className}
      ref={forwardedRef}
      {...rest}
      >
      {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
      {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
      </video>
      </DesktopVideo>}
      </React.Fragment>
      )
      }
      }



      But chrome is downloading both videos anyway. HTML seems to be correct. No idea what chrome is doing there..



      First i really don"t understand why they removed the media attribute from the source tag inside of a video tag. Thats not consistently implemented.



      Anyway: How can I change the source at a defined viewport width and prevent downloading both videos?







      html reactjs video responsive viewport






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited yesterday







      Tobi

















      asked yesterday









      TobiTobi

      4810




      4810
























          1 Answer
          1






          active

          oldest

          votes


















          0














          Simple JavaScript solution (not ReactJS specific, but see this component for a pure ReactJS solution)



          if (matchMedia) {
          var mq = window.matchMedia("(min-width: 600px)");
          mq.addListener(WidthChange);
          }

          function WidthChange(mq) {
          if (mq.matches) {
          // set source to desktop
          } else {
          // set source to mobile
          }
          }





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54251829%2fhtml-video-tag-change-source-on-viewport-without-media-attribute%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            Simple JavaScript solution (not ReactJS specific, but see this component for a pure ReactJS solution)



            if (matchMedia) {
            var mq = window.matchMedia("(min-width: 600px)");
            mq.addListener(WidthChange);
            }

            function WidthChange(mq) {
            if (mq.matches) {
            // set source to desktop
            } else {
            // set source to mobile
            }
            }





            share|improve this answer




























              0














              Simple JavaScript solution (not ReactJS specific, but see this component for a pure ReactJS solution)



              if (matchMedia) {
              var mq = window.matchMedia("(min-width: 600px)");
              mq.addListener(WidthChange);
              }

              function WidthChange(mq) {
              if (mq.matches) {
              // set source to desktop
              } else {
              // set source to mobile
              }
              }





              share|improve this answer


























                0












                0








                0







                Simple JavaScript solution (not ReactJS specific, but see this component for a pure ReactJS solution)



                if (matchMedia) {
                var mq = window.matchMedia("(min-width: 600px)");
                mq.addListener(WidthChange);
                }

                function WidthChange(mq) {
                if (mq.matches) {
                // set source to desktop
                } else {
                // set source to mobile
                }
                }





                share|improve this answer













                Simple JavaScript solution (not ReactJS specific, but see this component for a pure ReactJS solution)



                if (matchMedia) {
                var mq = window.matchMedia("(min-width: 600px)");
                mq.addListener(WidthChange);
                }

                function WidthChange(mq) {
                if (mq.matches) {
                // set source to desktop
                } else {
                // set source to mobile
                }
                }






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered yesterday









                OffbeatmammalOffbeatmammal

                5,55121838




                5,55121838






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54251829%2fhtml-video-tag-change-source-on-viewport-without-media-attribute%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Liquibase includeAll doesn't find base path

                    How to use setInterval in EJS file?

                    Petrus Granier-Deferre