How to target “hidden” iframes? (application: link

2019-08-22 22:57发布

问题:

Meta: A similar question about locally stored A/V files can be found here: Clickable "positioning" hyperlinks to A/V (locally stored on your website and “hidden” behind a poster image).


Dear people from the Stackoverflow community,

The application

I am having an iframe <iframe name="video"... which is named video, and which is to be seen as the "main player" of a certain video.

Since I haven't been able to get interactive transcript on this video yet, I call different playing/starting positions in the video using: <a href="//www.youtube.com/embed/...&start=60" target="video">1:00</a>, e.g. for second 60.

This is working fine when the <iframe name="video".. is already "active": then the link shifts the video's playing position inside the iframe. This is great!

However it is not working fine, when the <iframe name="video".. isn't "active" yet, which is the case: then the link then opens in a different browser tab, instead of inside the iframe (or where the iframe is supposed to show up).

What I mean by hidden

What I mean with the iframe not being "active" is the following: it is "hidden" behind a "poster image" via the following code:

<div onclick="play();" id="vid" style="...; background: ... url('...poster.image.url...') no-repeat center;-webkit-background-size: cover; ...;overflow:hidden"></div>

<script type="text/javascript">function play(){document.getElementById('vid').innerHTML = '<iframe name="video" ... src="//www.youtube.com/embed/...?&...start=0"></iframe>';}</script>


In other words: i specifically don't want "<a target="_blank""-behaviour. I guess the target="video" is not working properly now, since the iframe is "hidden" behind the poster image.

I know for sure this behavior isn't occuring when the iframe wouldn't be hidden at all. I tested this multiple times. Further more, with the current "hidden" poster feature, this behavior is also not occuring when the poster image is clicked FIRST (before clicking on a <a href="...></a>).

If you would to see this behaviour for yourself, you can see it on my site. The best is to look/CTRL-F for "stef", and open the ▾̲ ̲u̲n̲d̲e̲r̲l̲i̲n̲e̲d̲ ̲t̲o̲g̲g̲l̲e̲, which you will find there.

So how to sucessfully "target" the "hidden" iframe without opening a new browser window/tab?

Any help would be greatly appreciated. Many thanks, Vincent Verheyen.

回答1:

Well here we are! The fiddle.

HTML

Your html will contain both facades and jumpers to accomplish what you asked.

    What's a facade?

facade: "the principal front of a building, that faces on to a street or open space."

This will be the image shown before you play the video or click on any jumpers, it's html will look like this:

<div class="videoFacade" data-name="video1" data-video="ByJFdTFEwF4" data-start="8">
    <img src="http://i.imgur.com/xeUiWGn.png" />
</div>

    What's a jumper?

A jumper is an anchor that will update the iframe's url to the desired time in the video, it will look like this:

JavaScript

window.addEventListener("load", initVideoFacade);

function initVideoFacade() {
    var allFacades = document.querySelectorAll(".videoFacade");
    for (var i = 0; i < allFacades.length; i++) {
        var facade = allFacades[i];
        setUpFacade(facade);
    }

    var allJumpers = document.querySelectorAll(".videoJumper");
    for (var i = 0; i < allJumpers.length; i++) {
        var jumper = allJumpers[i];
        setUpJumper(jumper);
    }
}

function setUpJumper(jumper) {
    jumper.addEventListener("click", function (e) {
        e.preventDefault();
        jumpTo(jumper);
        var video = jumper.dataset.video;
        var facade = getFacadeByVideo(video);
        if (facade) playVideo(facade);
        return false;
    });
}

function setUpFacade(facade) {
    facade.addEventListener("click", function () {
        playVideo(facade);
    });
}

function getFacadeByVideo(video) {
    return document.querySelector(".videoFacade[data-name=" + video + "]");
}

function getIframeByVideo(video) {
    return document.querySelector(".videoIframe[data-name=" + video + "]");
}

function updateVideoSource(iframe, start, end) {
    var iframeSrc = iframe.src.replace(/start=[0-9]+/i, "start=" + start);
    var hasEnd = iframeSrc.indexOf("end") != -1;
    if (hasEnd) iframeSrc = iframeSrc.replace(/end=[0-9]+/i, "end=" + end);
    else iframeSrc += "&end=" + end;
    return iframeSrc;
}

function updateFacadeData(facade, start, end) {
    facade.setAttribute("data-start", start);
    facade.setAttribute("data-end", end);
}

function jumpTo(jumper) {
    var start = jumper.dataset.start;
    var end = jumper.dataset.end;
    var video = jumper.dataset.video;
    var iframe = getIframeByVideo(video);
    if (iframe) {
        var iframeSrc = updateVideoSource(iframe, start, end);
        iframe.src = iframeSrc;
    } else {
        var facade = getFacadeByVideo(video);
        updateFacadeData(facade, start, end);
    }
}

function playVideo(facade) {
    var start = facade.dataset.start || 0;
    var end = facade.dataset.end;
    var name = facade.dataset.name;
    var video = facade.dataset.video;
    var iframe = document.createElement("iframe");
    iframe.dataset.name = name;
    iframe.className = "videoIframe";
    var iframeSrc = "http://www.youtube.com/embed/" + video + "?&cc_load_policy=1&modestbranding=1&autoplay=1&rel=0&showinfo=0&theme=light&start=" + start;
    if (end) iframeSrc += "&end=" + end;
    iframe.src = iframeSrc;
    iframe.frameBorder = 0;
    replaceNode(facade, iframe);
}

function replaceNode(node1, node2) {
    var parent = node1.parentNode;
    var next = node1.nextSibling;
    parent.insertBefore(node2, next);
    parent.removeChild(node1);
}

Here is a timeline:

  • We add the initVideoFacade() method to the load event in the page, this will make sure all our facades and jumpers are up and running before doing anything.

  • The initVideoFacade() method will find all jumpers and facades and set them up using the setUpFacade() and setUpJumper() methods.

  • The setUpJumper() method will add a click event on the jumper and tell it to jump to a determined time in the video, specified in the jumper. Also, if the video is not yet playing, it will do so now.

  • The jumpTo() method will update the iframe's src (or the facade initial data if the video is not playing) using a couple of regular expressions to replace the &start= and &end= parts of the iframe src.

  • The setUpFacade() method will simply play the video, removing the facade and inserting the iframe.

  • The playVideo() method will create a new iframe from a facade, replacing it and assigning it's source, start and end time to the video.

CSS

This just handles the styling of the facade and iframe :)

.videoFacade, .videoIframe {
    position: relative;
    width: 360px;
    height: 202.5px;
    margin:5px;
}
.videoFacade {
    cursor: pointer;
    border:1px solid black;
}
.videoFacade img {
    position: absolute;
    width: 50px;
    height: 50px;
    left: 50%;
    top: 50%;
    margin: -25px 0 0 -25px;
}

Hope it helped!