How to use events in NanoGallery2's API - javascript

I'm trying to use NanoGallery2 in my web application and it's nearly fantastic, but a couple of glaring omissions from the API (e.g. handling the display of image information) and the idiosyncratic documentation are driving me crazy.
Anyway, the API documentation lists a number of available events and callbacks. I have no trouble using the callbacks to trigger my own functionality, e.g.
$("#nanogallery2").nanogallery2({
items: images,
fnImgDisplayed: onImageDisplayed,
"lightboxNextImage.nanogallery2": onImageDisplayed,
thumbnailHoverEffect2: "scale120|labelSlideUp|toolsAppear",
});
function onImageDisplayed() {
alert("Image Displayed");
}
However, I cannot for the life of me figure out how to trigger my own functionality when any of the events fire. I'm pretty certain that I'm doing it wrong, but no variation I've tried works. I'm particularly interested in the lightboxNextImage and lightboxPreviousImage events. In the documentation all of the event names are listed with a .nanogallery2 suffix (e.g. lightboxNextImage.nanogallery2) so I'm guessing they aren't used in the same way as callbacks but I've tried everything I can think of. Things I've tried unsuccessfully:
$("#nanogallery2").nanogallery2({
items: images,
"lightboxNextImage.nanogallery2": onImageDisplayed,
thumbnailHoverEffect2: "scale120|labelSlideUp|toolsAppear",
});
function onNextImage() {
alert("Next Image")
}
$("#nanogallery2").nanogallery2({
items: images,
lightboxNextImage: onImageDisplayed,
thumbnailHoverEffect2: "scale120|labelSlideUp|toolsAppear",
});
function onNextImage() {
alert("Next Image")
}
$("#nanogallery2").nanogallery2({
items: images,
lightboxNextImage.nanogallery2: onImageDisplayed,
thumbnailHoverEffect2: "scale120|labelSlideUp|toolsAppear",
});
function onNextImage() {
alert("Next Image")
}
That last one even throws up IntelliSense errors because of the period in the key name, but I was getting desperate.
Can anyone put me out my misery? I've made a Codepen here in which I've been trying my various fruitless methods.

As I suspected, I was being completely stupid and this issue only arose because it's midnight and I'm tired. For the benefit of any other tired soul finding their way here, the solution is to add an event listener to the code like this:
$("#nanogallery2").on("lightboxNextImage.nanogallery2", function () {
alert("Next Image");
});
This uses jQuery but since NanoGallery2 is a jQuery plugin that shouldn't be a problem. I've created an updated Codepen with the working solution here.
This came to me 30 seconds after posting the question. Always the way, isn't it?

Related

Custom Unobtrusive Validation Method Not Firing as Per Documentation

I've been attempting to implement a ASP.NET MVC custom validation method. Tutorials I've used such as codeproject explain that you add data-val-customname to the element. Then jQuery.validate.unobtrusive.js then uses the third segment of the attribute
data-val-<customname>
as the name of the rule, as shown below.
$.validator.addMethod('customname', function(value, element, param) {
//... return true or false
});
However I just can't get the customname method to fire. By playing around I have been able to get the below code to work, but according to all the sources I've read Unobtrusive validation should not work like this.
$.validator.addMethod('data-val-customname', function(value, element, param) {
//... return true or false
});
I've posted an example of both methods
jsfiddle example
Any help would be much appreciated
I've updated my question hopefully to make clearer.
I have finally found got there in the end, but still feels like too much hard work and therefore I've probably got something wrong. Initial I was scuppered by a bug in Chrome Canary 62 which refused to allow the adding of a custom method.
My next issue was having to load jQuery, jQuery.validate and jQuery.validate.unobtrusive in the markup and then isolate javascript implementation in a ES6 class. I didn't want to add my adaptors before $().ready() because of my class structure and loading of the app file independent of jQuery. So I had to force $.validator.unobtrusive.parse(document);.
Despite this I was still having issues and finally debugged the source code and found that an existing validator information that is attached to the form was not merging with the updated parsed rules, and essentially ignoring any new adaptors added.
My final work around and admit feels like I've done too much, was to destroy the initial validation information before my forced re-parse.
Here is the working jsfiddle demo
Here is some simplified code
onJQueryReady() {
let formValidator = $.data(document.querySelector('form'), "validator" );
formValidator.destroy();
$.validator.unobtrusive.adapters.add("telephone", [], function (options) {
options.rules['telephone'] = {};
options.messages['telephone'] = options.message;
});
$.validator.unobtrusive.parse(document);
$.validator.addMethod("telephone", this.handleValidateTelephoneNumber);
}

How are custom broadcast events implemented in JavaScript (or jQuery)?

I want to implement a custom event that can be "broadcast", rather than sent to specific targets. Only those elements that have registered themselves as listeners for such events will receive them.
What I have in mind would look as follows.
First, in various places of the code, there would be statements of the form
some_subscriber.on_signal( 'some_signal', some_handler );
I'm using the term signal as shorthand for "broadcast event". In the expression above, some_subscriber registers itself as a listener of one type (called 'some_signal') of such signals, by providing a handler for it.
Elsewhere in the code, there would be statements of the form
publisher.signal_types[ 'some_signal' ].broadcast( event_data );
When statements like these get executed, a new event is generated and "broadcast". By this I mean that the code that calls the broadcast method has no direct information about the listeners for the signal it is issuing.
I have implemented a sketch of this idea in this jsFiddle, mostly in order to illustrate what I described in words above1. (It's certainly not production-grade, and I'm not particularly confident that it could be made so.)
The key elements of this implementation are the following. First, publisher objects do not keep track of their subscribers, as can be seen in the implementation of a factory method for such a publisher, shown below:
function make_publisher ( signal_types ) {
// ...
var _
, signal = {}
, ping = function ( type ) {
signal[ type ].broadcast( ... );
}
;
signal_types.forEach( function ( type ) {
signal[ type ] = $.register_signal_type( type );
} );
return { signal_types: signal_types, ping: ping };
}
This publisher object exposes only two items: the types of signals it broadcasts (in signal_types), and a ping method. When its ping method is invoked, the publisher responds by broadcasting a signal:
signal[ type ].broadcast( ... )
The ultimate recipients of this broadcast are nowhere to be seen in this code.
Second, elsewhere in the code, subscribers register themselves as listeners of these broadcast signals, like so
$( some_selector ).on_signal( signal_type, some_handler );
Note: It is basically impossible to illustrate the rationale for this scheme using an example that is both small and realistic. The reason for this is that the strength of this scheme is that it supports very loose coupling between the publisher code and subscriber code, and this is a feature that is never necessary in a small example. On the contrary, in a small example, code that implements such loose coupling invariably comes across as unnecessarily complex. It is therefore important to keep in mind that this apparent excess complexity is an artifact of the context. Loose coupling is very useful in larger projects. In particular, loose coupling via a publisher/subscriber-type pattern is one of the essential features of MVC.
My question is: is there a better (or at least more standard) way to achieve this effect of "broadcasting" custom events?
(I'm interested in both jQuery-based answers as well as "pure JS" ones.)
1An earlier, ill-fated version of this post was met with almost universal incomprehension, and (of course) the all-too-typical down-voting. With one exception, all the comments I got challenged the very premises of the post, and one directly questioned my grasp of the basics of event-driven programming, etc. I'm hoping that by presenting a working example of what I mean at least it won't come across as utterly inconceivable as it did when I described it in words alone. Luckily, the one helpful comment I did get on that earlier post informed me of the function jQuery.Callbacks. This was indeed a useful tip; the sketch implementation mentioned in the post is based on jQuery.Callbacks.
All right.
So I think what you can do is use the native dispatchEvent and addEventListener methods and use document as the only element for both publishing and subscribing to those events. Something like:
var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
document.addEventListener('someEvent', doSomething, false);
And to make cross-browser, you could:
var myCustomEvent = new Event('someEvent');
document.dispatchEvent(myCustomEvent);
...
if (document.addEventListener) {
document.addEventListener('someEvent', doSomething, false);
} else {
document.attachEvent('someEvent', doSomething);
}
You can read more on the subject here and here. Hope this helps.
My question is: is there a better (or at least more standard) way to
achieve this effect of "broadcasting" custom events?
No, there is not a more standard way of doing publish/subscribe in Javascript. It is not directly built into the language or the browser and there are no platform standards for it that I'm aware of.
You have several options (most of which you seem aware of) to put your own system together.
You could pick a specific object such as the document object or the window object or a new object you create and use jQuery's .on() and .trigger() with that object as a central clearing house to cobble together a publish/subscribe-like model. You could even hide the existence of that object from your actual use by just coding it into a few utility functions if you want.
Or, as you seem to already know, you could use the jQuery.Callbacks functionality. There's even publish/subscribe sample code in the jQuery doc.
Or, you can find a third party library that offers a somewhat traditional publish/subscribe model.
Or, you can build your own from scratch which really just involves keeping a list of callback functions that are associated with a specific event so when that event is triggered, you can call each callback function.
If you came here looking for the jQuery way of doing this, here you go:
Add the event broadcast/dispatch code:
Syntax:
$(<element-name>).trigger(<event-name>);.
Example:
$.ajax({
...
complete: function () {
// signal to registered listeners that event has occured
$(document).trigger("build_complete");
...
}
});
Register a listener for the event:
Syntax:
$(<element-name>).on(<event-name>, function() {...});
Example:
$(document).on("build_complete", function () {
NextTask.Init();
});
Note:
Doing it this way: $(document).build_complete(function() {...}); leads to an error: Uncaught TypeError: $(...).build_complete is not a function.
I know this has been marked as answered back in 2015 -- but a solution that is also elegant and simple could be to use Redux

simultaneously firing and events and location.path

We have a situation where we are utilizing a combination of events and routing inside of angular to handle navigation and communication between modules. Unfortunately the technique doesn't seem to work for us:
Neither this
Events.identifierSearch.fireOnChange(symbol);
$location.path('/explore');
nor this
Events.identifierSearch.fireOnChange(symbol);
$location.path('/explore');
Is working. The event handlers don't seem to fire. However a less optimal solution that we tried to demonstrate our event handlers are properly connected seems to work:
$location.path('/explore');
$timeout(function(){
var symbol = $event.target.innerText;
Events.identifierSearch.fireOnChange(symbol);
}, 500);
Now that is not what we want to have in our production code. In reality the "symbol" will be a large group of nested objects, so passing them on the URL of a route isn't ideal. And rewriting the routing seems like too much of a pain, it handles segregating partial pages nicely.
An idea i had would be to break this into two parts. First fire the event AND handle the event in the controller that fired it. This would require me adding a source to the event - so no sweat:
var controllerName = "thisBlahController" ;
Events.identifierSearch.fireOnChange(controllerName, symbol);
then in the same controller, handle the event and fire the location change:
$scope.$on(Events.identifierSearch.onChange, function() {
var source = Events.identifierSearch.source
if(source === controllerName) {
$location.path('/explore');
}
}
Again - it is not ideal, because it uses the assumption that the event has been handled by all sources prior to the route changing.
First, it would be great to know what it is in the angular routing that is causing the defusing of the events?
Second, I would love an official solution to this problem, one that has been battle tested.
Third, failing a systematic solution, can would like to hear opinions on the two solutions I have proposed up thread.
Quick modification
If i rewrite the order using the timeout approach:
Events.identifierSearch.fireOnChange(symbol);
$timeout(function(){
$location.path('/explore');
}, 1000);
The functionality doesn't seem to work either. Leading to question four Does a controller for a partial page, that is not the current ng-view get their event plumbing hooked up?

How to inject some JS code after every remote call in Rails 3

So I'm using timeago plugin in my Rails 3 app, wrapping it within this function (note than the timeout just keeps the timeago strings updated to the minute at every moment):
function doTimeago() {
$('.timeago').each(function() {
var $this = $(this);
if ($this.data('active')!='yes') {
$this.timeago().data('active','yes');
}
});
}
And then in my application.js
$(function() {
doTimeago();
}
This works great until I load some elements using remote calls. I researched a bit and found no working solution. I'm not happy adding livequery plugin as suggested in this question since it seems deprecated.
I was thinking of adding this JS code to the end of every js.erb file in my app, but it feels really duplicated and nasty.
doTimeago();
Question part 1: ¿Is there an easy way to inject that code after every js.erb execution?
Question part 2: ¿How do I achieve my primary goal of having ajaxy loaded elements work with timeago?
You can bind it to ajaxComplete event, like this:
$(document).on('ajaxComplete', function(){
do_timeago();
});
BTW, I didn't understand your timeout in do_timeago function. Also, JS best practices are a bit different than ruby ones, consider rename your function to something like doTimeago.
Hope it helps.

jQuery, What's Best, Have All the Binds in One Single File For an Entire Site or on a per Page Basis?

I'm in the middle of building a web app with heavy use of jQuery plugins and lots of bindings.
The backend was developed with a template system which only allows (as of now) to place all scripts in that one HTML file. We will use YUI compressor to merge all these into one.
Now, for bindings, how bad is it to have binds in an HTML file (which now is a template for the whole site) for elements that may not be present on a particular page?
Any advice is greatly appreciated
I've been using Paul Irish's markup-based solution pretty extensively on larger sites.
One of the biggest problems with doing this is one of performance - the selector will be evaluated and the DOM searched for each binding not intended for a specific page. At the very least, perhaps set up an object literal to run appropriate ready binding code based on a page identifier, which could be the window.location.href or a substring of. Something like
// avoid global pollution!
(function() {
var pages = {
pageX : {
ready: function() { /* code to run on ready */ },
teardown: function() { /* code to run on teardown */ }
},
pageY : {
ready: function() { /* code to run on ready */ },
teardown: function() { /* code to run on teardown */ }
},
}
// set up ready event handler
$(ready);
// handler function to execute when ready event raised
// Note: Access to pages through closure
function ready() {
var location = window.location.href;
pages[location].ready();
}
})();
Be careful with your selectors if you've got some large pages. For example, if you've got some pages with big, but inert (no bindings) tables, but other pages where tables are small but have controls in them, you probably don't want to do this:
$('td.bindMe').bind('whatever', function() { ... });
(Set aside the live() issue here; sometimes you need to do element-by-element work and that's what I'm talking about.) The problem is that Sizzle will have to look through all the td elements on the page, potentially. Instead, you can put some sort of "marker" container around things like the "active" table with controls, and work it that way:
$('table#withControls').find('td.bindMe').bind(/* ... */);
That way Sizzle only needs to figure out that there's no table called "withControls", and then it's done.
Biggest problem for using all bindings on all pages is that you can get bindings that you did not intended to have, causing troubles...
And of course you will have some performance issues in the page load, but if that is a problem is of course depending on how many bindings you have and how the code looks like.
You might lose some performance on the client side (parsing the file, executing the document-ready handler), but it improves caching on the client (i.e. the file doesn't need to be transferred more than once). That saves server lookups as well. I think this is rather an advantage than a disadvantage as long as you can ensure you're not accidentally modifying objects.
I think the selector engine is fast enough that you, or anyone else, shouldn't notice a difference.
Obviously this is not a "best practice," but if you're binding to ID's and classnames and you won't have any conflicts or unintended bindings then I don't see the harm.

Categories

Resources