deselecting a radio button

One of the nice features of OpenClinica is the SimpleConditionalDisplay. It's straightforward: for example if a user enters Yes, the Informed Consent was signed, then an item for the Consent date is shown. If the user then thinks 'that is not correct, the answer must be No' and he/she selects that, the item for Consent date is hidden again. And if the date has already been entered, then a warning is displayed, saying something like 'please remove the Consent date.'
So far so good, but sometimes the items that are shown using SimpleConditionalDisplay are radio-buttons. You answer Yes and other items with radio-buttons are shown and you select them and only then you realise that the first answer should have been No and now you're stuck. You want to save the CRF, but in order to do this you must deselect the radio-buttons, and that is not possible.


fig. 1: example of a warning

let's add some script

In order to do this, we must add some java-script to our CRF, in the Right_Item_Text of the item with the radio-buttons. Below is the script plus an explanation of the workings, but if you can't wait to see it in action, click here for the XL-file.


<img id="undoradio1" src="images/bt_Restore.gif">
<script src="includes/jmesa/jquery.min.js">// for OC versions before 3.1.4, use jquery-1.3.2.min.js !</script>
<script lang="Javascript">
$.noConflict();
jQuery(document).ready(function($){
 $("#undoradio1").click(function (e) {
 for (i = 0; i < radioGroup1.length; i++) { radioGroup1[i].checked = false;  }
 radioGroup1.change();
  });
 // identify the group radio buttons
 var radioGroup1 = $("#undoradio1").parent().parent().find("input");
});     
</script>

As you can see we first put an icon in the Right_Item_Text and for this we use the default 'undo'-icon of the OpenClinica interface. We give this icon an ID, undoradio1 and we use this to make a reference to the group with the radio-buttons: var radioGroup1 = $("#undoradio1").parent().parent().find("input");.
We then define what to do when the undo-icon is clicked, namely: loop through all the elements of the radio-group and set them to not chosen: for (i = 0; i < radioGroup1.length; i++) { radioGroup1[i].checked = false;.
As a last step we set the change-status of the radio-group, which activates the red and yellow icon on the CRF that indicates there are unsaved data: radioGroup1.change();.

must I repeat that for each and every radio group?

If you want to be able to deselect more than one radio-group, you can copy the above code and change undoradio1 to undoradio2, undoradio3, etc. but that looks messy. Thomas Kissner modified the code, so that you can make a function of the deselecting and call that function, every time an image of a certain class is clicked. Sounds difficult? Look at the code. First the part in the RIGHT_ITEM_TEXT:
<img class="undoradio_for_all_img" title="Click to deselect entry" alt="Click to deselect entry" src="images/bt_Restore.gif">.
That is more or less the same as we saw before: add the image. But as an extra, we add a class to the image. Now we can use that for our function and because we want to define that just once, we put it in the instructions part of our CRF:

<script src="includes/jmesa/jquery.min.js">// for OC versions before 3.1.4, use jquery-1.3.2.min.js !</script>
<script>
$.noConflict();
jQuery(document).ready(function($) {
 $("img.undoradio_for_all_img").each(function () {
  $(this).click(function (e) {
   var radiogroup = $(this).parent().parent().find("input");
   for (i = 0; i < radiogroup.length; i++) { 
    radiogroup[i].checked = false;
   }
  });
  }); 
});
</script>

Many thanks, Thomas Kissner.

but I need this in a repeating item group

What puzzled me for a long time was: how do I apply this in a RepeatingItemGroup? The problem is that you cannot place an undo-button, or any other object for that matter, somewhere in the table that is used for the RIG. I thought. But again Thomas came to the rescue! He made a script that used the function .after, which does more or less what it promises: it places something after an object. Together we came up with the following:


<div id="UndoRadio_Col"></div>
<script type="text/JavaScript" language="JavaScript" src="includes/jmesa/jquery.min.js"></script>
<script>
$.noConflict();
jQuery(document).ready(function($){
  function Display_Undo_Buttons_In_Column(ColNo){
    $('#UndoRadio_Col').parent().parent().parent().parent().find('tbody > tr:visible').each(function(i){
      // only add the undo image, if it doesn't exist
      if ($(this).children('td:nth-child('+ColNo+')').find('.undoradio').length == 0){
        //if the radio's are vertical, then br-tags exist, so put the undo button after the last br
        if ($(this).children('td:nth-child('+ColNo+')').children('br').length != 0){
          $(this).children('td:nth-child('+ColNo+')').children('br').last().after("<img class='undoradio' title='Click to deselect entry' alt='Click to deselect entry' src='images/bt_Restore.gif'>"); 
        }
        else{
          //if the radio's are horizontal, add the undobutton after the last radio-button of the group
          $(this).children('td:nth-child('+ColNo+')').children('input[type=radio]').last().after("<img class='undoradio' title='Click to deselect entry' alt='Click to deselect entry' src='images/bt_Restore.gif'>"); 
        }
      }
    });
  };
  function UndoRadio(){
    $("img.undoradio").each(function (){
      $(this).click(function (e){
        var groupName = $(this).parent().find('input[type=radio]').attr('name');
        var radioGroup1 = $(this).parent().parent().find('input[name="'+groupName+'"]');
        for (i = 0; i < radioGroup1.length; i++){ 
          radioGroup1[i].checked = false;
        }
        radioGroup1.change();
      });
    });
  }; 
  //repeat the procedure when the add-button is clicked
  $('.button_search').click(function(){
    Display_Undo_Buttons_In_Column(3);
    Display_Undo_Buttons_In_Column(5);
    Display_Undo_Buttons_In_Column(7);
    UndoRadio();
  });
  //and run the procedure when the doc is loaded
  Display_Undo_Buttons_In_Column(3);
  Display_Undo_Buttons_In_Column(5);
  Display_Undo_Buttons_In_Column(7);
  UndoRadio();
});
</script>

Neat, isn't it?

First of all a word of warning: if you copy this code and paste it into your XL you may run out of space, as the limit is 2000 characters. To get (just) under this, replace all two spaces with one.

I'm sure you would like to have some subtitles with that.
The main thing in the script is the function Display_Undo_Buttons_In_Column(ColNo) which takes as parameter the column-number. First you loop through all the rows of the table with find('tbody > tr:visible').each(function(i) and in each row you go to the column with index ColNo: $(this).children('td:nth-child('+ColNo+')'). Then you first check if there is already an undo-button, or rather: you check if anything of class .undoradio exists in that column. (Don't worry, we'll explain later when this is the case.)
Now we have two options: the radio buttons are horizontal or vertical. If they are vertical, then each option ends with a br so we place our undo-button after the last one: children('br').last().after. If on the other hand the radio's are horizontal, then we go looking for the last radio and place the undo-button there: children('input[type=radio]').last().after
Once we have the buttons, we must tell what to do when one is clicked and this is done in the same way as described above, using function UndoRadio(). Or: almost the same way. This time we capture the name of the radio button and then uncheck all radio's with that same name.

Of course in most cases we do not know how many rows there will be, because of the Add-button. The click-event of this button already has script assigned to it, but fortunately this button also has a class, .button_search, so what we in fact do is say: everytime an object of class .button_search is clicked, then please add undo-buttons. And that is the reason why we had to check first if there was already an undo-button.


fig. 2: deselecting in a group

does it really, really work?

You may ask 'does it really work?' and the answer is of course: 'implement it and watch the radio-buttons in your CRF' but that is probably not what you mean. You want to know if all these activities are stored correctly. Well, let's have a look at the audit-trail. In the next screenshot you see the trail after entering data in the CRF and then marking it complete.


fig. 3: audit trail after initial entry

Now we realize our mistake and we open the CRF again and deselect the radio-groups 2 and 3. Before changing the first answer, we click Save and then we're prompted to give a ReasonForChange. We provide this by (manually) creating Discrepancies in the normal way. Then we change the value of the first question and then we click Save and we're prompted one last time for a ReasonForChange-Discrepancy.


fig. 4: making changes

And the audit-trail records all these activities correctly:


fig. 5: audit trail after changes

Other how-to-pages can be found here.

this page was last reviewed April 2015