Sometimes we wish to carry out a JavaScript action after some content has been flagged (or unflagged).
For example, we may wish to refresh a block on the page, via AJAX. This could happen if, for example, this block lists bookmarked content. We wouldn't want to have a stale block on the page.
The Flag module provides us with two "pseudo" events to which we can attach our JavaScript handlers. These events are both triggered after content has been successfully flagged (or unflagged).
The two events
- The flagGlobalAfterLinkUpdate event
- This event is triggered immediately after a flag link has been updated. (Flag links appear in two flavors: "Bookmark this!" and "Unbookmark this!", and when we speak of "update" we mean this change in appearance).
- The flagGlobalBeforeLinkUpdate event
- This event is triggered just before a flag link is updated. As will be explained later, the existence of this event is for cosmetic reasons only: it allows us to carry out actions while the throbber () is still visible.
Example
The following handler, attached to the flagGlobalAfterLinkUpdate event, will pop up an alert box with the details of the flagging operation.
$(document).bind('flagGlobalAfterLinkUpdate', function(event, data) {
alert('Object #' + data.contentId + ' (of type ' +
data.contentType + ') has been ' + data.flagStatus +
' using flag "' + data.flagName + '"');
});
Where should you put this code? In a file. And make Drupal "load" this file.
For example, Drupal 6 users could put it in a 'my_script.js' file in their theme's folder and add "scripts[] = my_script.js' to their theme's .info file. Then they should clear Drupal's cache or else Drupal's theming system won't notice this new file. For Drupal 5 users the instructions are a bit more complex: they should use a drupal_add_js() call somewhere in their 'template.php' file.
Several interesting observations about our code:
- We attach the handler to some arbitrary object (the
document
object) and not to the flag link itself. That's why we nickname this event a "global event". When content is flagged, or unflagged, the event is "broadcasted" to all listening elements. This scheme is used mainly because Drupal 5 doesn't support the new 'Drupal.behaviors' scheme. - Our handler receives two parameters: the first is the obligatory
event
object, and the second is an object containing some information about the flagging operation (You may wish to peek at the flag_page() PHP function to learn about all the available fields).
Example: Making "popup" messages vanish after 5 seconds
Avid users of the Flag module must have noticed that the "popup" messages shown after flagging content aren't removed from the page. This is a glitch that should be fixed, of course, but in the meantime here's how to fix the problem:
$(document).bind('flagGlobalAfterLinkUpdate', function(event, data) {
window.setTimeout(function() {
$('.flag-message', data.link).hide();
}, 5*1000);
});
The data
object contains a data.link
field, which points to the link's DOM element.
Example: Refreshing a block on the page, via AJAX
In this case we need to fetch a block's HTML from the server. The Flag module cannot do this. This isn't its purpose. We need to use some other module for this. I've chosen the component module for this demonstration. That module provides a ?q=component/block/MODULE/BLOCKID
URL where blocks' HTML can be fetched.
$(document).bind('flagGlobalAfterLinkUpdate', function(event, data) {
// The following line is needed for compatibility with Drupal 5,
// which doesn't have 'Drupal.settings.basePath'. Drupal 5 users
// must update the '/' to match their bash path.
var base_url = (Drupal.settings && Drupal.settings.basePath) || '/';
function blockExistsOnPage(module_name, block_id) {
var domId = 'block-' + module_name + '-' + block_id;
return $('#'+domId).length != 0;
}
function refreshBlock(module_name, block_id) {
var domId = 'block-' + module_name + '-' + block_id;
$.get(base_url + '?q=component/block/' + module_name + '/' + block_id, null, function(newHtml) {
// Replace the old block with the new one.
var domElement = $(newHtml).insertAfter('#'+domId);
$('#'+domId).remove();
if (Drupal.attachBehaviors) {
Drupal.attachBehaviors(domElement);
}
});
}
//
// Note:
//
// Change the 'flag_bookmarks-block_1' below to match
// your block's ID.
//
if (data.flagName == 'bookmarks') {
if (blockExistsOnPage('views', 'flag_bookmarks-block_1')) {
refreshBlock('views', 'flag_bookmarks-block_1');
}
}
});
Example: Refreshing a view on the page, via AJAX
You can alter the code above to work with views. But there's now a module to do exactly this: Views Flag Refresh.
The data.link field
A previous example showed that the data
object contains a data.link
field pointing to the flag link's DOM element. This makes it possible for us to learn about the context of the flagging operation. Examples:
if ($(data.link).parents('.block').length) {
// The link clicked was inside a block
}
if ($(data.link).parents('.node').length) {
// The link clicked was under a node
}
Aesthetics: the flagGlobalBeforeLinkUpdate event
When the flagGlobalAfterLinkUpdate event is triggered, the link shown on screen is the new one, and, also, the throbber is gone already. If we're now to carry out some lengthy operation --e.g. an AJAX operation-- we won't enjoy a throbber to notify the user of the wait.
The solution is to cancel the update of the link. We do this by triggering our code in the flagGlobalBeforeLinkUpdate event and in it notifying the Flag module of our wish: by setting data.preventDefault
to true
. It's now our responsibility to update the link, so this method is mainly useful in AJAX scenarios where we refresh the section of the page where the flag link appears in.
(Technical note: It's more common to prevent default actions form taking place by calling event.preventDefault()
, or by doing return false
in our handler; However, we resort to data.preventDefault = true
because the antique jQuery libraries shipped with Drupal prevent us from "doing it the right way".)
Comments
Multiple node flag links on a page
I am trying to write a js script to update all flag links on the same node.
for instance, a wishlist.
Suppose we are on a node detail of a product and it shows a flag link "add to wishlist". Clicking it, a right sidebar block updates and the node appears (views block loads via ajax on after update event) in the wishlist. This wishlist shows the remove flag link as well. So at that moment there are two flag links for the same node. I click "remove from wishlist" in de wishlist itself, meaning that block will update, removing that node from the wishlist. Problem here is that the other flag link in full node is still "remove from wishlist".
So how could we do a search or body context replace of all affected flag links?
Any ideas will be helpfull
The solution
And a link alter to add the classes
To embed the view (menu callback to return json)
Flag that hides the teaser
I'm trying to implement community-moderated content with Flag and FlagAbuse and Actions-- basically the same functionality as we see on Digg.com with "X Bury" button.
Expected behavior: you click a flag and the teaser of that node instantly vanishes as if it was never there.
I've been searching for this "instantly-vanishing" functionality for more than a year, and I can see that many people (especially with Drigg sites) would love to have it.
How can we implement such a javascript with Flag module?
...
1. Add a special CSS class to buried nodes. This page explains how.
2. In your stylesheet hide any portion you don't want shown of a "buried" node. Example:
(Naturally you don't want to hide the title or the flag links (because you want to allow people to unbury a node)).
3. Add JavaScript to hide/remove this special CSS class upon flagging/unflagging:
(I haven't checked this code. Expect typos.)
I've found a solution to my
I've found a solution to my problem, described on #963578: How to list users who flagged a comment in the body of the comment itself?. The following piece of code, placed in comment.tpl.php is working very well:
However, I would like a user, who has just flagged a comment, to be seen in the body of the comment immediately and not after reloading the whole page. If I place the above code in between, let's say,
, how can I load that division via The Flag API (JavaScript) just after flag has been clicked on?
Flag 8.x-4.x (Drupal 9+)
The
flagGlobalAfterLinkUpdate
event is no longer fired clientside, but with a patch you can call AJAX commands in a PHP event subscriber.