The below suggested changes are now live, the bug is fixed
The bug, as expected, is in stackoverflow\content\js\realtime-se.js
(included in the full.en.js
bundle). Specifically, the problem is in the function postEdit()
, which is called when a realtime WebSocket post-edit
event is received:
function postEdit(post) {
var div = $('.question[data-questionid=' + post.id + '], .answer[data-answerid=' + post.id + ']');
var notificationDiv = $('<div class="new-post-activity" data-postid="' + post.id + '>').append($('<a href="#">').text("an edit has been made to this post; click to load"));
if (div.find('.new-post-activity[data-postid=' + post.id + ']').length > 0) return;
var func = function () {
$('.new-post-activity[data-postid=' + post.id + ']').remove();
if ($('#review-content').length == 0) {
$.get("/posts/ajax-load-realtime/{postIdsSemiColonDelimited}?title=true".formatUnicorn({ postIdsSemiColonDelimited: post.id }), refreshPost(div.find('.postcell, .answercell'), post));
}
$(document).trigger('refreshEdit', post.id);
};
div.click(func);
div.prepend(notificationDiv.find('a').click(function (e) {
e.stopPropagation();
func();
return false;
}));
}
The line div.click(func);
sets up a click handler on the entire post div
that reloads the post content using $.get()
. Now, one might naïvely expect that reloading the entire post would also wipe out the click handler as a side effect, but this doesn't happen, because the callback function returned by refreshPost()
doesn't actually replace the entire post div with the Ajax result, but rather just cherry-picks specific elements from the Ajax result to replace.
Earlier, where the click handler was only set on the "click to load" banner itself, the line:
$('.new-post-activity[data-postid=' + post.id + ']').remove();
inside the func
callback worked fine to remove the entire banner. Now that the click handler is also added to the entire post div, however, that's not sufficient. A simple fix would be to add the following line just after the previously quoted one:
div.off('click', func);
This should take care of removing the click handler once it's no longer needed.
Ps. That same function contains a number of other bugs, too:
The notificationDiv
creation fails, because the HTML is missing a double quote: the expression post.id + '>'
should read post.id + '">'
. Thus, the notification about new edits is never actually shown to the user.
Fixing the previous bug would reveal a new one — the notification box is not correctly prepended to the post, causing it to be mis-styled and not to get removed when clicked. To fix this, the expression:
div.prepend(notificationDiv.find('a').click(function (e) { ... }));
at the end of the code should be replaced with:
notificationDiv.prependTo(div).find('a').click(function (e) { ... });
It might also be desirable to only remove the notification and the click handler when the Ajax call completes successfully, rather than when it's started. Doing this would require slightly more extensive changes to the code, though.