With a UNCHECKED checkbox, how to filter out NSFW tagged divs to be hidden at all times but when checked ON, to follow the visibility rules?

Viewed 263

This question is based on this Question/Answer: Filter accurately with BOTH checkboxes AND dropdown menus at the same time

Hi!

(NOTE, the simplified question and code is in the codepen or the snippet below, here I'm just explaining the context.)

Image of the simplified question, for which I actually need the answer: enter image description here

My reference image PHP gallery is progressing nicely: https://manu.mymaterial.org (Watch out, it includes NSFW material, Fine Art painting by Andrew Loomis, in the front page ATM.)

And that brings me to this question.

How can I have that "Display NSFW material" to NOT show ANY of the images that have the NSFW tag in them when the "Display NSFW material" checkbox is UNCHECKED, no matter what other filtering options I do?

I have a feeling that it is a simple if-statement in Javascript, but I'm uncertain where to put it exactly and if it breaks other things.

At the moment the NSFW checkbox displays ONLY the NSFW material (yellow box) and nothing else. This is not desired.

So, in other words, I have tags for all of the material to be shown in various ways - but now the NSFW tag should HIDE stuff when included into a div. Or in my case, it's just an image file that gets processed by PHP and into a div: Andrew_Loomis; Traditional_Characters_Realistic_Color_NSFW.jpg

Codepen project: https://codepen.io/manujarvinen/pen/wvdzeQv

Thank you all, this is a fine place.

Image of my project thing: enter image description here

var $filterCheckboxes = $('input[type="checkbox"]');
       var $filtermenues = $('.grid1');


       var filterFunc = function () {

           var selectedFilters = [];
           $filtermenues.find(":selected").each(function () {
               
               var v = this.value;
               if (selectedFilters.indexOf(v) === -1 && v)
                   selectedFilters.push(v);
           });

           $('.animal' && '.filterDiv')
               .hide()
               .filter(
                   function (_, a) {
                       var itemCat = $(a).data('category').split(' ');
                       if (itemCat.indexOf("showAll") > -1)
                           return;
                       return selectedFilters.every(
                           function (c) {
                               return itemCat.indexOf(c) > -1;
                           })
                   })
               .show();
           $filterCheckboxes.filter(':checked').each(function () {
               var v = this.value;
               if (selectedFilters.indexOf(v) === -1)
                   selectedFilters.push(v);
           });

           $('.animal' && '.filterDiv')
               .hide()
               .filter(
                   function (_, a) {
                       var itemCat = $(a).data('category').split(' ');
                       return selectedFilters.every(
                           function (c) {
                               return itemCat.indexOf(c) > -1;
                           })
                   })
               .show();

       }

       $filterCheckboxes.on('change', filterFunc);

       $('select').on('change', filterFunc);
body {
width: 100%;
text-align: center;
background-color: black;
color: white;
font-family: sans-serif;
}
.grid {
width: 300px;
margin: 50px auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.grid1 {
width: 300px;
margin: 50px auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.filterDiv {
width: 100px;
height: 100px;
padding-top: 20px;
color: black;
font-weight: bold;
}
<!-- Help needed in this URL: https://stackoverflow.com/q/68334085/4383420 -->

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

   <div class=grid1>
          <select>
              <option value="">--</option>
              <option value="violet">violet</option>
          </select>
          <select>
              <option value="">--</option>
              <option value="blue">blue</option>
          </select>
          <select>
              <option value="">--</option>
              <option value="yellow">yellow</option>
          </select>
      </div>

  <div class=grid>
      <label>VIOLET
      <input type="checkbox" value="violet" />
      <span class="checkmark"></span>
      </label>
      <label>BLUE
      <input type="checkbox" value="blue" />
      <span class="checkmark"></span>
      </label>
      <label>YELLOW
      <input type="checkbox" value="yellow" />
      <span class="checkmark"></span>
      </label>
  </div>

  <div class=grid>
      <div class="filterDiv" data-category="violet blue" style="background-color: blue">Tags: <br />violet <br />blue</div>
      <div class="filterDiv" data-category="violet red MVP" style="background-color: red">Tags: <br />violet <br />red <br />MVP</div>
      <div class="filterDiv" data-category="yellow NSFW MVP" style="background-color: yellow">Tags: <br />yellow <br />NSFW<br />MVP</div>
  </div>


  <div>
  <label>Most Valuable Players (MVP)
  <input type="checkbox" value="MVP" />
  <span class="checkmark"></span>
  </label>
  </div>

  <div>
  <label>Display NSFW material also
  <input type="checkbox" value="NSFW" />
  <span class="checkmark"></span>
  </label>
  </div>

    <div style="width:400px; text-align: left; margin: 60px auto;">
    What I need:
    
    <p>By default, NSFW material checkbox is OFF and YELLOW shouldn't be seen at all in ANY situation.</p>
      <p>When NSFW material checkbox is checked, ALSO YELLOW should be seen, but follow rest of the rules.</p>
      
      
    </div>

3 Answers

The code is overly complex, and hard to follow. For one, this bit

        var v = this.value;
        if (selectedFilters.indexOf(v) === -1 && v)
            selectedFilters.push(v);

merely serves to avoid duplicates in the array. This means that it is more like a set, which is a data structure that avoids duplicates by itself. If you do selectedFilters = new Set(), then that entire block can be replaced by selectedFilters.add(this.value);

Queries "is this category contained in the selected filters" can then also be reduced from selectedFilters.indexOf(cat) === -1 to selectedFilters.has(cat). That way the code already starts to match much more the desired behaviour.

Furthermore, the single-letter variables should also be abolished, and be replaced by explanatory names. The code should also have more comments to explain what's going on.

Then there is the confusion of being able to select filters from both the menu and the checkboxes. I'm not sure how this should work, so I've just changed the code such that it groups them all together: a filter is active when it's either selected by the checkbox or the menu.

Finally there is the problem that the "NSFW allowed" is not really the same as filtering of the other categories. Because of that, I just created a separate variable for it, and removed it from the filter. And finally finally I added an extra call to filterFunc() so that things start out filtered (instead of waiting for the first change to the filter checkboxes/menus). This is the end result.

EDIT: Updated for the "MVP only" checkbox.

EDIT 2: Don't let the "MVP" checkbox show everything with the MVP tag.

EDIT 3: Flipped the condition to show items; they need all the tags now. The old code is left commented-out, so that anyone can revert this switch for themselves if they want.

var $filterCheckboxes = $('input[type="checkbox"]');
var $filtermenus = $('.grid1');


// Return the intersection of setA and setB.
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
function intersection(setA, setB) {
    let _intersection = new Set()
    for (let elem of setB) {
        if (setA.has(elem)) {
            _intersection.add(elem)
        }
    }
    return _intersection
}
function isSuperset(set, subset) {
    for (let elem of subset) {
        if (!set.has(elem)) {
            return false
        }
    }
    return true
}


function filterFunc() {
    var selectedFilters = new Set();

    // Gather tags from the menus.
    $filtermenus.find(":selected").each(function () {
        if (this.value)
            selectedFilters.add(this.value);
    });
    // Gather tags from the checkboxes.
    $filterCheckboxes.filter(':checked').each(function () {
        if (this.value)
            selectedFilters.add(this.value);
    });
    console.log("selected filters:", [...selectedFilters]);
 
    let mayShowNSFW = selectedFilters.has("NSFW");
    selectedFilters.delete("NSFW");
  
    let mustShowMVPOnly = selectedFilters.has("MVP");
    selectedFilters.delete("MVP");

    $('.filterDiv')
        .hide()
        .filter(
            // Returns 'true' to show items, 'false' to hide them.
            function (_, element) {
                let itemCats = new Set($(element).data('category').split(' '));

                // If the item has tag 'showAll', always show it.
                if (itemCats.has("showAll"))
                    return true;
                
                // If item is NSFW but that's not in the filters, hide the item
                // regardless of other tags.
                if (itemCats.has("NSFW") && !mayShowNSFW)
                    return false;
                
                if (mustShowMVPOnly && !itemCats.has("MVP"))
                    return false;
                
                // If there are no filters at all, just show everything.
                if (selectedFilters.size == 0)
                    return true;

                // If this item has all the tags, show it:
                return isSuperset(itemCats, selectedFilters);
              
                // If this item has any of the tags, show it:
                // return intersection(selectedFilters, itemCats).size > 0;
            })
        .show();
}

$filterCheckboxes.on('change', filterFunc);
$('select').on('change', filterFunc);
filterFunc();

You can add this in your css stylesheet

.nsfw{
    visibility: hidden;
}

And this should be in the head tag of your html page

<script>
        $(document).ready(function () {
            $('#nsfw').change(function () {
                if (!this.checked)
                    //  ^
                    $('.nsfw').css('visibility', 'hidden');
                else
                    $('.nsfw').css('visibility', 'visible');
            });
        });
</script>

Your nsfw box should be something like this

<div class="filterDiv nsfw" data-category="yellow NSFW MVP" style="background-color: yellow">Tags: <br />yellow
            <br />NSFW<br />MVP
</div>

Here is the checkbox

<div>
        <label>Display NSFW material also
            <input type="checkbox" value="NSFW" id="nsfw" />
            <span class="checkmark"></span>
        </label>
    </div>

You can give a class to all nsfw images and then set their visibility to hidden in css. and when checkbox is checked you can set nsfw images visibility to hidden.

Related