Search code examples
javascriptjqueryhtmlcustom-data-attributetoggleclass

Using data-attr to identify an active selection


I'll try to keep it at simple as I can.

I have a JSON object that is pulled via AJAX. I am displaying a list of icons in a main div from the data dynamically which can be toggled on or off.

I have a secondary div where the selected items are appearing, while the main div icon receives a class of active.

I want the end user to be able to remove any of them by clicking on them on either the main div or secondary div.

Most of this is working, but I'm having trouble figuring out the best way to map them together so that I have 2 separate click events which can control the same outcome.

I think it may have something to do with the fact that I'm dynamically creating elements... which create more elements... which have to alter the initial elements.

My structure so far is to map the current selection inside of an array. This gives me control over keeping a code-based list of everything that is selected (there is much more data than in the example I'll be providing).

So, here is how I have it so far:

HTML:

<div id="options"></div>
<div id="selectedOptions"></div>

Javascript/jQuery:

// Simple simulated AJAX data
var ourData = {
  "color1": "yellow",
  "color2": "blue"
};
var $options = $('#options');
var $selectedOptions = $('#selectedOptions');
// Array to keep track of which objects are selected
var selectedOptions = [];

// Makes the initial option dynamic list
makeOptions(ourData, $options);

// If an option from the main div is clicked, handle class changing
$('button').on('click', function(){
    pickOption($(this));
});

/* This function is the problem. The option gets removed from the array, and from the secondary div, but the class of active still occurs on the main div. */
$selectedOptions.on('click', '.optionActive', function(){
  var option = $(this).data('color');
  var optionPosition = jQuery.inArray(option, selectedOptions);
  selectedOptions.splice(optionPosition, 1);
  displayOptions(selectedOptions, $selectedOptions);
});

// Creates initial icons (buttons in this case) to the DOM and applies a data-attribute for the color
function makeOptions(options, $container){
    var $results = $('<div id="results">');
  $.each(options, function(key, value){
    var $optionButton = $('<button>' + key + ':' + value + '</button>');
    $optionButton.data('color', value);
    $results.append($optionButton);
  });
  $container.append($results);
}

/* Handler for selecting an option from the Main Div. Handling the class active.
I am not using a simple classToggle because there are many situations where a click is not allowed */
function pickOption($option){
  var selectedOption = $option.data('color');
  // If the option returns true, or that it doesn't exist yet
  if(modifyOptions(selectedOption, selectedOptions)){
    $option.addClass('active');
  } else {
    $option.removeClass('active');
  }
  // Recreate our current selected options
  displayOptions(selectedOptions, $selectedOptions);
}

/* Searches array to see if the option exists. Returns true or false and either pushes or splices the option from the array */
function modifyOptions(option){
  var optionPosition = jQuery.inArray(option, selectedOptions);
  if(optionPosition == -1){
    selectedOptions.push(option);
    return true;
  } else {
    selectedOptions.splice(optionPosition, 1);
    return false;
  }
}

/* Displays all currently selected options that exist in our array */
function displayOptions(selectedOptions, $container){
  $container.empty();
  $.each(selectedOptions, function(option, value){
    var $optionTile = $('<div class="optionActive">');
    $optionTile.data('color', value)
                          .text(option + ":" + value)
                .css('background', value);
    $container.append($optionTile);
  });
}

So, to summarize, I want some some way to remove the .active class from the main div equivalent element when the item from the second div is clicked.

I tried removing the class active by searching for any elements with the data-color=data-color of the selected item, but I couldn't get that to work.

ex:

$('*[data-color="' + $(this).data('color') + '"]').removeClass('active');

I would really like some data approach to this, such as removing the class active if it had data-color="yellow" for instance.

Playground: https://jsfiddle.net/c75xcLha/

EDIT:

Both Are Selected, working as designed: both selected, both displayed

Clicked Yellow Div. Yellow Button is still active: yellow div clicked on (removed), but the option button remains active

Should remove the active class from the button when the yellow div OR the button is pressed, as shown here: proper way it should display after clicking yellow div


Solution

  • You are assigning data-* property using .data(PROP), not attribute hence element having data-* property could not be accessed/selected using attribute-selector, assign attribute using .attr('data-color') instead of .data(property)


    Attribute Equals Selector [name=”value”], Selects elements that have the specified attribute with a value exactly equal to a certain value.

    .data( key, value ), Store arbitrary data associated with the matched elements.

    When you use .data() to update a data value, it is updating internal object managed by jQuery, so it will not be updated in the data-* attribute[Ref]


    // Simple simulated AJAX data
    var ourData = {
      "color1": "yellow",
      "color2": "blue"
    };
    var $options = $('#options');
    var $selectedOptions = $('#selectedOptions');
    // Array to keep track of which objects are selected
    var selectedOptions = [];
    
    // Makes the initial option dynamic list
    makeOptions(ourData, $options);
    
    // If an option from the main div is clicked, handle class changing
    $('button').on('click', function() {
      pickOption($(this));
    });
    
    /* This function is the problem. The option gets removed from the array, and from the secondary div, but the class of active still occurs on the main div. */
    $selectedOptions.on('click', '.optionActive', function() {
      var option = $(this).data('color');
      var optionPosition = jQuery.inArray(option, selectedOptions);
      selectedOptions.splice(optionPosition, 1);
      $('[data-color="' + $(this).data('color') + '"]').removeClass('active');
      displayOptions(selectedOptions, $selectedOptions);
    });
    
    // Creates initial icons (buttons in this case) to the DOM and applies a data-attribute for the color
    function makeOptions(options, $container) {
      var $results = $('<div id="results">');
      $.each(options, function(key, value) {
        var $optionButton = $('<button>' + key + ':' + value + '</button>');
        $optionButton.attr('data-color', value);
        //___________^^^^^^^^^^^^^^^^^^Little trick here!
        $results.append($optionButton);
      });
      $container.append($results);
    }
    
    /* Handler for selecting an option from the Main Div. Handling the class active.
    I am not using a simple classToggle because there are many situations where a click is not allowed */
    function pickOption($option) {
      var selectedOption = $option.data('color');
      // If the option returns true, or that it doesn't exist yet
      if (modifyOptions(selectedOption, selectedOptions)) {
        $option.addClass('active');
      } else {
        $option.removeClass('active');
      }
    
      // Recreate our current selected options
      displayOptions(selectedOptions, $selectedOptions);
    }
    
    /* Searches array to see if the option exists. Returns true or false and either pushes or splices the option from the array */
    function modifyOptions(option) {
      var optionPosition = jQuery.inArray(option, selectedOptions);
      if (optionPosition == -1) {
        selectedOptions.push(option);
        return true;
      } else {
        selectedOptions.splice(optionPosition, 1);
        return false;
      }
    }
    
    /* Displays all currently selected options that exist in our array */
    function displayOptions(selectedOptions, $container) {
      $container.empty();
      $.each(selectedOptions, function(option, value) {
        var $optionTile = $('<div class="optionActive">');
        $optionTile.data('color', value)
          .text(option + ":" + value)
          .css('background', value);
        $container.append($optionTile);
      });
    }
    .active {
      background: #CCF;
    }
    .optionActive {
      min-width: 100px;
      min-height: 100px;
      display: inline-block;
      margin: 1em;
      background: #eee;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <div id="options"></div>
    <div id="selectedOptions"></div>

    Updated Fiddle

    Edit: If you still want to stick with .data method, use .filter to select target element.

    $('button').filter(function(){
      return $(this).data('color')=='yellow';
    }).removeClass('active');