3

I'm creating a filter form, I want to filter out objects with the property high=true, or high=false or ignore that property.
All I'm getting is the value "on" so far.
Also on the third value (null or whatever) I want the styling to be different, like disabled.
Anyone know an easy way to do this?

function readValue() {
  var highValue = $('#high').val();
  $('#result').html(highValue);
  if (highValue === null) {
    //don't filter
  } else {
    //filter
  }
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">

<div class="btn-group-toggle" data-toggle="buttons">
  <label onclick="readValue()" class="btn btn-secondary active">
    <input id="high" type="checkbox" checked autocomplete="off"> High
  </label>
</div>
Result:<div id='result'></div>

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
Cold_Class
  • 3,214
  • 4
  • 39
  • 82

4 Answers4

3

I figured out a way of having as many states as wanted to be toggled with one button while the button indicates the state. This is what I was looking for - most of the code is proudly found and put together from different parts of the internet!
If you have a suggestion how to make this even shorter AND easier to read, please comment or directly edit it :)

var $radios = $('input[type="radio"][name="high"]');
$('#result').html($radios.filter(':checked').val());

$('#toggler').click(function() {
  var colorClasses = ["btn-danger", "btn-success", "btn-secondary"];
  var $checked = $radios.filter(':checked');
  var $next = $radios.eq($radios.index($checked) + 1);
  if (!$next.length) {
    $next = $radios.first();
  }
  $next.prop("checked", true);
  var newValue = $radios.filter(':checked').val();
  $(this)
    .removeClass(colorClasses.join(" "))
    .addClass(colorClasses[newValue]);
  $('#result').html(newValue);
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">

<div class="btn-group-toggle" data-toggle="buttons">
  <input type="radio" name="high" value="2" checked hidden>
  <input type="radio" name="high" value="1" hidden>
  <input type="radio" name="high" value="0" hidden>
  <button type="button" id="toggler" class="btn btn-secondary">High</button>
</div>
Result:
<div id='result'></div>

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
Cold_Class
  • 3,214
  • 4
  • 39
  • 82
2

Similar question had been asked and some people suggested to use :indeterminate pseudo class to represent the NULL state - where that filter property is ignored.

What is :indeterminate?

Bootstrap 4 custom checkboxes utilize the :indeterminate pseudo class only when manually set via JavaScript: http://getbootstrap.com/docs/4.1/components/forms/#custom-forms

:indeterminate is pretty new and not all browsers render it correctly so I would just go for simpler approaches:

  • A checkbox next to the filter property to represent whether we need to filter by that property or not
  • A checkbox or radio button to present true/yes or false/no state.

On page load

The filter is unselected, and its available values are hidden.

enter image description here

Filter is selected/engaged

If the filter is selected, its available values are shown.

enter image description here

On submit

On form submit, you can always pass 2 values in:

  • Whether the filter property is engaged or not, i.e., $().is(":checked")
  • The filter property value, i.e., yes/no

Fiddle: http://jsfiddle.net/aq9Laaew/72593/

jQuery plugin

A plugin for null-able checkbox: http://vanderlee.github.io/tristate/

Community
  • 1
  • 1
David Liang
  • 20,385
  • 6
  • 44
  • 70
  • thanks, I think your answer gives some really useful information, +1, - but I want my specific question answered in this case, not alternatives. And I don't know how to actually use that :indeterminate class in my code example easily to reach my goal. – Cold_Class Jul 05 '18 at 21:39
  • See my updates. `:indeterminate` can be used to represent the null state but it's itself pretty new so not all browsers render it the same way. That was why I just went for "normal" way to do it. – David Liang Jul 05 '18 at 21:54
0

Try this way: this button group is part of Bootstrap UI radio input with labels. I don't have time or interest to beautify these radio buttons.

<script type="text/javascript">
    function renderRadioOptions() {
        var radioGroupOpacity = ( $(this).val() == '' ) ? 0.5 : 1.0;
        $("div#filter_options_group > label.btn").css('opacity', radioGroupOpacity);
    }

    $(function() {
        $("div#filter_options_group > label.btn").css('opacity', 0.5);
        $("input[name='options']").on('change', renderRadioOptions );
    });

</script>

<div class="btn-group btn-group-toggle" data-toggle="buttons" id="filter_options_group">
    <label class="btn btn-secondary active">
        <input type="radio" name="options" value="" id="option1" autocomplete="off" checked> 
        <i class="glyphicon glyphicon-remove">
    </label>
    <label class="btn btn-secondary">
        <input type="radio" name="options" value="low" id="option2" autocomplete="off"> Low
    </label>
    <label class="btn btn-secondary">
        <input type="radio" name="options" value="high" id="option3" autocomplete="off"> High
    </label>
</div>
BG San
  • 44
  • 4
0

I liked the answer from Cold_Class but I needed something a bit more generic. Building on that answer I did this.

Javascript:

function onTogglerClick(el) {
    // Get the radios that are children of the parent div element
    let $radios = $(el).children('input[type="radio"]');

    // Use the radio button vals to determine the array, note that the values are also the same as the class names
    let valsClasses = $radios.map(function () {
        return $(this).val();
    }).toArray();

    // Advance to the next radio button
    let $checked = $radios.filter(':checked');
    let $next = $radios.eq($radios.index($checked) + 1);
    if ($next.val() === undefined) {
        $next = $radios.first();
    }
    $next.prop("checked", true);

    // change the class of the parent element
    let newValue = $radios.filter(':checked').val();
    $(el)
        .removeClass(valsClasses.join(" "))
        .addClass(newValue);
}

css

/* Style for radioboxes */

.unchecked:before {
    font-family: "Font Awesome 5 Free";
    font-weight: 200;
    content: "\f0c8";
    width: 24px;
    height: 24px;
}


.checked:before {
    font-family: "Font Awesome 5 Free";
    font-weight: 200;
    content: "\f14a";
    width: 24px;
    height: 24px;
}

.musthave:before {
    font-family: "Font Awesome 5 Free";
    font-weight: 200;
    content: "\f0fe";
    width: 24px;
    height: 24px;
}

.mustnothave:before {
    font-family: "Font Awesome 5 Free";
    font-weight: 200;
    content: "\f146";
    width: 24px;
    height: 24px;
}

Html:

<ul>
@foreach (var tag in Model.Tags)
{
<li>
    <div class="btn-group-toggle toggler unchecked" data-toggle="buttons" onclick="onTogglerClick(this)">
        <input type="radio" name="@tag" value="unchecked" checked hidden>
        <input type="radio" name="@tag" value="checked" hidden>
        <input type="radio" name="@tag" value="musthave" hidden>
        <input type="radio" name="@tag" value="mustnothave" hidden>
        <label>@tag </label>
    </div>
</li>
}