-2

i have a lot of jQuery Event listeners that initialize when the document finishes loading. that part is working fine. I have a function that makes ajax calls and gets responses as strings of HTML that I append with jQuery to parts of the DOM. Although the append works, THAT new HTML snippet of code doesn't work with the jQuery event listeners because it wasn't part of the DOM when it loaded. Is there a way to reinitialize jQuery event listeners?

Here is another thing: I tried to store the event listener(s) in a function (function a) and call that function whenever I wanted the listeners reinitialized. That worked partially... Although the listeners responded to to new HTML snippets, the results of the listeners were doubled, and keeps doubling whenever function a() is called, resulting in a major bug.

I'm Mixing jQuery and Angular. Here is a snippet:

$(document).ready(function(){
     $('.like-btn').click(function(){

      var likeStatus = $(this).data('like-status-json');
      var contentType = $(this).data('content-type');
      var contentID = $(this).data('content-id');
      var likeMeter_id = '#' + contentType.toLowerCase() + '-' + 'likemeter' + '-' + contentID;
      var likeMeter_elm = $(likeMeter_id);

      // console.log( likeStatus, contentType, $(likeMeter_elm) );

      var obj = {
        likeStatus: likeStatus,
        contentType: contentType,
        contentID: contentID,
        likeMeter_elm: likeMeter_elm,
        likes: parseInt( $(likeMeter_elm).text() ),
        og_elm: $(this)
      }

      $scope.likeAction(obj);

    });
}); 

$scope.likeAction = function(dataObj) {
    // console.log(dataObj);

    var req = {
      method: 'POST',
      url: '/action/ajax/',
      headers: {
        'Content-Type': 'application/json',
        'responseType': 'json',
        "Accept" : "application/json",
        'X-CSRFToken': Cookies.get('csrftoken')
      },
      data: {
        action: dataObj.likeStatus.action,
        info: dataObj,
        csrfmiddlewaretoken: Cookies.get('csrftoken'),
      }
    }

    $http(req).then(function(resp){
      // Success Callback
      // console.log(resp);
      $(dataObj.og_elm).data('like-status-json', resp.data.likeStatus);
      $(dataObj.og_elm).removeClass(dataObj.likeStatus.class).addClass(resp.data.likeStatus.class);
      $(dataObj.og_elm).children('span.like-text').text(resp.data.likeStatus.text);
      $(dataObj.likeMeter_elm).text(resp.data.likeMeter);
    },
    function(resp){
      // Error Callback
      // console.log(resp);
    });
  }

Here is the ajax that appends new HTML snippets:

$scope.addPostCommentUser = function(inputELM, dataObj) {
    if( inputELM == undefined || dataObj == undefined ) {
      console.log('Missing Inputs...');
      return;
    }

    console.log(dataObj);
    // return;

    var req = {
      method: 'POST',
      url: '/action/ajax/',
      headers: {
        'Content-Type': 'application/json',
        'responseType': 'json',
        "Accept" : "application/json",
        'X-CSRFToken': Cookies.get('csrftoken')
      },
      data: {
        action: 'addPostCommentUser',
        info: dataObj,
        csrfmiddlewaretoken: Cookies.get('csrftoken'),
      }
    }

    $http(req).then(function(resp){
      // Success Callback
      console.log(resp);
      var id = '#cmlst-' + dataObj.post_id;
      $(id).append(resp.data.comment_html)
      $(inputELM).val('');


    },
    function(resp){
      // Error Callback
      console.log(resp);
    });
  }
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
ryanwaite28
  • 1,804
  • 2
  • 24
  • 40

3 Answers3

0

You need to use the on-call, instead of click and others - that should solve it.

So instead of $('.like-btn').click(function() you would do $('.like-btn').on('click',function()

I'm not a 100% sure it will work with the mix of functions and Angular you have, but try it and see.

junkfoodjunkie
  • 3,168
  • 1
  • 19
  • 33
  • So it didn't work, but thanks for trying? it turns out that jQuery listeners will only listen to elements that exists at the time the script were read/loaded. However i did find a way around this! – ryanwaite28 Dec 11 '16 at 17:11
  • i determined the problem was that the new HTML wasn't binded to the jQuery selectors because they didn't exist at the time the listeners were initialized. The code for selecting the elements are correct without a doubt(they were rendered by python jinja template engine). i fixed/solved the problem by creating a function that binds a given DOM element to a jQuery listener. so whenever i get HTML as a string from AJAX, i create a new DOM element and pass it to that function. it works perfectly. – ryanwaite28 Dec 13 '16 at 13:46
0

Attach the event to the parent element of the dynamically created elements using event delegation. See .on()

$("[id|=cmlst]")
.on("click"
, "css selector of element within `resp.data.comment_html` at `.then()`"
, clickHandlerFunctionReference);
guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    or `$(document).on('click', '.like-btn', function(){ /*...*/ })` – Thomas Dec 11 '16 at 03:34
  • So it didn't work, but thanks for trying? it turns out that jQuery listeners will only listen to elements that exists at the time the script were read/loaded. However i did find a way around this! – ryanwaite28 Dec 11 '16 at 17:11
  • 1
    @ryanwaite28 this answer specifically solves that problem. If it didn't work for you, you didn't use it correctly. – Kevin B Dec 12 '16 at 19:30
  • @KevinB OP uses different selectors at Question/Answer, see `var id = '#cmlst-' + dataObj.post_id;` at original Question , `var id = '#rplst-' + dataObj.comment_id;` at OP's Answer. `'#cmlst-'` is not within text at OP's Answer. – guest271314 Dec 12 '16 at 19:38
  • 1
    My assumption is angular is mangling the parent element you're binding the delegated event to. We would have to know a lot more about the way angular is attached and used in the page to know what parent we can use for binding (aside from `document` of course) – Kevin B Dec 12 '16 at 19:38
  • @KevinB Have not tried angularjs, though if OP was targeting incorrect selector, would explain "it didn't work" communication by OP `'#cmlst-' !== '#rplst-'`. Balanced work indicating to OP that `javascript` at Answer should resolve issue in concurrence with "duplicate" mark. – guest271314 Dec 12 '16 at 19:58
  • i determined the problem was that the new HTML wasn't binded to the jQuery selectors because they didn't exist at the time the listeners were initialized. The code for selecting the elements are correct without a doubt(they were rendered by python jinja template engine). i fixed/solved the problem by creating a function that binds a given DOM element to a jQuery listener. so whenever i get HTML as a string from AJAX, i create a new DOM element and pass it to that function. it works perfectly. – ryanwaite28 Dec 13 '16 at 13:42
  • @ryanwaite28 _"i determined the problem was that the new HTML wasn't binded to the jQuery selectors because they didn't exist at the time the listeners were initialized."_ Yes. The `javascript` at Answer addresses that case. The listener is attached to the parent element of the dynamically created elements that are appended to the `document`. If element having `id` beginning with `"cmlst"` is does not exist, you can use `$("body").on("click", ".like-btn", function(e) {//do stuff})` which should only need to be called once, not at every ajax call. See comment by Thomas. – guest271314 Dec 13 '16 at 16:29
0

i actually found my own way of solving this problem.

I created a functions that applies a jQuery event listener to the element(s) from the given arguments/input parameters. So whenever i get HTML strings from server-side AJAX, i use jQuery to create a new DOM element and pass it to that function! It works perfectly actually!!!

Here is the example:

$scope.applyNewLikeListeners = function(elm) {
    $(elm).find('.like-btn').on('click', function(){
      var likeStatus = $(this).data('like-status-json');
      var contentType = $(this).data('content-type');
      var contentID = $(this).data('content-id');
      var likeMeter_id = '#' + contentType.toLowerCase() + '-' + 'likemeter' + '-' + contentID;
      var likeMeter_elm = $(likeMeter_id);

      var obj = {
        likeStatus: likeStatus,
        contentType: contentType,
        contentID: contentID,
        likeMeter_elm: likeMeter_elm,
        likes: parseInt( $(likeMeter_elm).text() ),
        og_elm: $(this)
      }

      $scope.likeAction(obj);
    });
  }

It works!

$scope.addCommentReplyUser = function(inputELM, dataObj) {
    if( inputELM == undefined || dataObj == undefined ) {
      console.log('Missing Inputs...');
      return;
    }

    console.log(dataObj);
    // return;

    var req = {
      method: 'POST',
      url: '/action/ajax/',
      headers: {
        'Content-Type': 'application/json',
        'responseType': 'json',
        "Accept" : "application/json",
        'X-CSRFToken': Cookies.get('csrftoken')
      },
      data: {
        action: 'addCommentReplyUser',
        info: dataObj,
        csrfmiddlewaretoken: Cookies.get('csrftoken'),
      }
    }

    $http(req).then(function(resp){
      // Success Callback
      console.log(resp);
      var id = '#rplst-' + dataObj.comment_id;
      var elm = $(resp.data.reply_html)
      $(id).append(elm)
      $(inputELM).val('');
      $(dataObj.replyMeter_elm).text(resp.data.replyMeter);
      $scope.applyNewLikeListeners(elm);

    },
    function(resp){
      // Error Callback
      console.log(resp);
    });
  }
ryanwaite28
  • 1,804
  • 2
  • 24
  • 40