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>

5
  • Does this answer your question? Event binding on dynamically created elements? Commented Apr 6, 2020 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 Commented Apr 6, 2020 at 23:23
  • What do you mean by "it doesn't work"? What's happening and what is supposed to be happening? Commented Apr 6, 2020 at 23:33
  • well, it is not fireing any event! Commented Apr 7, 2020 at 0:05
  • Please take a look at Pen demo , will noticewhat I mean Commented Apr 7, 2020 at 0:12

1 Answer 1

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" }).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.