Search code examples
javascriptjqueryregexfilterpartial

Check for partial match in JavaScript if statement


I've got elements set up something like this:

<div class="cat" data-cat="example-cat, test-cat, test-category">
    ...
</div>
<div class="cat" data-cat="test-category">
    ...
</div>
<div class="cat">
    ...
    <div class="cat" data-cat="example-cat, test-category">
        ...
    </div>
    <div class="cat" data-cat="test-category, one-more-cat">
        ...
    </div>
</div>

Using JavaScript, I need to check each bit of text between commas for a match with a user selected value. For example, if the user selected "test-cat," I need to check each div to see if data-cat matches the selection. If it does, I need to add class="active" to each matching div.

Part of the trick is that if the user selects test-cat, a div with a data-cat of test-category should not return positive. Only exact matches should be consider matches.


I had already set up a complex filtering system with support for multiple filters, but the client wants to be able to set multiple categories per div, which is making this tricky. I have a script set up to show matches if the attribute is an exact match, and I'll be trying to modify this to work as I need it to:

$(document).ready(function() {
    var changedOnce = false;
    $("#filters select").change(function() {
        $(".cat").each(function() {
            $(this).attr("data-match", "true");
            $(this).removeClass("open");
        });
        $("#filters select").each(function() {
            var filter = $(this).attr("name");
            var value = $(this).val();
            $(".cat").each(function() {
                if ($(this).attr("data-match") === "false") {
                    return true;
                }
                var attr = $(this).attr("data-" + filter);
                var childAttr = $(this).find(".cat").attr("data-" + filter)
                if ((typeof attr !== typeof undefined && attr !== false) || (typeof childAttr !== typeof undefined && childAttr !== false)) {
                    if ($(this).attr("data-" + filter) === value || $(this).find(".cat").attr("data-" + filter) === value || value === "") {
                        $(this).attr("data-match", "true");
                        $(this).parents(".cat").attr("data-match", "true");
                    } else {
                        $(this).attr("data-match", "false");
                        return true;
                    }
                } else {
                    if (value !== "") {
                        $(this).attr("data-match", "false");
                        return true;
                    } else {
                        $(this).attr("data-match", "true");
                        $(this).parents(".cat").attr("data-match", "true");
                    }
                }
            });
        });
    });
});

My filters are set up something like:

<div id="filters">
    <select name="cat">
        <option value="test-cat">Test Cat</option>
        <option value="example-cat">Example Cat</option>
        ...
    </select>
    ...
    <select name="nth-filter">
        ...
    </select>
</div>

It's probably not the most elegant solution (I'm no JavaScript master), but it was working, until I made this most recent change. If you need more information, just let me know.


UPDATE: Here's my current script, using .data() and .split() as suggested. I'm having trouble getting the parent category to show as a miss if all its children are misses, but I'll post a separate question for that.

$(document).ready(function() {
    $("#filters select").change(function() {
        $(".cat").each(function() {
            $(this).data("match", true);
            $(this).css("opacity", "1");
            // $(this).removeClass("open");
        });
        $("#filters select").each(function() {
            var filter = $(this).attr("name");
            var value = $(this).val();
            $(".cat").not(".primary").each(function() {
                if ($(this).data(filter)) {
                    var match = $(this).data("match");
                    var attributes = $(this).data(filter).split(", ");
                    var i = 0;
                    $(attributes).each(function() {
                        if (value && attributes[i] !== value) {
                            match = false;
                        } else {
                            match = true;
                            return true;
                        }
                        i++;
                    });
                    $(this).data("match", match);
                }
                if ($(this).data("match") === false) {
                    $(this).css("opacity", "0.25");
                } else {
                    $(this).css("opacity", "1");
                }
            });
        });
    });
});

Solution

  • You can use the String's split function to split the comma-separated values into an array, and then use the Array's indexOf function to check for a match.

    var attr = $(this).attr("data-" + filter);
    
    if (attr && (attr.split(/[\s*,\s*]+/).indexOf() >= 0)) {
    

    Note: I left out this part of the check: attr !== false. attr should either be a String or undefined, so it will never be false. Did you mean to check if it is the string "false"?


    Also, when you call the following:

    var childAttr = $(this).find(".cat").attr("data-" + filter)
    

    You should be aware that .attr() will return the value of the first matched element, and from your markup it looks like there could be multiple matched elements.