0

I'm receiving ajax content using getData() as

$('document').ready(function () {
    getData();
});

In the getDate, elements are dynamically added based on JSON response from server. The elements are added with the following code which is referred as CodeAddingHTMLFromAbove in the below code

            $("#list").append('<div class="ui card fluid" id="' + post._id + '"><div class="content"><i class="right floated star icon textWhite"></i><div class="header textWhite">' + post.title + '</div><div class="description"><p class="textWhite">' + post.description + '</p></div></div><div class="extra content"><span class="left floated like textWhite"><i class="like icon textWhite likeIcon"></i><span id="upvotes">' + post.upvotes + '</span></span><span class="right floated textWhite"><i class="comments icon textWhite"></i>Comments</span></div></div>');

For better visibility, here is the HTML being appended

<div class="ui card fluid" id="' + post._id + '">
    <div class="content">
        <i class="right floated star icon textWhite"></i>
        <div class="header textWhite">' + post.title + '</div>
        <div class="description">
            <p class="textWhite">' + post.description + '</p>
        </div>
    </div>
    <div class="extra content">
        <span class="left floated like textWhite">
            <i class="like icon textWhite likeIcon"></i>
            <span id="upvotes">' + post.upvotes + '</span>
        </span>
            <span class="right floated textWhite">
            <i class="comments icon textWhite"></i>
            Comments
        </span>
    </div>
</div>

After each instance of the above statement, onClick listeners are added as

function getData() {
    var data = {
        sortMethod: sortBy,
        lastPost: lastPost
    };
    // process the form
        $.ajax({
            type: 'POST',
            url: '/home/posts',
            data: data,
            dataType: 'json',
            encode: true
        })
        .done(function (data) {
            console.log(data);
            data.posts.forEach(function (post) {
                **CodeAddingHTMLFromAbove**
                $("#" + post._id).on("click", ".likeIcon", function () {
                    event.stopPropagation();
                    if ($(this).hasClass("liked"))
                        likeType = false;
                    else
                        likeType = true;
                    console.log(likeType);
                    // like function adds or removes class "liked" from the $this element based on likeType value
                    like(this, likeType);
                    // sendLike has AJAX call which sends and verifies like on server
                    sendLike(likeType, $(this).parent().parent().parent().attr("id"), this);
                });
                // lastPost acts as a marker for next content to get
                lastPost = data.posts[data.posts.length - 1]._id;
                // when inProcess is true, new content is not requested
                inProcess = false;
            }
        }
}

The content using getData() is always added as expected. Here is the problem though, on random page refreshes , the like on click works as it should and executes the functions as expected. But most times when I refresh, the on click does not execute the like() and sendLike() functions though console.log() executes weirdly printing out two true or two false.

TL;DR: On click listeners on dynamically added content randomly only work on random page refreshes and on rest, all the on click function calls aren't executed but the ones which are executed are executed twice.

**UPDATE: I'm using the following function to get more data on page scroll **

// on scroll get new data
$(document).scroll(function (e) {
    // grab the scroll amount and the window height
    var scrollAmount = $(window).scrollTop();
    var documentHeight = $(document).height();
    if ((documentHeight - scrollAmount < 1000) && !inProcess && !reachedLastPost) {
        inProcess = true;
        getData();
    }
});
Suhail Gulzar
  • 179
  • 10
  • 1
    It sounds like you have a race condition between adding the content to the DOM and adding the event handlers. You've not really given enough code for us to diagnose the issue for certain. That being said, you can make this much simpler and avoid any possible race conditions by using a single delegated event handler. – Rory McCrossan Sep 11 '17 at 15:19
  • Remove the `.likeIcon` selector, juste do : $("#" + post._id).on("click", function () { Otherwise the handler will be added several times. > Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. http://api.jquery.com/on/ – KitAndKat Sep 11 '17 at 15:23
  • @KitAndKat only need on click on .likeIcon not on all of the div – Suhail Gulzar Sep 11 '17 at 15:54
  • @RoryMcCrossan added more code. Hope it clarifies the issue. – Suhail Gulzar Sep 11 '17 at 15:55
  • so just do $(".likeIcon").on("click", function () { .. so that you don't attach forever. When you specify a selector, it attaches forever, even for elements that don't exist in the dom yet, that's why you see it twice or more, it's normal.. – KitAndKat Sep 11 '17 at 16:34

1 Answers1

1

What if you added the event delegation from each posts to their container? You'd add only one event, and would make sure the event is handled on any element that should...

$("#" + post._id).on("click", ".likeIcon", function () {
//To
$("#list").on("click", ".likeIcon", function () {

And add it on page load, once.

Salketer
  • 14,263
  • 2
  • 30
  • 58