Search code examples
javascriptjqueryfilteringundefinedjquery-isotope

Isotope custom function can't access HTML attribut


I'm in the process of creating a website to manage my board games club and I'm currently working on a page displaying all the games we have available for our members.

On this page, there's a section with filter buttons which, when clicked, will display or hide games according to the user's choice.

Currently, there are buttons for game types, buttons for game languages, and I'm working on buttons for filtering by minimum or maximum number of players.

I'm using Isotope to process the filters and, in the documentation, if you want to use a custom function, you can create it as a Javascript object.

Here's my current Javascript :

$(window).on('load', function(){

    var filtersFunctions = {
        minplayers: function(){
            var itemValue = $(this).attr('data-minplayers');
            var filterValue = $('.isotope-filter-minplayers.active').attr('data-players');

            return parseInt(itemValue, 10) >= parseInt(filterValue, 10);
        },
        maxplayers: function(){
            var itemValue = $(this).attr('data-maxplayers');
            var filterValue = $('.isotope-filter-maxplayers.active').attr('data-players');

            return parseInt(itemValue, 10) <= parseInt(filterValue, 10);
        }
    };

    var filters = {};

    var $grid = $('.isotope-grid').isotope({
        itemSelector: '.isotope-card',
        layoutMode: 'masonry'
    });

    $('#isotope-filters').on('click', '.isotope-filter', function(){

        var $this = $(this);
        var $buttonGroup = $this.parents('.isotope-group');
        var filterGroup = $buttonGroup.attr('data-filter-group');

        if($this.hasClass('active')){
            delete filters[filterGroup];
            $this.removeClass('active');
        }
        else{
            $buttonGroup.find('.isotope-filter').removeClass('active');
            $this.addClass('active');
            filters[filterGroup] = $this.attr('data-filter');
        }

        var filterValue = filtersFunctions[filters[filterGroup]] || concatValues(filters);

        $grid.isotope({filter: filterValue});
    });
});

function concatValues(obj){

    var value = '';

    for(var prop in obj){
        value += obj[prop];
    }

    return value;
}

Now here's my issue.

When a button is clicked, the filtering is done properly and if I click on one of the minimum or maximum player filter buttons, I know that the custom function (minplayers: function(){} or maxplayers: function(){}) is called and used.

And that's where the problem lies. Although the function is called, it seems impossible for my code to retrieve the value of the HTML attribute ($(this).attr('data-minplayers') or $(this).attr('data-maxplayers')) that lets me know what the minimum or maximum player value is for a game.

Each time I execute my function and console.log(itemValue), the value showed to me in the console is undefined.

I used the Isotope documentation and even tried to use their example but it never worked out for me. I had to do it all from scratch.

Here is an example of filter buttons in my view :

<div id="isotope-filters">
    <div class="card-group shadow">
        <div class="card">
            <div class="card-body isotope-group" data-filter-group="types">
                <h5 class="card-title">Types de jeux</h5>
                <button type="button" class="btn btn-outline-primary btn-sm isotope-filter" data-filter=".type-null">
                    Jeux sans types
                </button>
                <button type="button" class="btn btn-outline-primary btn-sm isotope-filter" data-filter=".type-1">
                    Type 1
                </button>
                <button type="button" class="btn btn-outline-primary btn-sm isotope-filter" data-filter=".type-2">
                    Type 2
                </button>
                <button type="button" class="btn btn-outline-primary btn-sm isotope-filter" data-filter=".type-3">
                    Type 3
                </button>
            </div>
        </div>
        <div class="card">
            <div class="card-body isotope-group" data-filter-group="langs">
                <h5 class="card-title">Langues</h5>
                <button type="button" class="btn btn-outline-info btn-sm isotope-filter" data-filter=".lang-fr">
                    FR
                </button>
                <button type="button" class="btn btn-outline-info btn-sm isotope-filter" data-filter=".lang-en">
                    EN
                </button>
                <button type="button" class="btn btn-outline-info btn-sm isotope-filter" data-filter=".lang-pt">
                    PT
                </button>
            </div>
        </div>
        <div class="card">
            <div class="card-body isotope-group" data-filter-group="minplayers">
                <h5 class="card-title">Joueurs min.</h5>
                <button type="button" class="btn btn-outline-warning btn-sm isotope-filter isotope-filter-minplayers" data-filter="minplayers" data-players="1">
                    1
                </button>
                <button type="button" class="btn btn-outline-warning btn-sm isotope-filter isotope-filter-minplayers" data-filter="minplayers" data-players="2">
                    2
                </button>
                <button type="button" class="btn btn-outline-warning btn-sm isotope-filter isotope-filter-minplayers" data-filter="minplayers" data-players="3">
                    3
                </button>
            </div>
        </div>
        <div class="card">
            <div class="card-body isotope-group" data-filter-group="maxplayers">
                <h5 class="card-title">Joueurs max.</h5>
                <button type="button" class="btn btn-outline-danger btn-sm isotope-filter isotope-filter-maxplayers" data-filter="maxplayers" data-players="4">
                    4
                </button>
                <button type="button" class="btn btn-outline-danger btn-sm isotope-filter isotope-filter-maxplayers" data-filter="maxplayers" data-players="5">
                    5
                </button>
                <button type="button" class="btn btn-outline-danger btn-sm isotope-filter isotope-filter-maxplayers" data-filter="maxplayers" data-players="6">
                    6
                </button>
            </div>
        </div>
    </div>
</div>

And here is an example of my game cards :

<div class="row isotope-grid">
    <div class="isotope-card lang-fr type-1" data-minplayers="2" data-maxplayers="5">
        <div class="card h-100 mb-3 shadow">
            <img src="https://picsum.photos/500" class="card-img rounded-bottom-0 object-fit-cover" alt="Adélaïde Allain-Hoarau">
            <div class="card-body">
                <h5 class="card-title">GAME TITLE</h5>
                <p class="card-text">GAME DESC</p>
                <div class="card-text mb-2">
                    <div class="btn-group btn-group-sm">
                        <a href="http://lbl.test/games/1" class="btn btn-success">Détails</a>
                        <a href="http://lbl.test/games/1" target="_blank" class="btn btn-success">
                            <i class="fa-solid fa-arrow-up-right-from-square"></i>
                        </a>
                    </div>
                </div>
            </div>
            <div class="card-footer">
                <small class="text-body-secondary">Ajouté le 06/06/2023 à 10:31</small><br>
                <small class="text-body-secondary">Modifié le 06/06/2023 à 10:31</small>
            </div>
        </div>
    </div>
    <div class="isotope-card lang-en type-2" data-minplayers="3" data-maxplayers="4">
        <div class="card h-100 mb-3 shadow">
            <img src="https://picsum.photos/500" class="card-img rounded-bottom-0 object-fit-cover" alt="Adélaïde Allain-Hoarau">
            <div class="card-body">
                <h5 class="card-title">GAME TITLE</h5>
                <p class="card-text">GAME DESC</p>
                <div class="card-text mb-2">
                    <div class="btn-group btn-group-sm">
                        <a href="http://lbl.test/games/1" class="btn btn-success">Détails</a>
                        <a href="http://lbl.test/games/1" target="_blank" class="btn btn-success">
                            <i class="fa-solid fa-arrow-up-right-from-square"></i>
                        </a>
                    </div>
                </div>
            </div>
            <div class="card-footer">
                <small class="text-body-secondary">Ajouté le 06/06/2023 à 10:31</small><br>
                <small class="text-body-secondary">Modifié le 06/06/2023 à 10:31</small>
            </div>
        </div>
    </div>
</div>

What do I have to do so that my functions can use the HTML attributes in the game cards when it's executed ?

Thank you for your time and patience. I hope I've made myself clear and understandable enough.

Best regards,

SatanicGeek

  • Laravel 10.8
  • Bootstrap 5.3
  • jQuery 3.7.0
  • Isotope-layout 3.0.6
  • Masonry-layout 4.2.2

Solution

  • Thank you Pointy, your solution is working. I had to pass the element as an argument.

    Here's the new code :

    var filtersFunctions = {
            minplayers: function(itemElem){
    
                // Valeur du jeu
                var itemValue = $(itemElem).attr('data-minplayers');
    
                // Valeur du filtre
                var filterValue = $('.isotope-filter-minplayers.active').attr('data-players');
    
                // Retourne le résultat du test
                return parseInt(itemValue, 10) >= parseInt(filterValue, 10);
            },
            maxplayers: function(itemElem){
    
                // Valeur du jeu
                var itemValue = $(itemElem).attr('data-maxplayers');
    
                // Valeur du filtre
                var filterValue = $('.isotope-filter-maxplayers.active').attr('data-players');
    
                // Retourne le résultat du test
                return parseInt(itemValue, 10) <= parseInt(filterValue, 10);
            }
        };
    

    It is maybe the way I include jQuery and Isotope using ViteJS which is bundled with Laravel.