mardi 24 septembre 2019

Drupal attachBehaviors checkbox overlay not working

Although this question relates to Drupal, I am asking it here because I have a JSFiddle example of the behavior, and no knowledge of Drupal should be required.

The Goal

Create an overlayed prettier checkbox for each one. The user should be able to check/uncheck them by clicking or by keyboard. Checking the select all checkbox should check all boxes below. Unchecking should uncheck them all. If already checked, unchecking a checkbox below should uncheck the select all checkbox. Also, clicking a row should act as if you were clicking on the checkbox.

Table with checkboxes

The Problem

Clicking the non-"select all" boxes doesn't work. It changes the value twice, due to some of the VBO code for row-clicking, I think. This can be seen by viewing what is logged in the browser console when clicking one.


The JSFiddle: https://jsfiddle.net/42zxng7c/7/

Only the JQuery is included below.


A summary of the code sections:

The HTML is a small table with simulated VBO (Views Bulk Operations 7.x-3.x) checkboxes. The top checkbox is a Select All checkbox. The HTML will be modified dynamically a bit by the JQuery.

The CSS includes a basic version of some styles I use for the checkboxes. Don't pay it much attention, as it's not important to the question.

The JQuery has basically three parts:

  1. Drupal.attachBehaviors This is boilerplate, and is not super relevant. It is a minimal piece of drupal.js from Drupal 7.67 to use Drupal.attachBehaviors().
var Drupal = Drupal || { 'settings': { 'vbo': { 'row_clickable': true } }, 'behaviors': {} };

Drupal.attachBehaviors = function (context, settings) {
  context = context || document;
  settings = settings || Drupal.settings;
  // Execute all of them.
  $.each(Drupal.behaviors, function () {
if ($.isFunction(this.attach)) {
  this.attach(context, settings);
}
  });
};

$.fn.once = function (events, callback) {
  //The handler is executed at most once per element for all event types.
  return this.each(function () {
$(this).on(events, myCallback);
function myCallback(e) {
  $(this).off(events, myCallback);
  callback.call(this, e);
}
  });
};

// ( and this part from the end )

//Attach all behaviors.
$(function () {
  Drupal.attachBehaviors(document, Drupal.settings);
});
  1. Drupal.behaviors.prettyCheckboxes This is my code, the part with a bug. It is where I modify the HTML a bit and do some work to create pretty checkboxes and radio buttons that replace the normal ones. The radio button portions are not included. Any changes to fix the problem need to happen in this section.
/**
 * Add some JS-based state detection in order to allow prettier checkboxes
 * and radio buttons.
 */
Drupal.behaviors.prettyCheckboxes = {
  attach: function (context, settings) {
// If there are are a ton of elements, don't bring the browser to a
// screeching halt.
var $elements = $('.form-type-checkbox, .form-type-radio', context);
$elements.each(function () {
  // Used for styling purposes.
  $('label', this).prepend('<span class="input"></span>');

  var $input = $('input', this);

  // Add selected state on checkbox inputs.
  $('input[type="checkbox"]', this).each(function () {
    $(this).change(function (event) {
      if (event) {
        // Show change events.
        console.log($(this).attr('value'));
      }
      Drupal.behaviors.prettyCheckboxes.toggleCheckbox(this);

      // Uncheck the "select all" checkbox in the table header.
      var table = $(this).closest('table')[0];
      if (table) {
        var $selectAll = $('.vbo-table-select-all', table);
        Drupal.behaviors.prettyCheckboxes.toggleCheckbox($selectAll);
      }
    });

    Drupal.behaviors.prettyCheckboxes.toggleCheckbox(this);
  });

  // Check if this is a VBO "select all" checkbox.
  if ($input.hasClass('vbo-table-select-all')) {
    // Trigger change event on all vbo-select checkboxes on change,
    // because VBO updates the properties without triggering the event.
    $(this).change(function (event) {
      $(this).closest('table').find('.vbo-select').trigger('change');
    });
  }
});

// if (Drupal.settings.vbo.row_clickable) {
//   $('span.input', context).on('click', function () {
//     $(this).next('input[type="checkbox"]:not(.vbo-table-select-all)').trigger('click');
//   });
// }
  },
  toggleCheckbox: function (checkbox) {
if ($(checkbox).is(':checked')) {
  $(checkbox).closest('.form-type-checkbox').addClass('checked');
}
else {
  $(checkbox).closest('.form-type-checkbox').removeClass('checked');
}
//Drupal.attachBehaviors(checkbox);
  }
};
  1. Drupal.vbo Some code I copied from the Views Bulk Operations 7.x-3.5 project (views_bulk_operations.js) that I am using in order to show how it interacts with my code. It is licensed under GNU GPLv2.
(function ($) {
  Drupal.behaviors.vbo = {
attach: function(context) {
  $('.vbo-views-form', context).each(function() {
    Drupal.vbo.initTableBehaviors(this);
    Drupal.vbo.initGenericBehaviors(this);
  });
}
  }

  Drupal.vbo = Drupal.vbo || {};
  Drupal.vbo.initTableBehaviors = function(form) {
$('.vbo-table-select-all', form).show();
// This is the "select all" checkbox in (each) table header.
$('input.vbo-table-select-all', form).click(function() {
  var table = $(this).closest('table:not(.sticky-header)')[0];
  $('.vbo-select:not(:disabled)', table).prop('checked', this.checked);
});

// Set up the ability to click anywhere on the row to select it.
if (Drupal.settings.vbo.row_clickable) {
  $('.views-table tbody tr', form).click(function(event) {
    var tagName = event.target.tagName.toLowerCase();
    if (tagName != 'input' && tagName != 'a' && tagName != 'label') {
      $('.vbo-select:not(:disabled)', this).each(function() {
        // Always return true for radios, you cannot de-select a radio by clicking on it,
        // it should be the same when clicking on a row.
        this.checked = $(this).is(':radio') ? true : !this.checked;
        $(this).trigger('change');
      });
    }
  });
}
  }

  Drupal.vbo.initGenericBehaviors = function(form) {
// Handle a "change" event originating either from a row click or an actual checkbox click.
$('.vbo-select', form).change(function() {
  // If a checkbox was deselected, uncheck any "select all" checkboxes.
  if (!this.checked) {
    $('.vbo-select-this-page', form).prop('checked', false);
    $('.vbo-select-all-pages', form).prop('checked', false);
    // Modify the value of the hidden form field.
    $('.select-all-rows', form).val('0')

    var table = $(this).closest('table')[0];
    if (table) {
      // Uncheck the "select all" checkbox in the table header.
      $('.vbo-table-select-all', table).prop('checked', false);
    }
  }
});
  }

})(jQuery);

I have spent hours working on this, and I fixed some other issues I had, but so far I cannot figure out why the checkboxes don't work or how I can adapt my code to fix it in a good way. There is a piece of commented out code in my section that might work as a workaround, but it's bad since it simply fires the click event again for each checkbox.

Any help would be great. Thanks.




Aucun commentaire:

Enregistrer un commentaire