BO
466 10
Asked
Updated
Viewed
7.8k times

I have the following comment structure for an app I'm creating

<div id="Username-13" class="comment">
    <div class="comment-body body drop-shadow">
        <div class="comment-avatar">
            <img src="http://portfolio.test/api/avatar/Username" alt="Username">
        </div>
        <div class="comment-card">
            <div class="comment-header post-info">
                Commented by: <em class="p-rel"><a href="#" class="stretched-link">Username</a></em> on <em>February 8,
                    2024</em>
            </div>
            <div class="comment-content">
                Yeah, you would say that
            </div>
            <div class="comment-actions">
                <div class="left-actions">
                    <ul>
                        <li>
                            <a href="#" data-comment-id="13" class="voteUp">▲</a>
                        </li>
                        <li>
                            <a href="#" data-comment-id="13" class="voteDown">▼</a>
                        </li>
                    </ul>
                    <div class="comment-vote" id="13">0</div>
                </div>
                <div class="right-actions">
                    <ul>
                        <li><a href="#">Delete</a></li>
                        <li><a href="#">Edit</a></li>
                        <li><a href="#">Flag</a></li>
                        <li><a href="#" class="reply-link" data-comment-id="Username-13" data-id="13">Reply</a></li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>

and I have the following AJAX call to cast a vote

$(document).on('click', '.voteUp', function(e) {
    e.preventDefault();
    var resourceId = $(this).data('comment-id');
    var resourceType = 'comment';
    castVote(resourceId, resourceType, 'upvote');
});

$(document).on('click', '.voteDown', function(e) {
    e.preventDefault();
    var resourceId = $(this).data('comment-id');
    var resourceType = 'comment';
    castVote(resourceId, resourceType, 'downvote');
});

function castVote(resourceId, resourceType, voteType) {
    var csrf_token = $('meta[name="csrf-token"]').attr('content');

    $.ajax({
        url: `{{ route('cast.vote') }}`,
        method: 'POST',
        headers: {
            'X-CSRF-TOKEN': csrf_token
        },
        data: {
            _token: csrf_token,
            resource_id: resourceId,
            resource_type: resourceType,
            vote_type: voteType
        },
        success: function(response) {
            updateVoteCount(resourceId, response.total_votes, response.vote_action);
        },
        error: function(xhr, status, error) {
            console.error('Error casting vote:', error);
        }
    });
}

function updateVoteCount(resourceId, totalVotes, voteAction) {
    var commentSelector = $('#' + resourceId);
    var voteUpLink = commentSelector.closest('.comment').find('.voteUp');
    var voteDownLink = commentSelector.closest('.comment').find('.voteDown');
    
    if (voteAction == 'reset') {
        // Reset the vote arrows to links
        voteUpLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteUp">▲</a>');
        voteDownLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteDown">▼</a>');
    } else if (voteAction == 'upvote') {
        // Toggle upvote arrow to span, and downvote arrow to link
        voteUpLink.replaceWith('<span class="comment-voted">▲</span>');
        voteDownLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteDown">▼</a>');
    } else if (voteAction == 'downvote') {
        // Toggle downvote arrow to span, and upvote arrow to link
        voteUpLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteUp">▲</a>');
        voteDownLink.replaceWith('<span class="comment-voted">▼</span>');
    }

    // Update the vote count
    commentSelector.text(totalVotes);
}

The problem lies with the updateVoteCount() function. The voteAction gets set in the controller which casts the vote to the database and returns the action that it essentially performed.

The conditions for upvote and downvote gets run properly but the condition for reset doesn't. I've put a console.log('some text'); in the reset condition and it prints to the console at the appropriate time but the replacements don't happen.

I don't know how to fix that, being struggling with it for a few hours now.

add a comment
0

1 Answer

  • Votes
  • Oldest
  • Latest
BO
466 10
Answered
Updated

Leave it to me to struggle for hours on a problem, post it on Ozzu and then solve it within minutes afterwards. The selecting logic for the links wasn't selecting the spans, because the condition was different. Changing the comment structure to:

<div id="Username-13" class="comment">
    <div class="comment-body body drop-shadow">
        <div class="comment-avatar">
            <img src="http://portfolio.test/api/avatar/Username" alt="Username">
        </div>
        <div class="comment-card">
            <div class="comment-header post-info">
                Commented by: <em class="p-rel"><a href="#" class="stretched-link">Username</a></em> on <em>February 8,
                    2024</em>
            </div>
            <div class="comment-content">
                Yeah, you would say that
            </div>
            <div class="comment-actions">
                <div class="left-actions">
                    <ul>
                        <li>
                            <span data-comment-id="13" class="comment-voted voteUpSpan">▲</span>
                        </li>
                        <li>
                            <a href="#" data-comment-id="13" class="voteDown">▼</a>
                        </li>
                    </ul>
                    <div class="comment-vote" id="13">1</div>
                </div>
                <div class="right-actions">
                    <ul>
                        <li><a href="#">Delete</a></li>
                        <li><a href="#">Edit</a></li>
                        <li><a href="#">Flag</a></li>
                        <li><a href="#" class="reply-link" data-comment-id="Username-13" data-id="13">Reply</a></li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>

And changing the javascript to

function updateVoteCount(resourceId, totalVotes, voteAction) {
    var commentSelector = $('#' + resourceId);
    var voteUpLink = commentSelector.closest('.comment').find('.voteUp, .voteUpSpan').filter(':first');
    var voteDownLink = commentSelector.closest('.comment').find('.voteDown, .voteDownSpan').filter(':first');
    
    if (voteAction === 'reset') {
        // Reset the vote arrows to links
        voteUpLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteUp">▲</a>');
        voteDownLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteDown">▼</a>');
    } else if (voteAction === 'upvote') {
        // Toggle upvote arrow to span, and downvote arrow to link
        voteUpLink.replaceWith('<span data-comment-id="' + resourceId + '" class="comment-voted voteUpSpan">▲</span>');
        voteDownLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteDown">▼</a>');
    } else if (voteAction === 'downvote') {
        // Toggle downvote arrow to span, and upvote arrow to link
        voteUpLink.replaceWith('<a href="#" data-comment-id="' + resourceId + '" class="voteUp">▲</a>');
        voteDownLink.replaceWith('<span data-comment-id="' + resourceId + '" class="comment-voted voteDownSpan">▼</span>');
    }

    // Update the vote count
    commentSelector.text(totalVotes);
}

Fixed my issue.

I couldn't use the same class for the spans that the links use because of the click events being added to those classes trigger the vote ajax call.

  • 0
    Unfortunately this didn't solve the problem entirely after all. The jQuery closest selector selects all the child comments that are nested to the parent being voted and updates their voting links as well, so voting on any child comments later on just casts/updates a vote on the comment that was voted originally — Bogey
  • 1
    I fixed the issue and this answer was updated. Changed the voteUpLink and voteDownLink selectors. — Bogey
add a comment
0