-4

I'm trying to figure how I can use the each() method to loop through dynamic elements. The method does not seem to be working if the element is not present. I do not wish to use setTimeout either and would prefer something that would not delay the method or event.

So far all of the research and searching I've seen is using an event handler to trigger on dynamic objects.

$('.task').each(function() {

        let that = this;
        // let startTask = $('.start-task', that);
        let mc = new Hammer(this);
        mc.on("panright", function(e){

            if(e.deltaX < 100 ) {
                $(that).css('transform', 'translateX(' + e.deltaX + 'px' + ')');
                $(that).addClass('pan-task');
            } else {
                $(that).css('transform', 'translateX(100px)');
                $('.start-task', that).trigger('click');
            }
        });

        mc.on('panend', function(e){
            $(that).css('transform', 'translateX(' + '0' + 'px' + ')');
            $(that).removeClass('pan-task');

        });
    });
Rojo
  • 2,749
  • 1
  • 13
  • 34
clestcruz
  • 1,081
  • 3
  • 31
  • 75

4 Answers4

2

This will work with dynamic elements. It just depends when you run it. First you do what you have copied here but you isolate what is inside the “each” in a function. Then when you create a new task, you run the same function as a callback.

Really do a function where the task is the argument. Otherwise you risk attaching the same event multiple times.

function initTaskEvents(task) {
  let mc = new Hammer(task.get(0));
  mc.on("panright", function(e){
    if (e.deltaX < 100 ) {
      task.css('transform', 'translateX(' + e.deltaX + 'px' + ')');
      task.addClass('pan-task');
    } else {
      task.css('transform', 'translateX(100px)');
      $('.start-task', task).trigger('click');
    }
  });

  mc.on('panend', function(e){
    task.css('transform', 'translateX(' + '0' + 'px' + ')');
    task.removeClass('pan-task');
  });
}

As you see the argument is assumed to be already a jQuery object. This is why I used .get(0) for your constructor. Remove it if it accepts jQuery objects. By the way, it is not needed anymore, but avoid creating the same jQuery object $(that) many times because $ is actually an expensive function. You want to save it in a variable if possible and reuse it.

Then you use it on existing tasks if you have tasks when loading the page already.

$(function() { // When ready
  $('.task').each(function() {
    initTaskEvents($(this));
  });
});

So far it does the same as what you already had. But then when you insert a new task in the DOM, you run it through the function as well.

// ...
tasklist.append(myNewTask);
initTaskEvents(myNewTask);
// ...

Does that make sense? It is the common way to do it.

Now if you only attach events (not your case since you are creating Hammer in the middle), you can use this in jQuery.

$('body').on('.task', 'click', function(event) {
  // ...
});

It works on things which don't exist yet because the event is attached to body and delegates. I have seen this mentioned in the comments, but again this does not work in your case because you are creating a Hammer object from your task.

Mig
  • 662
  • 4
  • 13
  • Ok, so basically what you're telling me is to insert the code inside of a function. Instead of initially loading the script – clestcruz Sep 15 '19 at 07:48
1

You can achieve this behavior with MutationObserver
as the name implies, it is used to observe mutations on DOM elements. It's the most efficient way to watch DOM changes and the callback is invoked after the DOM updated.

const taskList = $('#task-list');
const observer = new MutationObserver((mutation, observer) => {
  // Looping only on added nodes
  mutation[0].addedNodes.forEach(node => {
    // Do something with added node
  })
})

const observerOts: MutationObserverInit = { attributes: true, childList: true, subtree: true };

observer.observe(taskList[0], observerOts);

here's a working example

Ron
  • 844
  • 7
  • 8
0

Not sure if I am understanding the problem exactly, but everytime I have to loop a list of dynamic elements I use an event delegation approach, and it looks like this is what you need here.

Basically, it consists in attaching the event listener to the parent and then use event.target to get the element you need to work with.

Here is a great explanation by David Walsh: https://davidwalsh.name/event-delegate

Oriol Grau
  • 609
  • 6
  • 8
0

from what I understand from your question is that you want to addEventsListener to elements which returned from Hammer class right? so can you post your Hammer class too ? you can try:

$(mc).on('panright', function() { ... })
Nam Tang
  • 100
  • 1
  • 10