Search code examples
javascriptcsslaravelturbolinks

Turbolinks prohibiting Javascript appended class for background images


I am integrating a front end html theme with a Laravel app and I am running into an issue with turbolinks not allowing Javascript to append div classes. This is causing the background images to only be displayed on refresh.

<div class="intro-banner" data-background-image="/storage/images/hero.jpg">
    <div class="container">

custom.js

/*----------------------------------------------------*/
/*  Inline CSS replacement for backgrounds
/*----------------------------------------------------*/
function inlineBG() {

    // Common Inline CSS
    $(".single-page-header, .intro-banner").each(function() {
        var attrImageBG = $(this).attr('data-background-image');

        if(attrImageBG !== undefined) {
            $(this).append('<div class="background-image-container"></div>');
            $('.background-image-container').css('background-image', 'url('+attrImageBG+')');
        }
    });

} inlineBG();

// Fix for intro banner with label
$(".intro-search-field").each(function() {
    var bannerLabel = $(this).children("label").length;
    if ( bannerLabel > 0 ){
        $(this).addClass("with-label");
    }
});

// Photo Boxes
$(".photo-box, .photo-section, .video-container").each(function() {
    var photoBox = $(this);
    var photoBoxBG = $(this).attr('data-background-image');

    if(photoBox !== undefined) {
        $(this).css('background-image', 'url('+photoBoxBG+')');
    }
});

Solution

  • It looks like this code is only run once: on the initial page load. To get it working for every page load, you will need to run it on turbolinks:load. As the script also appends elements to the page, you need to be careful that you don't end up with unnecessary duplicate elements. Turbolinks stores a copy of the page in its final state before a visitor navigates away. This cached copy will include any appended HTML. So be sure your code checks for the presence of the appended elements before appending, or remove the elements before they are cached.

    The following takes the latter approach, by removing elements on turbolinks:before-cache:

    /*----------------------------------------------------*/
    /*  Inline CSS replacement for backgrounds
    /*----------------------------------------------------*/
    
    $(document).on('turbolinks:load', function () {
        $(".single-page-header, .intro-banner").each(function() {
            var attrImageBG = $(this).attr('data-background-image');
    
            if(attrImageBG !== undefined) {
                $(this).append('<div class="background-image-container"></div>');
                $('.background-image-container').css('background-image', 'url('+attrImageBG+')');
            }
        });
    
        // Fix for intro banner with label
        $(".intro-search-field").addClass(function () {
            if ($(this).children("label").length) return "with-label";
        });
    
        // Photo Boxes
        $(".photo-box, .photo-section, .video-container").css('background-image', function () {
            return 'url('+$(this).attr('data-background-image')+')'
        })
    });
    
    $(document).on('turbolinks:before-cache', function () {
        $(".single-page-header, .intro-banner").each(function() {
            $(this).children(".background-image-container").remove();
        });
    });
    

    I have also tidied up some of the jQuery code. Many jQuery functions accept functions as arguments, which simplifies things somewhat, and removes the need to iterate over a jquery selection with each.

    Finally, wrapping lots of snippets in $(document).on('turbolinks:load', function () {…} is not great practice as creates a dependency on Turbolinks, and if you ever decided to move to something else, you have to update every place where this is called. If you're feeling adventurous, you may want to create a mini-framework like the one I create here: https://stackoverflow.com/a/44057187/783009