3
\$\begingroup\$

Time ago I had a header implemented with a Bootstrap carousel: You know, something like this:

<div class="carousel">
    <div class="item"><img src="pic1.jpg"></div>
    <div class="item"><a href="example.com"><img src="pic2.jpg"></a></div>
</div>

Pay attention to that link on "pic2.jpg", this will be fun...

Recently, I have been asked to use <picture> elements, so now it is posible to display images for mobile devices:

<div class="item">
    <picture>
        <source media="(max-width: 576px)" srcset="pic1-small.jpg">
        <img src="pic1.jpg">
     </picture>
</div>

<div class="item">
    <picture>
        <source media="(max-width: 576px)" srcset="pic2-small.jpg">
        <img src="pic2.jpg">
     </picture>
</div>

And here is the tricky question: I need to add a link only in the mobile image (pic2-small.jpg).

(Well, in fact could be links on every image, or none at all, only in the mobile image, only in the desktop image... This is managed by the administrator user)

So, I can't do this because the link should be only for pic2-small, not pic2:

<div class="item">
    <a href="example.com">
        <picture>
            <source media="(max-width: 576px)" srcset="pic2-small.jpg">
            <img src="pic2.jpg">
         </picture>
    </a>
</div>

What I did is this:

<div class="item" onclick="header_onclick(this)">
    <picture>
        <source media="(max-width: 576px)" srcset="pic2-small.jpg?target=pic2-small-link">
        <img src="pic2.jpg">
     </picture>
     <a id="pic2-small-link" href="example.com" style="display: none"></a>
</div>

<script>
function header_onclick(carousel_item)
{
    var current_src = carousel_item.querySelector("img").currentSrc;                
    if (typeof current_src === "undefined") {
        return;
    }

    var src_parts = current_src.split("?target=");                
    if (src_parts.length < 2) {
        return;
    }
            
    var link = document.getElementById(src_parts[1]);                
    if (link) {
        link.click();
    }
}
</script>
  • An onclick event in the carousel item and a javascript function.
  • A target value in the URL of the image.
  • A hidden <a> with the destination URL.

The magic is in this line:

var current_src = carousel_item.querySelector("img").currentSrc;

currentSrc has the URL of the image that the browser is currently showing according to the screen size. So, from that URL I take the target parameter and use it to get the corresponding link and then execute it.

All this is working, but I feel it too hacky for my taste. I'm not very experienced with the <picture> element, so my question is: this could be resolved in a better way?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Yes hacky!

This is very hacky as you are doing a media query the long way. Interpreting the resulting media aware DOM for a URL that you then manually parse.

Learn and use the API

There are many API's available to a modern browser environment. The general rule is if it can be done on the page via HTML/CSS there is an API that can do it in JavaScript.

I never use media queries as I serve to interrogated devices so I am not 100% up on media querie API's, but for static pages you can use matchMedia

Setting up media dependent links.

There are many ways to setup your page. But in code you can access media queries using matchMedia which returns the result of a media query as MediaQueryList

You can access an elements media attribute by name. for example if an image is clicked in your example HTML event.target.parentElement.querySelector("source").media

You can store the different links as data attributes.

I think the best approach is to pre-process the markup when it has loaded and just set the link references depending on the associated media query.

Example

The code checks each link (anchor element) for a source element and uses the source elements media attribute to do a media query.

Depending on the result of the media query it gets the appropriate data set item and set the link href property.

Each picture has a different media query. Hover to see link href (I have set title to show the link as example)

// do in page onload event
for (const link of linkedImages.querySelectorAll("a")) {
    const source = link.querySelector("source");
    if (source) {
        const isSmall = matchMedia(source.media).matches;
        link.href = isSmall ? link.dataset.linkSmall : link.dataset.linkNormal;
        link.title = link.href; // just for example 
    }
}
    

    
<div class="item" id = "linkedImages">
    <a data-link-small="smallLink.com" data-link-normal="normalLink.com">
        <picture>
            <source media="(max-width:  1700px)" srcset="https://i.sstatic.net/C7qq2.png?s=48&g=1">
            <img class="images" src="https://i.sstatic.net/C7qq2.png?s=328&g=1">
        </picture>
    </a><br>
    <a data-link-small="smallLink.com" data-link-normal="normalLink.com">
        <picture>
            <source media="(max-width:  500px)" srcset="https://i.sstatic.net/C7qq2.png?s=48&g=1">
            <img class="images" src="https://i.sstatic.net/C7qq2.png?s=328&g=1" width="128">
         </picture>    
     </a>
</div>

\$\endgroup\$
3
  • \$\begingroup\$ By interrogated devices did you mean integrated devices? Apparently Device Interrogation is a concept for devices like pacemakers... \$\endgroup\$ Commented May 24, 2021 at 22:18
  • 1
    \$\begingroup\$ @SᴀᴍOnᴇᴌᴀ I meant interrogated as a substitute for device discovery on a landing page. \$\endgroup\$
    – Blindman67
    Commented May 25, 2021 at 9:59
  • \$\begingroup\$ This looks more like the kind of code I want! I wasn't aware of matchMedia(), I will investigate further and I will give it a try later, but at least now I have a path to follow. Thanks! \$\endgroup\$
    – CJ Nimes
    Commented May 25, 2021 at 13:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.