skip to Main Content

I am trying to make a search page with an advanced filtering feature. Directors, ratings and years should be displayed on a sidebar with no duplicates. When checking a box, the filtered results containing films should be displayed on the same page and then disappear when no boxes are checked.

For example you should be able to check the box ‘Francis Ford Coppola’ and get a list of the films which are directed by him(‘The Godfather’ and ‘The Outsiders’), or if you click the checkbox for rating 5 as well, you should only get ‘The Godfather’ etc.

I’ve never used ajax or JavaScript and have very limited experience with PHP so I am struggling to make anything work

This is an excerpt of the JSON data contained in the endpoint api.php?films, which contains 100s of films:

[
    {
        "film": "The Bridge on the River Kwai",
        "director": "David Lean",
        "rating": "2",
        "year": "1957"
    },
    {
        "film": "A Night To Remember",
        "director": "Roy Ward Baker",
        "rating": "2",
        "year": "1958"
    },
        "film": "The Outsiders",
        "director": "Francis Ford Coppola",
        "rating": "4",
        "year": "1983"
    },
    {
        "film": "Asylum",
        "director": "Roy Ward Baker",
        "rating": "3",
        "year": "1966"
    },
    {
        "film": "Trading Places",
        "director": "John Landis",
        "rating": "5",
        "year": "1983"
    },
    {
        "film": "The Godfather",
        "director": "Francis Ford Coppola",
        "rating": "5",
        "year": "1983"
    }
 
]

This is my code for displaying the years with checkboxes, but I need there to be no duplicates of the years (do I need a different GET request to only display unique values for year?). As for the JavaScript to filter and display the results, I really need help with that.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script
  src="https://code.jquery.com/jquery-3.6.0.min.js"
  integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
  crossorigin="anonymous"></script>
  
</head>
<body>

<h1>Filter films by year</h1>
<div id='items-container'>

</div>


<script>

$.ajax({
        url: "api.php",
        method: "GET",
        dataType: 'json',
        data: 'films',
        success: function(data) {
          console.log(typeof(data));
          var html_to_append = '';
          $.each(data, function(i, item) {
            html_to_append +=
            '<div class="col-3 mb-3"><p>' +
            item.year +
            '<input type="checkbox"></p></div>';
          });
          $("#items-container").html(html_to_append);
        },
        error: function() {
          console.log(data);
        }
      });



    </script>

</body>
</html>

Any help would be really appreciated as I’ve been trying to do this for some time.

2

Answers


  1. Year should not be a checkbox it should be a text box that the user can enter(four-character numeric).
    The API call should take parameters, like year(text box), rating(select), director(text box), etc to filter out movies instead of dumping the 100s of movies in one call.
    You can also do autocomplete for director and title, where the API retrieves only director or title when a user is filling a field.

    Login or Signup to reply.
  2. You could do the following to generate filter conditions for the criteria "director", "rating" and "year":

    const data=[{"film":"The Bridge on the River Kwai","director":"David Lean","rating":"2","year":"1957"},
                {"film":"A Night To Remember","director":"Roy Ward Baker","rating":"2","year": "1958"},
                {"film":"The Outsiders","director":"Francis Ford Coppola","rating":"4","year": "1983"},
                {"film":"Asylum","director":"Roy Ward Baker","rating":"3","year": "1966"},
                {"film":"Trading Places","director":"John Landis","rating":"5","year": "1983"},
                {"film":"The Godfather","director":"Francis Ford Coppola","rating":"5","year": "1983"}];
    
    // define globally: constants, variables and delegated event attachment:
    // ---------------------------------------------------------------------
    const filt=document.getElementById("filt"),
          list=document.querySelector("#list tbody");
    let cols={}, trs; // arrays for columns sequence in data and all table records
          
    // set up filtering (needs be done only once, before the AJAX call):
    filt.addEventListener("change",ev=>{let cb=ev.target;
      let tst=Object.entries([...filt.querySelectorAll(":checked")].reduce((a,c)=>((a[c.name]=a[c.name]||[]).push(c.value),a), {}));
      trs.forEach(tr=>
       tr.style.display=tst.length==0 || tst.every(([n,arr])=>arr.includes(tr.children[cols[n]].textContent)) 
         ?"":"none"
      )
    });      
          
    
    // put the following code into the "success" function of your AJAX call: 
    // ---------------------------------------------------------------------
    if (data.length) { // only if data array is not empty
     // columns sequence:
     Object.keys(data[0]).forEach((c,i)=>cols[c]=i);
     // build the filter-menu structure:
     const menu={director:{},rating:{},year:{}};
           data.forEach(f=>Object.entries(menu).forEach(([k,o])=>o[f[k]]=1));
    
     // translate the filter-menu structure into HTML:
     filt.innerHTML=Object.entries(menu).map(([k,o])=>k+':<br>'+
       Object.keys(o).map(c=>'<label><input type="checkbox" value="'+c+'" name="'+k+'">'+c+'</label>').join('<br>')
     ).join('<br>n');
    
     // fill the main film list with data entries:
     list.innerHTML=data.map(f=>'<tr><td>'+Object.values(f).join('</td><td>')+'</td></tr>').join('n');
     trs=[...list.children];
    }
    #filt {width: 200px; display:inline-block}
    #list {display:inline-block; vertical-align:top}
    th {text-align:left}
    tbody>tr:nth-child(odd) {background-color:#ddd}
    <div id="filt"></div>
    <div id="list"><table><thead><tr><th>Title</th><th>Director</th><th>Rating</th><th>Year</th></tr></thead>
    <tbody></tbody></table</tdiv>

    You might have noticed, putting this together took me a while and I did it in several instalments. As often in coding I tend to write stuff, look at it and quickly scrap it again. For me, a design needs to evolve fluidly, allowing for frequent rewrites until the "final" (there is no such thing 😁) layout resembles what I was looking for.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search