1

I am having some problems regarding the jquery event handler function. I couldn't understand how can I set it in the correct way. I will explain my problem, I have three checkboxes: 3D, TRI-Planar, and MPR. And when users check the 3D option the following two checkboxes appear Volume and Sur. Furthermore, when the user clicks the vol checkbox, some options toggle. And when the user unclicks the vol disappear and again if they click them then there will be two options of same. How can I solve it?

Below I have provided a minimal example which I used first without using a class like desire_viewer and method_selection. If you click 3D then vol and sur options toggle and if you click vol options then another options toggle but when u unclick it then it disappears and then click it again then two same options appear. I think there is something wrong with the code.

I expect the output to be like this: If the user clicks 3D options then it should toggle Volume and Surface options and again if the user clicks on the volume checkbox then volume options should toggle vice-verse with the surface. When the user unchecks any checkbox then its process should stop. For example, at first, the user clicked 3D > VOL and then 3D > sur, in this case, the first step should be stopped when the sur checkbox is clicked. I mean like logic 0 and 1. If the user clicks Tri-Planar then it's 1 and its option should toggle and again if the user unchecks it which is 0 then it's processed and should be stopped. So, in the next step user can play with MPR or other checkboxes.

ID_3D_render_selection = "#v1"
ID_tri_planar = "#v2"
ID_mpr_selection = "#v3"
ID_vol_selection = "#r1"
ID_sur_selection = "#r2"

$(ID_3D_render_selection).change(function() {
    $('#select_render_method').slideToggle("slow");
            $(ID_vol_selection).change(function() {
               $('#VOL_OPTIONS').slideToggle("slow");

                var vol_arr = []
                var vol_arr2 = []

                $( "#btn1" ).change(function() {
                    var vol_myFile = $('#btn1').prop('files');
                    vol_arr.push(vol_myFile)
                });

                <!--//////////////////////////////////////-->
                $(document).ready(function() {

                    var vol_opacity = $('<input class=vol_set_opacity id="setScalarOpacityUnitDistance0" type="range" min="0" max="100" step="0.5" value="3">').appendTo('#set_opacity_distance_bm_Color');
                    var vol_distance = $('<input class=vol_set_distance id="setSampleDistance0" type="range" min="0.1" max="10" step="0.1" value="0.4">').appendTo('#set_opacity_distance_bm_Color');
                    var vol_blending = $('<select class=vol_blending_mode id=blendMode0 >').appendTo('#set_opacity_distance_bm_Color');
                        vol_blending.append($("<option>").attr('value',"0").text("Composite"));

                     var sel = $('<select class=colors_channels >').appendTo('#set_opacity_distance_bm_Color');
                          sel.append($("<option>").attr('value',"Reds").text("Reds"));
                          sel.append($("<option>").attr('value',"Blues").text("Blues"));
                          sel.append($("<option>").attr('value',"Greens").text("Greens"));


                    $( "#f" ).change(function() {
                      $('.vol_set_opacity').remove();
                      $('.vol_set_distance').remove();
                      $('.vol_blending_mode').remove();
                      $('.colors_channels').remove();

                            for(i=0; i<$('#f').val(); i++) {
                                 var vol_opacity = $('<input class=vol_set_opacity id="setScalarOpacityUnitDistance'+ i +'"'+ ' type="range" min="0" max="100" step="0.5" value="3">').appendTo('#set_opacity_distance_bm_Color');
                                 var vol_distance = $('<input class=vol_set_distance id="setSampleDistance'+ i + '"' + 'type="range" min="0.1" max="10" step="0.1" value="0.4">').appendTo('#set_opacity_distance_bm_Color');
                                 var vol_blending = $('<select class=vol_blending_mode id="blendMode'+ i +'"'+ ' >').appendTo('#set_opacity_distance_bm_Color');
                                 vol_blending.append($("<option>").attr('value',"0").text("Composite"));

                                  var sel = $('<select class=colors_channels >').appendTo('#set_opacity_distance_bm_Color');
                                  sel.append($("<option>").attr('value',"Reds").text("Reds"));
                                  sel.append($("<option>").attr('value',"Blues").text("Blues"));
                                  sel.append($("<option>").attr('value',"Greens").text("Greens"));

                            }
                    })
                });

                <!--//////////////////////////////////////-->
                $( "#btn2" ).click(function() {
                    var vol_Picked_color = $(".colors_channels");

                    for(var i = 0; i < vol_Picked_color.length; i++){
                        vol_arr2.push($(vol_Picked_color[i]).val() );
                    }

                    console.log(vol_arr2)

                    if (vol_arr.length==vol_arr2.length){
                        vol_processFile(vol_arr, vol_arr2)
                        } else {
                            alert("Please check the number of input vs the number of channels ")
                    }

                });

            });
    });


var tri_arr =[]
$(ID_tri_planar).click(function() {
    $('#TRI_PLANAR_OPTIONS').slideToggle("slow");
});

$( "#btn1" ).change(function() {
    var tri_myFile = $('#btn1').prop('files');
    tri_arr.push(tri_myFile)
});
$( "#btn2" ).click(function() {
    tri_processFile(tri_arr)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="{% static '/js/jquery-3.6.0.min.js' %}"></script>
<div >
    <input class="desire_viewer" type="checkbox" name="Viewers" id="v1" value="3D"><label for="v1">3D</label>
    <input class="desire_viewer" type="checkbox" name="Viewers" id="v2" value="TRI-Planar"><label for="v2">TRI-Planar</label>
    <input class="desire_viewer" type="checkbox" name="Viewers" id="v3" value="MPR"><label for="v3">MPR</label>
</div>

<div id="select_render_method" class="section_header_2" style="display:none;">
    <input class="method_selection" type="checkbox" name="Render" id="r1" value="Vol" ><label for="r1">Vol</label>
    <input class="method_selection" type="checkbox" name="Render" id="r2" value="Sur" ><label for="r2">Sur</label>
</div>

<div id="divInput" class="section_internal_layout_card_middle_body_title_4" >
    <table>
        <tbody>
        <td>Select the number of inputs:</td>
        <td>
            <input min=1 max=10 type="number" id="f" value="1" style="width: 43px; height: 25px;">
        </td>
        </tbody>
    </table>
    <div>
        <input name="inputFile" type="file" multiple id="btn1" style="margin-left: 1px; margin-top: 30px; padding: 0px; border: none; z-index: 1;">
    </div>
    <div>
        <button name="render" id="btn2" style="margin-top: 0px; margin-left:304px; width: 96px; font-size: 14pt; border: none; z-index: 1; margin-bottom: 7px;"> Render </button>
    </div>
</div>


<div id="VOL_OPTIONS" class="section_header_5" style="display:none;">
    <div id="set_opacity_distance_bm_Color" class="section_internal_layout_card_middle_body_title_5">
        <p><b>Scalar Opacity</b>&ensp<b>SampleDistance</b><b>Blending</b><b>Color</b></p>
    </div>
</div>

<div id="SUR_OPTIONS" class="section_header_6" style="display:none;">
    <div id="set_iso_Color" class="section_internal_layout_card_middle_body_title_55">
        <p><b>Scalar Opacity</b><b>Iso value</b><b>Color</b> </p>
    </div>
</div>

<div id="TRI_PLANAR_OPTIONS" class="section_header_7" style="display:none;">
    <table>
        <tbody>
        <tr>
            <td>XY</td>
            <td>
                <input class="XY" type="range" min="0" max="119" step="1" value="0"> <!-- sliceI -->
            </td>
        </tr>
        <tr>
            <td>XZ</td>
            <td>
                <input class="XZ" type="range" min="0" max="119" step="1" value="0"> <!-- sliceJ -->
            </td>
        </tr>
        <tr>
            <td>YZ</td>
            <td>
                <input  class="YZ" type="range" min="0" max="119" step="1" value="0"> <!-- sliceK -->
            </td>
        </tr>
        <tr>
            <td>Color level</td>
            <td>
                <input class="colorLevel" type="range" min="0" max="255" step="1" value="0">
            </td>
        </tr>
        <tr>
            <td>ColorWindow</td>
            <td>
                <input class="colorWindow" type="range" min="0" max="255" step="1" value="0">
            </td>
        </tr>
        </tbody>
    </table>
</div>

<div id="MPR_OPTIONS" class="section_header_8" style="height:412px; display:none;">
    <table><tbody><tr> // do some stuff </tr></tbody></table>
</div>
DMC-416
  • 45
  • 6
  • 1
    Please visit [help], take [tour] to see what and [ask]. Do some research, search for related topics on SO; if you get stuck, post a [mcve] of your attempt, noting input and expected output, preferably in a [Stacksnippet](https://blog.stackoverflow.com/2014/09/introducing-runnable-javascript-css-and-html-code-snippets/) – mplungjan Apr 14 '22 at 16:32
  • @mplungjan Thank you for the suggestion. I just edited my post so that viewers can understand my problem better. – DMC-416 Apr 14 '22 at 23:29

2 Answers2

1

UPDATE:

OP has edited their question significantly and much of my original answer now makes no sense. Here's a new answer for the current code. Leaving my original answer below for reference.

The main problem in the new code is that your volume toggle actions are not balanced - the untick action does not do the reverse of the tick action.

  • When you tick the volume checkbox, you add some HTML to the page, display it, and set up some new change handlers;

  • When you untick that same checkbox, you add the same HTML again, hide it, and add another set of identical change handlers on top of the first set;

So you can see that if you tick to volume checkbox, untick it, and then tick it again, you will see 2x your HTML on the page.

The other major problem is what I described in my original answer below as "problem 2" - you are adding event handlers inside event handlers. In the example tick/untick/tick example of the volume checkbox above, you will end up with 2x handlers for #f, and 2x handlers for #btn1. If, for eg, you click #btn1, that code will now run 2x, simultaneously. This gets very messy very quickly, and will cause all kinds of problems.

Here's updated, working code, with comments.

Note I have simplified the code a lot by removing lots of stuff which is not relevant to the current problem. Your question summaries the key problem:

... and when the user unclicks the vol disappear and again if they click them then there will be two options of same. How can I solve it?

Ticking and unticking the volume checkbox shows 2x duplicate options. That's the problem we're tying to solve. Everything else here is not relevant, and we can remove it. It is much simpler - for you too! - to find and fix the problem when you focus on just the part that is not working. This is what the minimal in MVCE refers to.

// Document ready event handler should never be nested inside other handlers.
$(document).ready(function() {

    // Volume-related variables we need to be able to access later
    var vol_opacity, vol_distance, vol_blending, sel;

    // Click and change events are technically the same for checkboxes, but still be,
    // consistent - use change here, not a mix of change and click as your code had.
    // https://stackoverflow.com/questions/11205957/jquery-difference-between-change-and-click-event-of-checkbox
    $('#v1').change(function() {
        $('#select_render_method').slideToggle("slow");
    });

    $("#v2").change(function() {
        $('#TRI_PLANAR_OPTIONS').slideToggle("slow");
    });

    // Do not nest event handlers inside each other - move this handler outside other
    // change handlers
    $('#r1').change(function() {

        // We are toggling elements on and off here.  If you are ADDing the HTML when
        // the checkbox is ticked, it means you have to REMOVE that HTML when it is
        // unticked.  Is it really necessary?  Modifying the DOM like this is slow and
        // inefficient, what about just toggling visibility, like you do with
        // slideToggle() for other options?

        // Anyway - assuming you want to add/remove it, let's do it like this:

        if ($(this).is(':checked')) {
            addVolumeStuff();
        } else {
            deleteVolumeStuff();
        }

        // I moved this toggle below the code above - .appendTo is synchronous, so
        // wait until the HTML updates are done before displaying them.
        $('#VOL_OPTIONS').slideToggle("slow");
    });

    /**
     * Add some HTML when the vol checkbox is ticked
     */
    function addVolumeStuff() {
        vol_opacity = $('<input class=vol_set_opacity id="setScalarOpacityUnitDistance0" type="range" min="0" max="100" step="0.5" value="3">').appendTo('#set_opacity_distance_bm_Color');
        vol_distance = $('<input class=vol_set_distance id="setSampleDistance0" type="range" min="0.1" max="10" step="0.1" value="0.4">').appendTo('#set_opacity_distance_bm_Color');

        vol_blending = $('<select class=vol_blending_mode id=blendMode0 >').appendTo('#set_opacity_distance_bm_Color');
        vol_blending.append($("<option>").attr('value',"0").text("Composite"));

        sel = $('<select class=colors_channels >').appendTo('#set_opacity_distance_bm_Color');
        sel.append($("<option>").attr('value',"Reds").text("Reds"));
        sel.append($("<option>").attr('value',"Blues").text("Blues"));
        sel.append($("<option>").attr('value',"Greens").text("Greens"));
    }

    /**
     * Delete the volume controls when vol checkbox is unticked
     */
    function deleteVolumeStuff() {
        vol_opacity.remove();
        vol_distance.remove();
        vol_blending.remove();
        sel.remove();
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div>
    <input class="desire_viewer" type="checkbox" name="Viewers" id="v1" value="3D">3D
    <input class="desire_viewer" type="checkbox" name="Viewers" id="v2" value="TRI-Planar">TRI-Planar
    <input class="desire_viewer" type="checkbox" name="Viewers" id="v3" value="MPR">MPR
</div>

<div id="select_render_method" class="section_header_2" style="display:none;">
    <input class="method_selection" type="checkbox" name="Render" id="r1" value="Vol" >Vol
    <input class="method_selection" type="checkbox" name="Render" id="r2" value="Sur" >Sur
</div>

<div>
    <input min=1 max=10 type="number" id="f" value="1">
    <div>
        <input name="inputFile" type="file" multiple id="btn1">
    </div>
    <div>
        <button name="render" id="btn2">Render</button>
    </div>
</div>

<div id="VOL_OPTIONS" style="display:none;">
    <div id="set_opacity_distance_bm_Color">
        <p><b>Scalar Opacity</b> <b>SampleDistance</b> <b>Blending</b> <b>Color</b></p>
    </div>
</div>

<div id="SUR_OPTIONS" style="display:none;">
    <div id="set_iso_Color">
        <p><b>Scalar Opacity</b> <b>Iso value</b> <b>Color</b></p>
    </div>
</div>

<div id="TRI_PLANAR_OPTIONS" style="display:none;">
    <table>
        <tbody>
        <tr>
            <td>XY</td>
            <td>
                <input class="XY" type="range" min="0" max="119" step="1" value="0">
            </td>
        </tr>
        <tr>
            <td>... etc ...</td>
            <td> </td>
        </tr>
        </tbody>
    </table>
</div>

Original answer

Problem 1:

var value = $("input[name='Viewers']:checked").val();

The selector will match all 3 of your checkboxes. But what will the .val() of 3 elements look like? Quoting the docs for .val():

Get the current value of the first element in the set of matched elements.

So you're only ever going to get one of your checkbox values, the first checked one. Try it:

$('.desire_viewer').on('click', function() {
    console.log($('.desire_viewer:checked').val());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="desire_viewer" type="checkbox" name="Viewers" value="3D">3D
<input class="desire_viewer" type="checkbox" name="Viewers" value="TRI-Planar">TRI-Planar
<input class="desire_viewer" type="checkbox" name="Viewers" value="MPR">MPR

So how do you check if a particular checkbox is checked? As you can imagine this is a pretty common problem and there are a lot of duplicates here, eg:

You need to iterate over each of the inputs, like so:

$('.desire_viewer').on('click', function() {
    console.log('These are now checked:');
    $('.desire_viewer:checked').each(function(i) {
        console.log(i, ':', $(this).val());
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="desire_viewer" type="checkbox" name="Viewers" value="3D">3D
<input class="desire_viewer" type="checkbox" name="Viewers" value="TRI-Planar">TRI-Planar
<input class="desire_viewer" type="checkbox" name="Viewers" value="MPR">MPR

Note that inside the loop, $(this) represents the current jQuery object in the loop.

So in your case, your code might look something like:

$('.desire_viewer').on('click', function() {
    $('.desire_viewer:checked').each(function(i) {
        var val = $(this).val();
        if (value == "3D") {
            // Do stuff ...
        } else if (value == "TRI-Planar") {
            // Do other stuff ...
        } else if (value == "MPR") {
            // Do other different stuff ...
        }
    });
});

Problem 2:

$( "#btn1" ).change(function() {

This adds an event handler to your button. From this point on, any time that button is changed this handler will notice, and that function code will fire.

That's fine and will work. The problem is where it is added - when a checkbox is clicked. If the user decides they made a mistake and UNticks the TRI-Planar checkbox, guess what happens? Another identical event handler will be added to the button. And the best part? They will both fire! The handler function, whatever it is, will run 2x, in parallel. They don't overwrite each other, they all stack up, and they all run.

The (simplest) solution is to add event handlers independent of user interaction or other changes on the page, so they can only be bound once. If the code that runs is somehow dependent on where/when it would be triggered, set that up in the handler. Eg if this button change should only do something if TRI-Planar is checked, have the button event handler check if that is true before doing anything. Eg:

$("#btn1").change(function() {
    // Maybe this should only happen if a certain checkbox is checked
    $('.desire_viewer:checked').each(function(i) {
        var val = $(this).val();
        if (value == "TRI-Planar") {
            // Do stuff
        }
    });
});

// Rest of your code - handlers are independent of user interaction and
// will only be bound once.
$('.desire_viewer').on('click', function() {
    // ...

Note another option is to add handlers wherever you like, and use .off() to remove them, so you can dynamically add/remove them throughout your code. For simple applications I don't think this extra complexity is necessary.

There are lots of questions here about this too:

Minor suggestion:

It looks like this is part of a bigger applicaton, and it looks like you're trying to reduce code repetition with variables like ID_desire_viewer = '.desire_viewer' etc. That's a good start but since those represent jQuery selectors, you may as well go just a tiny bit further and cache the actual selector.

Every time you do something like $(ID_desire_viewer) jQuery scans the whole DOM again to find matches. For small applications/pages you probably won't notice the performance hit doing that, but it is good practice to avoid it in case your app grows, and just in terms of read- and maintain-ability, DRY, etc.

To avoid that, save the results of the first search by doing:

let $viewers = $('.desire_viewer');

// Then you can do:
$viewers.on('click', function() { ...

Some references about that:

Don't Panic
  • 13,965
  • 5
  • 32
  • 51
  • Thank you for the reply. I tested your suggestion but still when I perform this step: click unclick click and try to render 3 same images. Please check the above-edited question where I have uploaded the picture and as well as the code which I tried after your suggestion. For me below code is important because it helps to toggle the options and i think it might be creating the problem of repretation. $(ID_tri_planar).click(function() { $('#TRI_PLANAR_OPTIONS').slideToggle("slow"); – DMC-416 Apr 15 '22 at 21:10
  • Why did you delete your variables? Now `ID_tri_planar`, `ID_mpr_selection`, etc are not defined so `$(ID_tri_planar)` etc will not work ...? Why are you still nesting the `$("#btn1").change` handler inside the `$(ID_tri_planar).click` handler, same for `$(ID_mpr_selection)` handler? What is `#TRI_PLANAR_OPTIONS`, it is not in your HTML, but you say it is important? Try to create a [minimal, complete, and verifiable example](https://stackoverflow.com/help/mcve) - include **just** the HTML/JS necessary to show the **current** problem. – Don't Panic Apr 16 '22 at 06:11
  • Hi, I didn't delete the variables, earlier I just didn't include them in the edited post. Yeah, I was, unfortunately, passing $("#btn1").change inside a $(ID_tri).click loop, which I already fixed as you can see above in the edited post. #TRI_PLANAR_ and other options are other toggles that appear after the user checks boxes. For example, if a user checks the TRI-Planar box then #TRI_PLANAR.. should toggle and it's the same for other boxes. The current prob: when I check the box, options toggles but when I uncheck it, still there are toggled options, why it didn't vanish after I unchecked it. – DMC-416 Apr 16 '22 at 11:05
  • 1
    Wow, your edited code is now wildly different from your original code. You've introduced *many* new problems and complications ... My answer makes no sense now bcs you've changed the question so much. For future reference, please don't do that - this question/answer is now useless to future visitors. Why are you using `.unbind()`? Why do you have `$(document).ready()` inside `$("#btn1").change()`, `$( "#btn2" ).click()` inside `$("#btn1").change()`, 2x `$(document).ready()`, 2x `$( "#btn2" ).click()` handlers (both nested inside other handlers), ...? – Don't Panic Apr 16 '22 at 13:30
  • If you're still looking for help, I'd suggest editing your questions back to your original code, but **removing** the stuff that is not relevant - please see https://stackoverflow.com/help/mcve. `#btn` is not relevant to this particaular part of your problem, remove that code; `#TRI_PLANAR_OPTIONS` is apparently important, so include that (**minimal!**) HTML. I think the original question was simply about displaying your slide toggles? Focus on that, and only that. – Don't Panic Apr 16 '22 at 13:32
  • I am really sorry. I thought you understood my prb and I was implementing your sugg and was updating this post same time to keep things in the loop but I was wrg. Now, I have edited the post and have also provided minimal example. Play with the 3D box and its toggle option. You will see everytime you click the vol checkbox, the options start to add. So that's the prb, which I am facing since the beginning. I always assume if u check the checkbox then the operation starts and if unclick it then it should stop but here it's adding the output. Here I haven't used classes, but I want to use them. – DMC-416 Apr 16 '22 at 21:58
  • After your suggestion, I started to play with classes: $('.desire_viewer').on('click', function() { $('.desire_viewer:checked').each(function(i) { var value = $(this).val(); console.log(value); if (value == "3D") { ....... and so on. Thank you for being calm. – DMC-416 Apr 16 '22 at 22:00
  • I have updated my answer to suit your new code and problems. – Don't Panic Apr 17 '22 at 08:49
  • Thank you, with your help now it's working as I imagine. Both updated and original answers of yours helped to solve my problem. I just have one Q: Is it possible to block other checkboxes when users are working on any one box? For exp: I clicked 3D > VOL and at the same time can also click TRI-Planar, and I saw when I do this volume option and tri_planar options come on the same page. So I was thinking about whether it is possible to block other checkboxes unless the user first unchecks the 3D box and so on for others. When I click two checkboxes then both checkboxes toggle on the same page. – DMC-416 Apr 17 '22 at 12:18
  • Sure it is possible - it sounds like those should maybe be `radio` buttons? Checkboxes you should be able to tick more than one, but there can only be 1 selected radio button at once. So if radios have class `.method_selection` you could have `$('. method_selection').change(){ ... }`, and in there you find out which one is selected: `var selected = $(this).val();`. And then, you do whatever you want depending on which is selected: `if (selected === 'Vol') { //1. hide Tri stuff; 2. show Vol stuff; } else { //1. Hide Vol stuff; //2 show Tri stuff; }` – Don't Panic Apr 17 '22 at 15:48
  • Eg: https://stackoverflow.com/questions/2777139/how-to-use-jquery-to-show-hide-divs-based-on-radio-button-selection, ... – Don't Panic Apr 17 '22 at 15:49
  • Hi there, I did what you asked for radio choice but when I go back from 3D> VOl to TRI-Planar directly. Then it shows me both toggle on the same screen. $('.method_selection').change(function() { if ($(this).val() == "Volume") { addVolumeStuff(); } else { deleteVolumeStuff(); } $('#VOL_OPTIONS').slideToggle("slow"); $('#TRI_PLANAR_OPTIONS').slideToggle("slow").hide(); $('#MPR_OPTIONS').slideToggle("slow").hide(); }); and with desire_viewer class I create if condition... But it didn't work. If you don't mind then can you help me on this! – DMC-416 Apr 18 '22 at 14:10
  • Your volume `method_selection` value is not `Volume`. C'mon, you need to learn to debug things on your own :-) Use `console.log()` to show the value of variables, and to show which branches of the code are happening, check if your event handlers are firing as expected, etc etc ... Use your browser dev tools console to watch for errors and your logs ... – Don't Panic Apr 18 '22 at 15:16
0

Try this

$('input["type=checkbox"]').attr("checked",'false');

i hope it was useful !

Joukhar
  • 724
  • 1
  • 3
  • 18