1

My aim is to display a different image or video displayed within a div, which changes when other elements are hovered over.

I think I have this working with just an image by checking what image file is specified in a data-src and loading that into an img tag on the page. However I need to change the markup from img to video when a movie file is specifed - that's what I need help with.

You can see the 'working' image version here (not the 3rd item has a placeholder video in the data-src so won't show): https://codepen.io/moy/pen/BaNxzdL

So currently on the page I have this empty image tag:

<div class="carousel__bg">
    <img src="" />
</div>

Image files are specified on multiple carousel items in a data-src like the example below:

<div class="carousel__item" data-src="img/content/1-wide.jpg">
    <div class="carousel__content">
        <h4 class="carousel__title">Behind The Scenes</h4>
        <span class="carousel__flag">// Featured</span>
        <h2 class="carousel__subtitle">Denim Cox in Fuck Yes Dude!</h2>
        <a href="#" class="carousel__btn">Read the Article</a>
    </div>
    <img src="img/content/1.jpg" class="carousel__image" />
</div>

And the javascript that gets the image URL and adds it to the page is this:

$(function() {

    var overlay = $('.carousel__bg img'), cached = {};

    $('.carousel__item').mouseenter(function() {

        var item = $(this),
        spot = $(this).index('.carousel__item'),
        value = item.attr('data-src');

        overlay.fadeTo(0,0).attr('src', value);

        if (!overlay[0].complete && !cached[spot]) {

            cached[spot] = true;
            $('.carousel__bg').addClass('loading');

            overlay.one('load', function() {
                $('.carousel__bg').removeClass('loading');
                overlay.fadeTo(300,1);
            });
        }
        else overlay.fadeTo(300,1);
    })

    .mouseleave(function() {
        overlay.finish();
    });
});

Obviously the problem is if I specify data-src="video/safari.mp4" it isn't going to work as it's currently trying to add the video into an img element. So how would I go about switching between img/video tags? A related issue would be to be able to load both an mp4 + webm/ogg versions of the file?

So would it need to be reworked to 'inject' an img or video element onto the page depending on the extension? I tried using an if/else statement to check if the data-src contained the .mp4 extension and just hardcoded a video element on the page to text but couldn't get that to work. :/

These files can be quite large which is why I'm not loading them until they're needed.

Edit

As a bit of an aside, I decided to put these items in a carousel to see if this effect would work - and it pretty much does!

However, I noticed the way that I fade out the images in the CSS (fade all of them out when the .carousel is hovered but then target the individual item and overwrite) is now a problem when you hover over the prev/next buttons as the images don't fade back in.

Anyone got a better way of handling this? I tried a 100% CSS method but maybe added a class would be better?

Slick carousel example: https://codepen.io/moy/pen/JjdvRyG

halfer
  • 19,824
  • 17
  • 99
  • 186
user1406440
  • 1,329
  • 2
  • 24
  • 59

1 Answers1

1

The video element part is not two hard, the important part is getting the mime type of the video to add to the source element. The data-src takes an array of video urls (of different types) and adds the different sources to the element after finding the type.

I updated your codepen

As for the buttons, they are inside the .carousel element so the will bubble the hover to all the elements styled based on that. I made some elements more specific so they will only change style when the list of items is hovered.

Finally, in order for the listeners to apply to the slick element, I changed them to .on.

var VIDEO_TYPES = {
  'mp4': 'video/mp4',
  'webm': 'video/webm',
  'ogv': 'video/ogg',
}



/**
 * Slick
 */

$(document).ready(function() {
  $('.slick-carousel').slick({
      //centerMode: true,
      centerPadding: '0',
      slidesToShow: 3,
      arrows: true,
      dots: false,
      prevArrow: '<a class="slick-arrow slick-arrow--prev"><span>&larr;</span></a>',
      nextArrow: '<a class="slick-arrow slick-arrow--next"><span>&rarr;</span></a>',
      responsive: [{
          breakpoint: 960,
          settings: {
            centerMode: true,
            slidesToShow: 1
          }
        },
        {
          breakpoint: 600,
          settings: {
            centerMode: true,
            slidesToShow: 1
          }
        },
        {
          breakpoint: 480,
          settings: {
            centerMode: true,
            slidesToShow: 1
          }
        }
      ]
    })
    .on('setPosition', function(event, slick) {
  slick.$slider.find(".slick-slide .tile:not(.position-set)").addClass('position-set').css('height', slick.$slideTrack.height() - 30 + 'px');
    });
    
    
 /**
 * Image Swap
 */
   var cached = {};
  var overlay_video = $(".carousel__bg video");
  var overlay_img = $(".carousel__bg img");
  var overlay = $(".carousel__bg");
  $(".carousel__item")
    .on('mouseenter', function() {
      
      var item = $(this),
        spot = $(this).index(".carousel__item"),
        value = item.data("src");
      
      overlay_video.empty();

      var overlay_item;
      overlay.fadeTo(0, 0);
      //videos will have an array ur urls
      var is_video = value instanceof Array;
      if(is_video) {
        overlay_item = overlay_video;
        overlay_img.attr("src", '');
       
        
        overlay_video.append(value.map((url) => {
           var extension = url.split('.').pop(); 
           var type = VIDEO_TYPES[extension];
            return `<source src="${url}" type="${type}">`
        }));
        
      } else {
        overlay_item = overlay_img;
        overlay_img.attr("src", value);
      }
      //force the video element to reload
      overlay_video.get(0).load();
      
      if (!overlay_item.complete && !cached[spot]) {
        cached[spot] = true;
        overlay.addClass("loading");
     
       
        overlay_item.one(is_video ? "loadeddata" : "load", function() {
           overlay.removeClass("loading");
           overlay.fadeTo(300, 1);
        });
      } else  overlay.fadeTo(300, 1);
    })

    .on('mouseleave', function() {
      overlay.finish();
    });
});
/**
 * Base styling.
 */

html {
 background: rgb(255,255,255);
 font-size: 62.5%;
 -webkit-font-smoothing: antialiased;
 -moz-osx-font-smoothing: grayscale;
 -webkit-overflow-scrolling: touch;
 -webkit-text-size-adjust: 100%;
}

body {
 background-color: transparent;
 color: rgb(0,0,0);
 font-variant-ligatures: common-ligatures discretionary-ligatures historical-ligatures;
 font-family: 'Roboto', sans-serif;
 font-size: 1.6rem;
 font-weight: 400;
 line-height: 1.6rem;
 margin: 0;
 padding: 30px 0 0;
 text-rendering: optimizeLegibility;
}



/**
 * Carousel
 */

.carousel {
 background: rgb(0,0,0);
 color: rgb(255,255,255);
 height: 640px;
 margin: 0 auto;
 overflow: hidden;
 position: relative;
 width: 100%;
 max-width: 1200px;
}

.carousel:before,
.carousel:after {
 background: rgba(255,255,255,.25);
 content: "";
 height: 100%;
 position: absolute;
 top: 0;
 left: 33.33333%;
 width: 1px;
 z-index: 30;
}

.carousel:after {
 left: 66.66666%;
}



/**
 * Background (fullwidth) image
 */

.carousel__bg {
 position: absolute;
 top: 0;
 right: 0;
 bottom: 0;
 left: 0;
 height: 100%;
 width: 100%;
}

.carousel__bg.loading {
 background: url(../img/interface/loading.gif) no-repeat center center;
}

.carousel__bg img, .carousel__bg video {
 display: block;
 height: 640px;
 object-fit: cover;
 position: absolute;
 top: 0;
 right: 0;
 bottom: 0;
 left: 0;
 width: 100%; 
}



/**
 * Individual carousel item
 */

.carousel__item {
 box-sizing: border-box;
 float: left;
 height: 640px;
 position: relative;
 width: 33.33333%; 
}

.carousel__item:hover {
 cursor: pointer;
}



/* Text Content */

.carousel__content {
 background: rgba(0,0,0,.45);
 box-sizing: border-box;
 color: rgb(255,255,255);
 height: 100%;
 min-height: 100%;
 padding: 30px;
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 z-index: 15;
}


.carousel__title,
.carousel__subtitle,
.carousel__flag {
 color: rgb(255,255,255);
 letter-spacing: 1px;
 font-family: 'Anton', sans-serif;
 font-weight: 400;
 line-height: 1;
 margin: 0 0 5px;
 padding: 0;
 text-transform: uppercase;
}

.carousel__title {
 font-size: 20px;
 transition: all .25s;
}

.carousel__subtitle {
 display: none;
 font-size: 48px;
}

.carousel__flag {
 color: rgb(45,190,193);
 font-size: 14px;
}



/* Button */

.carousel__btn {
 background: transparent;
 border: 1px solid rgb(255,255,255);
 box-sizing: border-box;
 color: rgb(255,255,255);
 display: block;
 font-family: 'Anton', sans-serif;
 font-size: 12px;
 font-weight: 400;
 height: 45px;
 line-height: 45px;
 letter-spacing: 1px;
 opacity: 0;
 position: absolute;
 padding: 0 30px;
 bottom: 30px;
 left: 30px;
 right: 30px;
 text-align: center;
 text-decoration: none;
 text-transform: uppercase;
 transition: all .15s;
 -webkit-backface-visibility: hidden;
}

.carousel__btn:visited {
 background: transparent;
}

.carousel__btn:focus,
.carousel__btn:hover {
 background: rgb(45,190,193);
 border-color: rgb(45,190,193);
}



/* Image */

.carousel__image {
 display: block;
 height: 100%;
 opacity: 1;
 object-fit: cover;
 transition: all .30s;
 position: relative;
 width: 100%;
 max-width: 100%;
 -webkit-backface-visibility: hidden;
}



/* When hovering over the carousel, fade all the titles out */

.carousel>.slick-carousel>.slick-list:hover .carousel__title {
 opacity: .30;
}

/* But not the one contained without the 'item' you're hovering over */

.carousel:hover .carousel__item:hover .carousel__title {
 opacity: 1;
}

/* Fade all images out so the fullwidth background image is visble */

.carousel>.slick-carousel>.slick-list:hover  .carousel__image {
 opacity: 0;
}

/* Hide the flag element */

.carousel>.slick-carousel>.slick-list:hover .carousel__flag {
 display: none;
}

/* Show the subtitle */

.carousel:hover .carousel__item:hover .carousel__subtitle {
 display: block;
}

/* Display the CTA of the active item */

.carousel:hover .carousel__item:hover .carousel__btn {
 opacity: 1;
}






/* Slick Prev/Next */

.slick-carousel,
.slick-list,
.slick-track {
 height: 100%;
 min-height: 100%;
}


.slick-arrow {
 background: transparent;
 border: 1px solid rgb(255,255,255);
 color: rgb(255,255,255);
 display: block;
 font-family: 'Anton', sans-serif;
 font-size: 24px;
 height: 45px;
 line-height: 45px;
 margin-top: -30px;
 overflow: hidden;
 position: absolute;
 top: 50%;
 left: 30px;
 text-align: center;
 transform: rotate(45deg);
 transition: all .15s;
 width: 45px;
 z-index: 60;
}

.slick-arrow:hover {
 background: rgb(255,255,255);
 color: rgb(0,0,0);
}

.slick-arrow span {
 display: block;
 transform: rotate(-45deg);
}

.slick-arrow--next {
 left: auto;
 right: 30px;
}



/* Slick Core */

.slick-slider
{
    position: relative;

    display: block;
    box-sizing: border-box;

    -webkit-user-select: none;
       -moz-user-select: none;
        -ms-user-select: none;
            user-select: none;

    -webkit-touch-callout: none;
    -khtml-user-select: none;
    -ms-touch-action: pan-y;
        touch-action: pan-y;
    -webkit-tap-highlight-color: transparent;
}

.slick-list
{
    position: relative;

    display: block;
    overflow: hidden;

    margin: 0;
    padding: 0;
}
.slick-list:focus
{
    outline: none;
}
.slick-list.dragging
{
    cursor: pointer;
    cursor: hand;
}

.slick-slider .slick-track,
.slick-slider .slick-list
{
    -webkit-transform: translate3d(0, 0, 0);
       -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
         -o-transform: translate3d(0, 0, 0);
            transform: translate3d(0, 0, 0);
}

.slick-track
{
    position: relative;
    top: 0;
    left: 0;

    display: block;
    margin-left: auto;
    margin-right: auto;
}
.slick-track:before,
.slick-track:after
{
    display: table;

    content: '';
}
.slick-track:after
{
    clear: both;
}
.slick-loading .slick-track
{
    visibility: hidden;
}

.slick-slide
{
    display: none;
    float: left;

    height: 100%;
    min-height: 1px;
}
[dir='rtl'] .slick-slide
{
    float: right;
}
.slick-slide img
{
    display: block;
}
.slick-slide.slick-loading img
{
    display: none;
}
.slick-slide.dragging img
{
    pointer-events: none;
}
.slick-initialized .slick-slide
{
    display: block;
}
.slick-loading .slick-slide
{
    visibility: hidden;
}
.slick-vertical .slick-slide
{
    display: block;

    height: auto;

    border: 1px solid transparent;
}
.slick-arrow.slick-hidden {
    display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.9.0/slick.min.js"></script>
<div class="carousel">

  <div class="carousel__bg">
    <img src="" />
    <video autoplay muted loop></video>
  </div>

  <div class="slick-carousel">

    <div class="carousel__item" data-src="https://www.fillmurray.com/750/550">
      <div class="carousel__content">
        <h4 class="carousel__title">Behind The Scenes</h4>
        <span class="carousel__flag">// Featured</span>
        <h2 class="carousel__subtitle">Lorem ipsum dolor</h2>
        <a href="#" class="carousel__btn">Read the Article</a>
      </div>
      <img src="https://www.fillmurray.com/g/400/600" class="carousel__image" />
    </div>

    <div class="carousel__item" data-src="https://www.fillmurray.com/800/600">
      <div class="carousel__content">
        <h4 class="carousel__title">Reed Stark</h4>
        <span class="carousel__flag">// Featured</span>
        <h2 class="carousel__subtitle">Lorem ipsum dolor</h2>
        <a href="#" class="carousel__btn">Watch the Video</a>
      </div>
      <img src="https://www.fillmurray.com/g/450/650" class="carousel__image" />
    </div>

    <div class="carousel__item" data-src='[ "https://www.w3schools.com/tags/movie.mp4", "https://www.w3schools.com/tags/movie.ogg"]'>
      <div class="carousel__content">
        <h4 class="carousel__title">Fresh Drops</h4>
        <span class="carousel__flag">// Featured</span>
        <h2 class="carousel__subtitle">Lorem ipsum dolor</h2>
        <a href="#" class="carousel__btn">See The Collection</a>
      </div>
      <img src="https://www.fillmurray.com/g/350/550" class="carousel__image" />
    </div>
    
    <div class="carousel__item" data-src='[ "https://www.w3schools.com/tags/movie.mp4", "https://www.w3schools.com/tags/movie.ogg"]'>
      <div class="carousel__content">
        <h4 class="carousel__title">Fresh Drops</h4>
        <span class="carousel__flag">// Featured</span>
        <h2 class="carousel__subtitle">Lorem ipsum dolor</h2>
        <a href="#" class="carousel__btn">See The Collection</a>
      </div>
      <img src="https://www.fillmurray.com/g/300/500" class="carousel__image" />
    </div>

  </div>

</div>
Trobol
  • 1,210
  • 9
  • 12
  • This is great, thanks a lot! I actually fixed the content disappearing when prev/next links were hovered over by changing the class the transition was on in the CSS (`.carousel:hover` to `slick-carousel:hover`) which seemed to do the trick. Maybe a bit of a jump when you move from nav link to carousel and off again quickly but not sure there's much to do about that. What do you think about the `img` and `video` elements being on the page without content (onload) ...is that acceptable/valid? – user1406440 Mar 17 '20 at 18:08
  • 1
    You probably want to use `src="//:0"` like mentioned [here](https://stackoverflow.com/a/5775621/8781351) – Trobol Mar 17 '20 at 18:39
  • That's an interesting, simple fix ha! I've added it but still see the broken image graphic ...maybe I just need to add a transparent small image like another suggestion in that thread. Or I suppose I could add the first item in the carousel? – user1406440 Mar 19 '20 at 11:59
  • Any of those would most likely work, also you may want to add prefetch links to speed up loading – Trobol Mar 19 '20 at 12:33
  • I suppose that'd be fine, as long as the items in the carousel (portrait images) were loaded first :) – user1406440 Mar 20 '20 at 09:30