Issue with integrating Shuffle.js search functionality with Bootstrap 4 cards

For the past few days, I've been experimenting with integrating Shuffle.js into my cards within Bootstrap 4. My goal is to achieve a visually pleasing shuffling effect when searching/filtering these cards.

Below is the HTML structure and JavaScript code I am working with. You can also access the full code on JSFiddle.net.

class Card {
  constructor(ref) {
    this.hmi_ref = ref;

    // Bootstrap: container type
    this.BS = {}
    this.BS.container = document.createElement('div');
    this.BS.card = document.createElement('div');
    this.BS.image = document.createElement('img');
    this.BS.info = document.createElement('div');
    this.BS.title = document.createElement('h4');
    this.BS.link = document.createElement('a');

    this.BS.card.appendChild(this.BS.link);
    this.BS.link.appendChild(this.BS.image);
    this.BS.card.appendChild(this.BS.title);
    this.BS.container.appendChild(this.BS.card);

    this.BS.container.className = 'col-4 mb-3';
    this.BS.card.className = 'card h-100';
    this.BS.image.className = 'card-img-top';
    this.BS.title.className = 'card-title text-center align-middle';
  }

  add(name, image, page_link) {
    this.BS.image.src = image;
    this.BS.title.textContent = name;
    this.BS.link.href = page_link;
    let newNode = this.BS.container.cloneNode(true);
    this.hmi_ref.appendChild(newNode);
  }
}

let myCard = new Card( document.getElementById('card-space') );
[
    {title: 'Vacanza studio Londra', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Vacanza studio"},
    {title: 'Vacanza studio Roma', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Vacanza studio"},
    {title: 'Vacanza studio Bangkok', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Vacanza studio"},
    {title: 'Vacanza studio Catania', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Vacanze studio"},
    {title: 'Vacanza studio Siracusa', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Vacanza studio"},
    {title: 'Vacanza studio Ragusa', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Vacanza studio"},
    {title: 'Vacanza studio Trapani', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Vacanza studio"},
].map(e => myCard.add(e.title, e.img, e.link, e.category));

class Shuffler {
    constructor(element) {
        this.shuffle = new window.Shuffle(element, {
            itemSelector: '.card',
            sizer: element.querySelector('.sizer'),
        }); 
        document.getElementById('searchBox').addEventListener('keyup', this._handleSearchKeyup.bind(this));
    }

    /**
     * Filter the shuffle instance by items with a title that matches the search input.
     * @param {Event} evt Event object.
     */
    _handleSearchKeyup(evt) {
        const searchText = evt.target.value.toLowerCase();
        this.shuffle.filter(element => {
            console.log('filtering...');
            const titleText = element.querySelector('.card-title').textContent.toLowerCase().trim();
            return titleText.indexOf(searchText) !== -1;
        });
    }
}

window.onload = () => {
    window.demo = new Shuffler(document.querySelector('#card-space'));
}  
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">

<div class="container pt-3">
  <div class="row">
    <div class="col">
      <!-- Main column -->
      <div class="row pt-4">
        <div class="col-9">
          <div id="card-space" class="row h-100">
            <div class="col-1@sm sizer"></div>
          </div>
        </div>
        <div class="col-3">
          <div class="row">
            <form class="form-inline" action="javascript:void(0);">
              <div class="input-group">
                <div class="input-group-prepend">
                  <div class="input-group-text"><i class="fa fa-search" aria-hidden="true"></i></div>
                </div>
                <input id="searchBox" class="form-control" type="search" placeholder="Cerca" aria-label="Cerca">
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<script src="https://unpkg.com/shufflejs@5"></script>
<!-- jQuery first, then Popper.js and then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>

One specific area where I'm encountering issues is the following:

this.shuffle.filter(element => {
    const titleText = element.querySelector('.card-title').textContent.toLowerCase().trim();
    return titleText.indexOf(searchText) !== -1;
});

I'm struggling to debug inside this portion of the code. Does anyone have suggestions or ideas for resolving this problem? Tackling the complexities of Shuffle.js has proven challenging, but the smoothness of the desired end effect makes it worth the effort.

Answer №1

Check out this demonstration. What I've done is completely eliminate the grid structure and opted for Bootstrap's card-deck instead. The rationale behind this decision lies in how this library searches for the items array to filter on.

_getItems() {
    return Array.from(this.element.children)
        .filter(el => matches(el, this.options.itemSelector))
        .map(el => new ShuffleItem(el));
}

This means it specifically targets direct children that match your itemSelector. In your HTML layout, it selects all the columns but doesn't find any itemSelector classes on them.

Another crucial step was using the data-groups and/or data-title. I added it only for the title (name) but I assume you want to include separate groups as well. You can populate those from the category selector you've already made (though with only one option).

this.BS.card.setAttribute('data-title', name);
this.BS.card.setAttribute('data-groups', name);

With this solution, the filter is activated, the height is adjusted, and the final touch is ensuring the responsiveness of .card-deck, which I must say is fantastic (sorry for repeating myself).

Arranging multiple divs in CSS/JS?
Looping every 3 rows using Bootstrap card
How to add spacing between rows of a card-deck in bootstrap

Responsive card-deck CSS demonstration

Answer №2

You have the ability to sort through an Array and not through an Object. I utilize console.log for visibility.

class Card {
  constructor(ref) {
    this.hmi_ref = ref;

    // Bootstrap : container type
    this.BS = {}
    this.BS.container = document.createElement('div');
    this.BS.card      = document.createElement('div');
    this.BS.image     = document.createElement('img');
    this.BS.info      = document.createElement('div');
    this.BS.title     = document.createElement('h4');
    this.BS.link      = document.createElement('a');

    this.BS.card.appendChild(this.BS.link);
    this.BS.link.appendChild(this.BS.image);
    this.BS.card.appendChild(this.BS.title);
    this.BS.container.appendChild(this.BS.card);

    this.BS.container.className = 'col-4 mb-3';
    this.BS.card.className      = 'card h-100';
    this.BS.image.className     = 'card-img-top';
    this.BS.title.className     = 'card-title text-center align-middle';
  }

  add ( name, image, page_link){
    this.BS.image.src = image;
    this.BS.title.textContent = name;
    this.BS.link.href = page_link;
    let newNode = this.BS.container.cloneNode(true);
    this.hmi_ref.appendChild(newNode);
  }
}
      
let myCard = new Card( document.getElementById('card-space') );
[
    {title: 'Study Abroad London', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Study Abroad"},
    {title: 'Study Abroad Rome', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Study Abroad"},
    {title: 'Study Abroad Bangkok', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Study Abroad"},
    {title: 'Study Abroad Catania', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920x1080', category: "Study Abroad"},
    {title: 'Study Abroad Syracuse', img: 'https://source.unsplash.com/random/1920x1080', link: 'https://source.unsplash.com/random/1920xx`, category:` Study Abroad},
    {title: `Study Abroad Ragusa`, img: `https://source.unsplash.com/random/1920x1080`, link: `https://source.unsplash.com/random/1920x1080`, category: `Study Abroad`},
    {title: `Study Abroad Trapani`, img: `https://source.unsplash.com/random/1920x1080`, link: `https://source.unsplash.com/random/1920x1080`, category: `Study Abroad`}
].map(e => myCard.add(e.title, e.img, e.link, e.category));

class Shuffler {
    constructor(element) {
        this.shuffle = new window.Shuffle(element, {
            itemSelector: '.card',
            sizer: element.querySelector('.sizer'),
        }); 
        document.getElementById('searchBox').addEventListener('keyup', this._handleSearchKeyup.bind(this));
    }

    /**
     * Filter the shuffle instance by items with a title that matches the search input.
     * @param {Event} evt Event object.
     */
    _handleSearchKeyup(evt) {
        const searchText = evt.target.value.toLowerCase();
        Object.values(this.shuffle.element.children).filter(element => {
            const titleText = element.textContent.toLowerCase().trim();
            console.log(element.textContent.toLowerCase().trim(), titleText.indexOf(searchText));
            return titleText.indexOf(searchText) !== -1;
        });
    }
}

window.onload = () => {
    window.demo = new Shuffler(document.querySelector('#card-space'));
}   
</script>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
</head>

<body>
  <div class="container pt-3">
    <div class="row">
      <div class="col">
<!-- Main column -->
<div class="row pt-4">
<div class="col-9">
<div id="card-space" class="row">
<!-- <div class="col-1@sm sizer"></div> -->
</div>
</div>
<div class="col-3">
<div class="row">
<form class="form-inline" action="javascript:void(0);">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fa fa-search" aria-hidden="true"></i></div>
</div>
<input id="searchBox" class="form-control" type="search" placeholder="Search" aria-label="Search">
</div>
</form>
</div>
<div class="row">
<select class="custom-select my-3" id="eventCategories">
<option selected>Choose a category</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Shuffle/5.2.3/shuffle.min.js"></script>
<!-- jQuery first, then Popper.js and then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</body>

</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Troubleshooting problem with MongoDB queries within a for loop

I have an array of user emails obtained from the post data. My goal is to find the _id associated with each email. Here's the for loop I attempted: var studentIds = []; for (var i = studentEmails.length - 1; i >= 0; i--) { var email = studentEm ...

What are some ways to incorporate TypeScript into a function component to enforce type constraints and set default values?

I have a function component similar to the example provided. I am familiar with using PropTypes to define prop types and default values in React, but my team has transitioned to TypeScript. Can anyone help me understand how to achieve the same in TypeScrip ...

Is it possible to implement AngularJS Routing for displaying HTML pages within a slideshow?

I have a vision for my website in mind - a stunning slideshow with a sleek navigation bar at the top. The idea is to have each slide represent a different category or concept, all uniquely customized with their own HTML page. However, I'm facing a cha ...

The error message "Failed to load the default credentials in Firebase" occurs sporadically. Approximately 50% of the time, the app functions properly, while the other 50% of the time, this specific

Occasionally in my firebase functions log, I encounter an error message stating: "Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information." This error appears randomly withi ...

Unexpected format of _id is returned by Mongolian DeadBeef .toArray() method

My love for Mongolian deadbeef is strong, but I'm facing a challenge. I want the output of a simple .find() method to match the JSON format from Mongo's command line: $ db.mycollection.find(); # outputs.. # { ...some data... , "_id" : ObjectId(" ...

Differences in HTML animations can be seen when comparing Google Chrome to Microsoft Edge. Looking for a workaround for autoplay to ensure

My intro video animation is facing recording difficulties due to the autoplay policy in Google Chrome. It seems nearly impossible to capture it accurately. If only autoplay could function for an offline HTML file, my issue would be resolved. Unfortunately ...

Header Blocks that are Fixed While Scrolling in fullPage.js Sections Using the "scrollOverflow: true" Feature

I am experiencing an issue where a fixed header on my website prevents scrolling through sections when hovering, specifically when the setting scrollOverflow: true is enabled. However, everything works fine and scrolling through all sections is possible w ...

Is it possible to place a div and a heading side by side within the same line?

Is there a way to align a div and header on the same line? Currently, the red div is displaying below the text. Thank you for your help! .underlined { border-bottom: 1px dotted #000; text-decoration:none; } .block { height:15px; background-color: #ff505 ...

Display modal within a React list

I need to display a list of items with an edit button for each item, which should trigger a modal showing the details of that specific item. Initially, I had a single modal component in the parent element and passing the visible values to the parent state ...

Sending optional data in Angular routesIn Angular, you can include additional

In my project utilizing angular 5, I have a lengthy routing file: const homeRoutes: Routes = [ { path: 'home', component: HomeComponent, children: [ { path: 'registration', component: RegistrationCompone ...

Reconstructing the complete pathway using object identifiers

Imagine I have a set of website routes represented by the object below: const routes = { HOME: "start", ACCOUNT: { HOME: "account", PROFILE: "profile", ADDRESSES: { HOME: "addresses", DETA ...

What steps can be taken to restrict a user's access to the main page unless they are logged in?

I have created sign up and login pages using JavaScript, HTML, and PHP with a database. After a user successfully logs in on the login page, the following code is executed: sessionStorage.setItem('logged','loggedIn'); The user is then ...

The input does not provide a value for the variable [value]

Having a small issue with using [value] in my input fields. This is the code snippet from my component .html file: <div class="form-group"> <mat-form-field class="example-full-width" style="padding-left: 15px;"& ...

What advantages can be gained from having multiple package.json files within a single application?

Embarking on the journey of creating my inaugural full react web application entirely from scratch. Previously, I've mainly worked on assignments that were partially pre-made for me. While setting up my project, I couldn't help but notice that I ...

Employing the "div" element to target in CSS styling

I'm dealing with a document structure that is consistent across multiple pages. HTML: <ul> <li> <div> <img src="" /> </div> </li> </ul> Presently, there is a border aroun ...

Can saving data within a mongoose pre-save hook initiate a continuous loop?

Currently, I am developing an API for a forum system which includes functionalities for users, forums, and posts. In my MongoDB Database, there is a 'categories' collection where each category acts as a container for a group of forums. Each categ ...

AngularJS - Setting an initial delay for ng-bind

We have a span element with the following attributes: <span role="link" ng-show="showLink()" ng-bind="textLink"></span> (Just an fyi: we implemented a fade-in, fade-out animation for this link, hence the use of ng-show instead of ng-if) The ...

"Enhance your code editing experience in Eclipse with HTML, CSS, and JavaScript syntax

Hi there! I'm looking for a way to enable syntax highlighting for HTML/CSS/JS in Eclipse. My main focus is on developing in Python using the PyDev package, but currently I am working on creating Cheetah templates which are difficult to read without pr ...

Why might the Bootstrap menu be malfunctioning?

The reality is that Bootstrap is functional and widely used. Sharing the code could make a big difference in getting assistance! <ul class="top-header-contact-info secondary-menu"> <li class="nav-item dropdown-toggle"> ...

Avoid using fs.read without returning a value to console.log

Seeking assistance with parsing a text file named information.txt and displaying the values using console.log. Here is the code snippet: fs.readFileSync("information.txt", "utf-8", function (err, data) { ...