0

I am working on the below demo, and would like to enable the code to fire event while the controls added dynamically to the page.

To test this please click on Add Controlsbutton to add check-boxes to the document. Now the shuffle js is not listening to the check box changes!

A running Pen demo is also at here

'use strict';
var Shuffle = window.shuffle;

function arrayIncludes(array, value) {
  return array.indexOf(value) !== -1;
}


function toArray(thing) {
  return Array.prototype.slice.call(thing);
}

var Demo = function (element) {
  this.shapes = toArray(document.querySelectorAll('.js-shapes input'));

  this.shuffle = new Shuffle(element, {
    easing: 'cubic-bezier(0.165, 0.840, 0.440, 1.000)', // easeOutQuart
    sizer: '.the-sizer',
  });

  this.filters = {
    shapes: [],
  };

  this._bindEventListeners();
};

/**
 * Bind event listeners for when the filters change.
 */
Demo.prototype._bindEventListeners = function () {
  this._onShapeChange = this._handleShapeChange.bind(this);

  this.shapes.forEach(function (input) {
    input.addEventListener('change', this._onShapeChange);
  }, this);


};

/**
 * Get the values of each checked input.
 * @return {Array.<string>}
 */
Demo.prototype._getCurrentShapeFilters = function () {
  return this.shapes.filter(function (input) {
    return input.checked;
  }).map(function (input) {
    return input.value;
  });
};


/**
 * A shape input check state changed, update the current filters and filte.r
 */
Demo.prototype._handleShapeChange = function () {
  this.filters.shapes = this._getCurrentShapeFilters();
  this.filter();
};


/**
 * Filter shuffle based on the current state of filters.
 */
Demo.prototype.filter = function () {
  if (this.hasActiveFilters()) {
    this.shuffle.filter(this.itemPassesFilters.bind(this));
  } else {
    this.shuffle.filter(Shuffle.ALL_ITEMS);
  }
};

/**
 * If any of the arrays in the `filters` property have a length of more than zero,
 * that means there is an active filter.
 * @return {boolean}
 */
Demo.prototype.hasActiveFilters = function () {
  return Object.keys(this.filters).some(function (key) {
    return this.filters[key].length > 0;
  }, this);
};

/**
 * Determine whether an element passes the current filters.
 * @param {Element} element Element to test.
 * @return {boolean} Whether it satisfies all current filters.
 */
Demo.prototype.itemPassesFilters = function (element) {
  var shapes = this.filters.shapes;
  var shape = element.getAttribute('data-shape');


  // If there are active shape filters and this shape is not in that array.
  if (shapes.length > 0 && !arrayIncludes(shapes, shape)) {
    return false;
  }

  return true;
};

document.addEventListener('DOMContentLoaded', function () {
  window.demo = new Demo(document.querySelector('.js-shuffle'));
});

$("#add-controls").on("click", function(){
  $("#controls").html('<div class="col-6@sm"> <div class="filter-group filter-group--compound js-shapes"><h5 class="filter-group__label filter-group__label--compound">Shapes</h5><span class="ib"><input type="checkbox" value="circle" id="cb-circle"> <label for="cb-circle">Circle</label></span><span class="ib"><input type="checkbox" value="diamond" id="cb-diamond"> <label for="cb-diamond">Diamond</label></span><span class="ib"><input type="checkbox" value="square" id="cb-square"> <label for="cb-square">Square</label></span><span class="ib"><input type="checkbox" value="triangle" id="cb-triangle"> <label for="cb-triangle">Triangle</label></span> </div></div> </div> ')
});
.shape-shuffle-container {
  position: relative;
  overflow: hidden; }

.shape {
  position: relative;
  margin-left: 0;
  margin-top: 10px; }
  .shape .shape__space {
    width: 100%;
    height: 100%;
    background-color: black;
    border-style: solid;
    border-width: 0;
    border-color: transparent; }

.shape--blue .shape__space {
  background-color: #3498DB;
  border-bottom-color: #3498DB; }

.shape--red .shape__space {
  background-color: #E74C3C;
  border-bottom-color: #E74C3C; }

.shape--orange .shape__space {
  background-color: #F39C12;
  border-bottom-color: #F39C12; }

.shape--green .shape__space {
  background-color: #2ECC71;
  border-bottom-color: #2ECC71; }

.shape--circle .shape__space {
  border-radius: 50%; }

.shape--diamond .shape__space {
  -webkit-transform: rotate(45deg) scale(0.70711);
          transform: rotate(45deg) scale(0.70711); }

.shape--triangle .shape__space {
  padding-top: 9px;
  height: 0;
  width: 0;
  border-width: 0 66px 114px 66px;
  background-color: transparent;
  margin: auto; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" integrity="sha256-l85OmPOjvil/SOvVt3HnSSjzF1TUMyT9eV0c2BzEGzU=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/Shuffle/4.0.0/shuffle.js" ></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>

<section class="container">
<button id="add-controls"> Add Controls</button>
 </section> 
<section class="container">
<div id="controls"></div>
</section>
<section class="container">
  <div class="row">
    <div class="shape-shuffle-container js-shuffle">
      
      <div class="col-3@xs col-3@sm shape shape--circle shape--blue" data-shape="circle" data-color="blue" data-size="20">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--diamond shape--red" data-shape="diamond" data-color="red">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--triangle shape--green" data-shape="triangle" data-color="green">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--triangle shape--orange" data-shape="triangle" data-color="orange">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--square shape--red" data-shape="square" data-color="red">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--diamond shape--green" data-shape="diamond" data-color="green">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--circle shape--red" data-shape="circle" data-color="red">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--square shape--green" data-shape="square" data-color="green">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--circle shape--orange" data-shape="circle" data-color="orange">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--diamond shape--blue" data-shape="diamond" data-color="blue">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--square shape--orange" data-shape="square" data-color="orange">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="col-3@xs col-3@sm shape shape--square shape--blue" data-shape="square" data-color="blue">
        <div class="aspect">
          <div class="aspect__inner">
            <div class="shape__space"></div>
          </div>
        </div>
      </div>
      
      <div class="the-sizer col-1@xs col-1@sm"></div>
    </div>
  </div>

</section>
halfer
  • 19,824
  • 17
  • 99
  • 186
Behseini
  • 6,066
  • 23
  • 78
  • 125
  • Does this answer your question? [Event binding on dynamically created elements?](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) – VLAZ Apr 06 '20 at 23:05
  • I already tried this by adding ` document.addEventListener('change', this._onShapeChange);` instead of ` input.addEventListener('change', this._onShapeChange);` but it didn't work – Behseini Apr 06 '20 at 23:23
  • What do you mean by "it doesn't work"? What's happening and what is supposed to be happening? – VLAZ Apr 06 '20 at 23:33
  • well, it is not fireing any event! – Behseini Apr 07 '20 at 00:05
  • Please take a look at Pen demo , will noticewhat I mean – Behseini Apr 07 '20 at 00:12

1 Answers1

1

The issue is that the event listeners are bound to non-existent elements when demo is initialized. The constructor sets its shape and color properties to null because .js-shapes and and .js-colors don't exist yet, so the event listeners aren't being bound to anything.

To fix it, you can set the shapes, colors, and bind the event listeners after the controls are dynamically added.

Here's a forked version of your pen: https://codepen.io/ethan-marsh/pen/abOeEbK

Only changes are moving shape, color initialization and the event listener binding into a new function and called it after adding the controls html.

Another option is to simply move setting window.demo = new Demo(...) from when the document is loaded to right after you add the controls html.

A third option is to leave the controls html originally on the page but hide the controls with css (set style to display:none) and making the add controls button change the visibility: eg $("#add-controls").on("click", function(){ document.getElementById('#controls').style="display: block" }).

marsheth
  • 822
  • 6
  • 9