Skip to main content
April 1, 2026Dan Rodney/10 min read

Photo Filter Website: Creating an Exclusive Filter

Master Advanced JavaScript Photo Filtering Techniques

Key JavaScript Concepts in This Tutorial

Inclusive vs Exclusive Filtering

Learn the difference between additive filters that show images matching any criteria versus exclusive filters requiring all criteria to match.

Dynamic DOM Manipulation

Master techniques for toggling element visibility and managing user interface states through JavaScript event handling.

Query Selector Chaining

Understand how to build complex CSS selector strings dynamically to target specific combinations of elements.

Topics Covered in This JavaScript & jQuery Tutorial:

This comprehensive tutorial guides you through implementing sophisticated photo gallery filtering: adding a checkbox to toggle exclusive filtering on/off, separating inclusive and exclusive filter logic, writing conditionals to select the appropriate filter dynamically, differentiating exclusive filter functionality, and rerunning filters when the checkbox is toggled. These techniques form the foundation of modern interactive web applications.

Exercise Preview

ex prev gallery exclusive filter

Inclusive vs Exclusive Filter Behavior

FeatureInclusive FilterExclusive Filter
Selection LogicShows images matching ANY selected categoryShows images matching ALL selected categories
Query MethodSeparate queries: '.animals', '.buildings'Chained query: '.animals.buildings'
Result CountMore results (additive)Fewer results (restrictive)
Recommended: Use inclusive for broader discovery, exclusive for precise filtering

Exercise Overview

In the previous exercise, we programmed the ability to filter photos on a gallery webpage. The filters we created were additive/inclusive so that when applying multiple filters, they showed photos that matched any of the parameters—a logical OR operation that casts a wide net.

In this exercise, we'll learn how to build an exclusive filter feature that will allow a user to show images only if they match all chosen parameters—essentially a logical AND operation. This dual-filtering approach mirrors the sophisticated search capabilities users expect from modern web applications like e-commerce platforms, content management systems, and digital asset libraries. Mastering both inclusive and exclusive filtering patterns will make you a more versatile developer.

Getting Started

Before diving into the code, let's establish our development environment and examine the existing inclusive filtering functionality.

  1. Open your code editor if it isn't already open.

  2. Close any files you may have open to maintain focus on this exercise.

  3. For this exercise we'll be working with the Photo-Filter-Exclusive folder located in Desktop > Class Files > yourname-JavaScript jQuery Class. If your code editor supports project folders (like Visual Studio Code, Sublime Text, or Atom), open the entire folder to get better file navigation and autocomplete functionality.

  4. Open index.html from the Photo-Filter-Exclusive folder.

  5. Preview index.html in Chrome (we'll be leveraging Chrome's DevTools throughout this tutorial for debugging and testing).

  6. Click the category links in the navigation at the top to see how the inclusive filtering function selects photos that match any of the selected categories. Notice how selecting multiple categories expands the results rather than narrowing them.

  7. Leave the page open in Chrome so we can return to it for testing as we build our new functionality.

Setup Requirements

0/3

Adding a Checkbox to Toggle Exclusive Filtering On/Off

We'll implement an exclusive filtering option through a checkbox interface. This approach gives users intuitive control over filter behavior—a pattern that's become standard in modern web applications where users need both broad discovery (inclusive) and precise targeting (exclusive) capabilities.

  1. To streamline development, we've provided the HTML markup for the checkbox. Navigate to Photo-Filter-Exclusive > snippets and open checkbox-toggle.txt.

  2. Examine the code structure: the checkbox includes a semantic ID, properly associated label element, and descriptive text—all wrapped in a container div. This follows accessibility best practices and provides clear user interaction cues. The corresponding CSS styling has already been implemented.

  3. Select all the code, copy it, and close the file.

  4. Back in index.html, paste the code before the </header> tag (around line 22). This placement ensures the toggle remains visually connected to the navigation controls:

    </nav>
       <div class="checkbox-toggle">
          <input id="exclusive" name="exclusive" type="checkbox">
          <label for="exclusive">image must include all selected categories</label>
       </div>
    </header>
    <div class="gallery">
  5. Save the file and reload index.html in Chrome to see the new checkbox positioned between the navigation and the photo gallery.

Code Reuse Strategy

The tutorial provides pre-written HTML and CSS for the checkbox toggle to save development time. This approach focuses learning on the JavaScript logic rather than markup creation.

Adding the Toggle Checkbox

1

Copy Provided HTML

Get the checkbox code from snippets/checkbox-toggle.txt file

2

Insert Before Header Close

Paste the code before the closing header tag around line 22

3

Test in Browser

Save and reload to see the new checkbox under navigation

Separating the Inclusive & Exclusive Filters

Now we'll refactor our existing monolithic filter function into discrete, purpose-built functions. This separation of concerns makes our code more maintainable and follows the single responsibility principle—each function handles one specific filtering approach.

  1. Back in your code editor, locate the filterPhotos() function that starts around line 212, and cut the var group; statement along with any surrounding whitespace.

  2. Around line 212, above the existing filterPhotos() function, create the following new functions that will house our separated filtering logic. Paste the variable declaration into both functions:

    function filterInclusive() {
       var group;
    }
    
    function filterExclusive() {
       var group;
    }
    
    function filterPhotos() {
  3. Since we already implemented the inclusive filter logic in the previous exercise, we'll migrate that code into our new dedicated function. Around lines 222–227, locate and select the for loop shown below:

    for(var i = 0; i < selectedArray.length; i++) {
       group = document.querySelectorAll('.' + selectedArray[i]);
       for(var j = 0; j < group.length; j++) {
          group[j].style.display = 'inline-block';
       }
    }
  4. This nested loop structure handles the inclusive filtering logic—it processes each selected category individually and displays all matching images. Cut this code block to relocate it.

  5. Paste the code inside the filterInclusive() function as shown:

    function filterInclusive() {
       var group;
       for(var i = 0; i < selectedArray.length; i++) {
          group = document.querySelectorAll('.' + selectedArray[i]);
          for(var j = 0; j < group.length; j++) {
             group[j].style.display = 'inline-block';
          }
       }
    }
Function Separation Pattern

Breaking the filtering logic into separate functions (filterInclusive and filterExclusive) follows the single responsibility principle, making code more maintainable and testable.

Writing a Conditional to Select the Appropriate Filter

Currently the filterPhotos() function only manages basic show/hide states. We need to implement intelligent routing logic that determines which filtering approach to use based on user preference. This conditional logic forms the heart of our dual-filter system.

  1. The checkbox has an ID of exclusive, so we'll create a reference to it for programmatic access. Around line 159, add the following code to your variable declarations section:

    var imageContainers = document.querySelectorAll('.gallery div');
    var exclusive = document.getElementById('exclusive');
    
    // functions
  2. Save the file to ensure our changes are preserved.

  3. Navigate to index.html in Chrome and reload the page.

  4. To write an effective conditional, we need to understand the checkbox's state properties. We'll use Chrome's JavaScript Console to inspect the checkbox behavior. Open the Console by pressing Cmd–Opt–J (Mac) or Ctrl–Shift–J (Windows).

  5. Type the following command and press Return (Mac) or Enter (Windows) to examine the exclusive variable's properties:

    console.dir(exclusive);
  6. Click the arrow next to input#exclusive to expand the object properties.

  7. Scroll down to locate the checked property and note that it currently shows false.

  8. Clear the Console output by pressing Cmd–K (Mac) or Ctrl–L (Windows).

  9. In the webpage, check the checkbox located under the navigation.

  10. Run console.dir(exclusive); again and press Return (Mac) or Enter (Windows).

  11. Expand input#exclusive and verify that checked now shows true.

  12. Now that we've confirmed exclusive.checked reliably returns boolean values, we can implement our conditional logic. Return to your code editor.

  13. Around line 230, add the following if statement to the filterPhotos() function:

    function filterPhotos() {
       hideAllPics();
       noFilterSelection();
       if(exclusive.checked) {
          filterExclusive();
       } else {
          filterInclusive();
       }
    }

    NOTE: We're using exclusive.checked as a shorthand boolean check. This is equivalent to writing exclusive.checked == true but follows JavaScript best practices for cleaner, more readable conditional statements.

Using Chrome DevTools for Debugging

1

Open JavaScript Console

Use Cmd-Opt-J (Mac) or CTRL-Shift-J (Windows) to access console

2

Inspect Checkbox State

Use console.dir(exclusive) to examine the checked property

3

Test State Changes

Toggle checkbox and re-examine to see true/false values

Shorthand Conditional Syntax

Instead of writing 'if(exclusive.checked == true)', JavaScript allows the shorthand 'if(exclusive.checked)' since the condition only fires when true.

Differentiating the Exclusive Filter

With our inclusive filter logic already established, we now need to implement the exclusive filter's unique approach. The key difference lies in how we construct our DOM queries—instead of separate queries for each category, we'll build a single compound query that requires all conditions to be met.

  1. Consider the difference in query approaches. For an inclusive filter selecting Animals and Buildings, we'd write:

    document.querySelectorAll('.animals');
    document.querySelectorAll('.buildings');

    This approach executes separate queries and combines results—showing any image that matches either category.

  2. In contrast, an exclusive filter requiring images that contain both Animals and Buildings uses a single chained query:

    document.querySelectorAll('.animals.buildings');

    This compound selector only matches elements that have both classes simultaneously—the essence of exclusive filtering. To build this dynamic query, we'll concatenate class names into a single string.

  3. Begin by adding a string variable to store our compound query. In the filterExclusive() function, add the following around line 225:

    function filterExclusive() {
       var group;
       var queryString = '';
    }
  4. Next, we'll iterate through the user's selected categories to build our compound query string. Add the loop structure:

    function filterExclusive() {
       var group;
       var queryString = '';
       for(var i = 0; i < selectedArray.length; i++) {
    
       }
    }
  5. Now we'll concatenate each selected category into our query string, building the compound selector:

    function filterExclusive() {
       var group;
       var queryString = '';
       for(var i = 0; i < selectedArray.length; i++) {
          queryString += '.' + selectedArray[i];
       }
    }

    NOTE: The += operator performs string concatenation, preserving existing content while appending new values.

  6. To verify our string-building logic works correctly, we'll add a temporary console log statement for testing:

    for(var i = 0; i < selectedArray.length; i++) {
          queryString += '.' + selectedArray[i];
       }
       console.log(queryString);
    }
  7. Save your changes and return to Chrome.

  8. Reload index.html and ensure the Console is open.

  9. Check the checkbox below the navigation to enable exclusive mode.

  10. Click the Animals button. The Console should display .animals.

  11. Click the Buildings button. The Console should now show .animals.buildings. Perfect! Our string concatenation is working correctly, building the compound selector we need for exclusive filtering.

Query Selector Approaches

FeatureInclusive MethodExclusive Method
Selector Formatdocument.querySelectorAll('.animals')document.querySelectorAll('.animals.buildings')
LogicSeparate queries for each categoryChained classes in single query
String BuildingNot requiredqueryString += '.' + selectedArray[i]
Recommended: Exclusive filtering requires building a concatenated selector string

Finishing the Exclusive Filter

Now we'll complete the exclusive filter by executing the compound query and displaying the matching results. This final step transforms our query string into actual DOM manipulation.

  1. Return to your code editor and remove the console.log() statement around line 229 since our testing confirmed the logic works correctly.

  2. Before executing our query, we should validate that the queryString contains selectors. Around line 229, add the following conditional logic:

    for(var i = 0; i < selectedArray.length; i++) {
          queryString += '.' + selectedArray[i];
       }
       if(queryString) {
          group = document.querySelectorAll(queryString);
          for(var j = 0; j < group.length; j++) {
             group[j].style.display = 'inline-block';
          }
       }
    }

    NOTE: The condition if(queryString) is a concise way to verify the string isn't empty. We use j as our loop variable in the nested loop for clarity, though we could reuse i since the first loop has completed execution.

  3. Save your changes and return to Chrome.

  4. Reload index.html and close the Console if it's open.

  5. Initially, all photos should be visible using the default inclusive filter. Click the Animals button to select that category.

  6. Check the checkbox to switch to exclusive filtering mode.

  7. To test exclusive filtering with multiple categories, click the Black & White button. You should see three photos that match both the Animals and Black & White criteria.

  8. Click the All button to add the fourth category. Notice how the results narrow to just one photo that matches all four selected categories—this demonstrates the power of exclusive filtering.

  9. Try unchecking the checkbox. You'll notice nothing happens immediately because we haven't yet programmed the checkbox to trigger a filter refresh when toggled. Let's address this final requirement.

Testing Results

When testing with Animals + Black & White + checkbox checked, you should see three results. Adding 'All' category should show just one photo that matches all four categories.

Rerunning the Filter When the Checkbox is Toggled

For a complete user experience, we need to ensure the filter updates immediately when users toggle between inclusive and exclusive modes. This requires adding an event handler to detect checkbox state changes and refresh the filter results accordingly.

  1. Return to your code editor.

  2. Locate the // active code comment around line 247, and add the following event handler:

    // active code
    exclusive.onchange = function() {
       populateArray();
       filterPhotos();
    }
    
    for(var i = 0; i < selectors.length; i++) {
       selectors[i].onclick = function() {
          toggleSelector(this);
          populateArray();
          filterPhotos();
       }
    }

    This event handler listens for changes to the checkbox state and immediately refreshes the current filter selection using the appropriate filtering mode.

  3. Save your changes and return to Chrome for final testing.

  4. Reload index.html. By default, all photos should display using inclusive filtering.

  5. Check the checkbox to switch to exclusive mode. The display should immediately update to show only images matching all currently selected categories.

  6. Uncheck the checkbox to return to inclusive filtering. The results should expand to show all images matching any selected category. Your dual-filter system is now complete and fully functional!

    NOTE: For reference, you can examine the complete implementation in Desktop > Class Files > yourname-JavaScript jQuery Class > Done-Files > Photo-Filter-Exclusive.

Event Handler Implementation

1

Add onchange Event

Attach event listener to checkbox for state changes

2

Call Filter Functions

Execute populateArray() and filterPhotos() on toggle

3

Test Toggle Behavior

Verify switching between inclusive and exclusive modes works

Reference Files Available

Complete working code examples are available in Desktop > Class Files > yourname-JavaScript jQuery Class > Done-Files > Photo-Filter-Exclusive for reference.

Key Takeaways

1Inclusive filters show images matching ANY selected criteria, while exclusive filters require ALL criteria to match
2Chrome DevTools Console is essential for debugging JavaScript state and examining object properties
3Query selector chaining (.class1.class2) enables targeting elements with multiple classes simultaneously
4Separating filtering logic into distinct functions improves code organization and maintainability
5Event handlers like onchange are crucial for creating responsive user interfaces that react to user input
6String concatenation with += operator allows dynamic building of complex CSS selector queries
7Conditional statements can use shorthand syntax (if(variable)) instead of explicit boolean comparison
8Testing filter combinations helps verify that exclusive logic correctly restricts results to exact matches

RELATED ARTICLES