Topic on Project:Support desk

[RESOLVED] SpecialPage extension: execute JavaScript function onclick

3
Jpgill86 (talkcontribs)

I am developing a SpecialPage extension. I have a large table on my page, and I would like to provide a link at the top of the page that hides certain rows within the table. I can do this by inserting JavaScript directly into the HTML:

$this->getOutput()->addHTML(
    Html::element("a", array("href" => "#", "onclick" => "$('.my-table-row-class').hide(); return false;"), "Click to hide")
)

This is working for me. However, I would prefer to keep my JavaScript separate from my PHP (partly because I plan to make it more complex), so I would like to move this JavaScript into a function in a file that is loaded by the ResourceLoader. I already have a JavaScript file which I know is being loaded successfully (it successfully attaches jQuery UI datepickers to form input fields).

My problem is this: When I take the naive approach and just create a new JavaScript function in my resource file, I can't successfully attach it to my "Click to hide" link. The JavaScript looks like this,

function hideTableRows() {
    $('.my-table-row-class').hide();
    return false;
}

and the PHP becomes

$this->getOutput()->addHTML(
    Html::element("a", array("href" => "#", "onclick" => "hideTableRows()"), "Click to hide")
)

This isn't working for me. When I click the link, my browser's web console reports ReferenceError: hideTableRows is not defined. I am guessing that I cannot define functions in the global space like this, or that my function is automatically stored inside some other object such that I would access it as obj.hideTableRows().

I have spend the last couple hours looking for examples or documentation for this type of thing, but I can't find anything. Perhaps I've just missed it, in which case, I apologize. Any help you can provide will be greatly appreciated.

Thanks in advance!

Jack Phoenix (talkcontribs)

The JavaScript needs to go to its own file. Then you need to attach the hook handler via JavaScript (jQuery, to be exact), like this:

jQuery( document ).ready( function() {
	jQuery( 'a.some-custom-class' ).on( 'click', function() {
		// Note: you really shouldn't be polluting the global namespace with your functions.
		// Use a more object-oriented approach, i.e. create a MyExtension object and then call
		// MyExtension.hideTableRows(); here
		hideTableRows();
	} );
} );

Do NOT use any onSomething handlers in the HTML; you'll only enter a world of pain that way.

See ResourceLoader for some more documentation, take a look at a pre-existing extension for a "live example" (from "my" extensions, I'd recommend Extension:Comments ā€” it's relatively sane and easier to understand than Extension:SocialProfile ;-) and if you still can't find the answer to your question(s), ask on the #mediawiki IRC channel on irc.freenode.net; there are a lot of people online there throughout the day and I'm sure someone there can either help you out or at least direct you to the correct direction.

Jpgill86 (talkcontribs)

Jack,

Thank you so much for your extremely helpful guidance! I will be modeling my JavaScript file after the beautiful code in Extension:Comments.

I'd like to return the favor by pointing out a bug in the Comments code, and providing a fix! I've used the toggleLiveComments function as a model for my own. I found that if I click the toggle link several times (>10), the toggling takes longer and longer, roughly twice as long each time. Quickly, this brings my browser to its knees.

The problem is that each time the toggleLiveComments function is called, a new jQuery event handler is created using .on. It is appended to the list of existing event handlers. So, for each click, JavaScript is executing toggleLiveComments(1); toggleLiveComments(0); toggleLiveComments(1); toggleLiveComments(0); toggleLiveComments(1); ..., etc., and this list doubles in length with each click.

One way to fix this is to use .one instead of .on in these two lines of code: . Although, since that extension is using jQuery( 'body' ).on instead of jQuery( 'a.some-custom-class' ).on and there are other on-click events associated with body, you may need to use .off in combination with .on instead (see the example here).

Thanks again for your help!

Reply to "[RESOLVED] SpecialPage extension: execute JavaScript function onclick"