Backbone.js on iPad: Second instance of a view has an issue with some event binding - ipad

I am developing an application using Backbone.js and have run into an issue where any instance of a view, excluding the first, fails to bind a click event to the ".playVideo" when accessed from an iPad. The same code executes as I would expect on desktop browsers.
I am creating a simple carousel that can scroll left/right and when a button is clicked I trigger another event to create a video player on the page. The first two events which bind to the .leftArrow / .rightArrow class do work on the second instance of the view; however, the click .playView binding will only work on the first instance.
While debugging, I also added a new binding 'click a' and observed that the additional instances don't execute an event for that item as well.
Simplified Code
Pod = Backbone.View.extend({
events: {
'click .leftArrow' : 'previous'
,'click .rightArrow' : 'next'
,'click .playVideo' : 'loadPlayerUI'
}
,initialize:function()
{
// Do stuff
return this;
}
,render:function()
{
// Draw stuff
return this;
}
,loadPlayerUI : function(event) {
event.originalEvent.preventDefault();
console.log("Hit");
return this;
}
});
In the above example the console message will not appear for any instance of this view except the first one created.
Question
What may I be doing incorrectly?
I appreciate your help.
Thank you.

Related

Is it possible to add buttons and control its event to toolbar of inappbrowser using javascript...?

I am developing a Hybrid App for iOS and Android using PhoneGap.Is it possible to add buttons and control its event to toolbar of inappbrowser using javascript.I know how to add it through ios native side but i cant use that process.I need to control the button event through a javascript method.
You have two options to do that.
The first option is, obviously, to patch the native plugin code, and that's it. Here you can find an example made for iOS, you will have to do the same to your Android Java code and for every other platform you want to support.
Another option is to hide the native toolbar and inject HTML and CSS to create a new one when the page is loaded.
Something like this:
// starting inappbrowser...
inAppWindow = window.open(URL_TO_LOAD, '_blank', 'location=no');
// Listen to the events, we need to know when the page is completely loaded
inAppWindow.addEventListener('loadstop', function () {
code = CustomHeader.html();
// Inject your JS code!
inAppWindow.executeScript({
code: code
}, function () {
console.log("injected (callback).");
});
// Inject CSS!
inAppWindow.insertCSS({
code: CustomHeader.css
}, function () {
console.log("CSS inserted!");
});
And you will have obviously to define the CustomHeader object, something like this:
var CustomHeader = {
css: '#customheader { your css here }',
html: function() {
var code = 'var div = document.createElement("div");\
div.id = "customheader";\
// Insert it just after the body tag
if (document.body.firstChild){ document.body.insertBefore(div, document.body.firstChild); } \
else { document.body.appendChild(div); }';
return code;
}
};
I had experience with this problem.
For my case, the second option was enough, not a critical task. Sometimes it takes a lot for the loadstop event to fire, and so you don't see the injected bar for >= 5 seconds.
And you have to pay attention even on the CSS of the loaded page, because obviously you can affect the original CSS, or the original CSS can affect the style of your toolbar.

jQuery UI dialog binding keydown doesn't always work

I'm writing my own ESC handler because I need to do other actions when ESC is pressed, specifically I need to manage where focus goes for keyboard-only users. I have it working for all menus and some dialogs (both of which are using jQueryUI) but I'm having problems with dialogs that open on top of other dialogs (confirmation dialogs).
I'm using a Backbone View and adding my keydown handler on dialogcreate. this.$el.on('dialogcreate', this.bindKeydownEvent);
My handler:
bindKeydownEvent: function(ev, ui) {
var self = this;
this.$el.dialog().on('keydown', function(evt) {
if(evt.keyCode === $.ui.keyCode.ESCAPE) {
self.$el.dialog("close");
if(self.options.closeFocusEl) {
$(self.options.closeFocusEl).focus();
}
evt.stopPropagation();
}
});
}
I've checked and this.$el.dialog() is the correct dialog when the second dialog calls this.bindKeydownEvent but for some reason the keydown handler is not being triggered no matter what I press in the dialog (Tab, Space, Enter, random letters, etc).
Any idea what I'm doing wrong or have a better way I could bind the keydown event?
EDIT:
I just noticed that this is also happening in some first-level dialogs. It looks like the only difference is the way we get the template and therefore create the interior of the dialog. In our Alert and Confirmation dialog classes, we define the template as an attribute on the object like this: template: _.template("<div><%= o.content %></div>"). In other views (in which the keydown binding works) we build the child elements and add them to the DOM of the dialog, set the template in the initialize function
this.options.template = 'navigation/CreateNewDialog.template';
or set it when we call the dialog
var closeConv = new views.CloseConversationDialogView({
confirm: this.closeConversationConfirmed,
content: i18n.t("closeConversationInput"),
template: "conversation/CloseConversationDialog.template"
});
closeConv.render();
Is there a reason that creating the template inline as an attribute on the view would not bind keydown correctly?
To understand why your event handler isn't being triggered you need first understand how event delegation works.
The key to event delegation in that events bubble up the DOM. So when you bind your event using this.$el.dialog().on('keydown',..., what you basically doing is listening to any keydown event that is triggered on your $el or it's descendants. In this case being that your second dialog isn't a descendant of your $el it's events won't bubble up to it and therefore don't trigger your handler.
To work around this you can either bind directly to your second dialog, or instead bind to a exisitng higher level element like the document. For example
$(document).on('keydown', '.myDialog', function() {...
The only thing my original attempt was missing was "widget". The widget method, according to api.jqueryui.com,
Returns a jQuery object containing the generated wrapper.
I don't see any documentation on what exactly $('.selector').dialog() returns but apparently it is not the same as $('.selector').dialog("widget"). I also changed on('keydown'... to just use the jQuery keydown instead.
bindKeydownEvent: function(ev, ui) {
var self = this;
this.$el.dialog("widget").keydown(function(evt) {
if(evt.keyCode === $.ui.keyCode.ESCAPE) {
self.$el.dialog("close");
if(self.options.closeFocusEl) {
$(self.options.closeFocusEl).focus();
}
evt.stopPropagation();
}
});
}

Why do I lose draggable after drag?

I just migrated to backbone and have a strange behaviour.
I attach draggable to an element which is created by a script, thus not directly available in DOM.
EDIT:
The element that is created is .nav, $("#viewer") as container is already in the DOM.
In plain jQuery i used .on and mousemove event for this and it worked.
With backbone I use the same in the initialize method:
initialize: function(options) {
this.viewer = $("#viewer");
this.viewer.on("mousemove", '.nav', function() {
$(this).draggable();
});
This seems to work, but only one time.
After dragging the element one time, I can't drag it anymore.
Are there conflicts with the events? Am I missing something?
You have to refer to $('#viewer') after you've called render(). initialize is called before render, and so the DOM element doesn't exist.
Also, use this.$('#viewer'), and it will grab the element (after render) even if it hasn't been appended to your page's DOM.
myView = new ExampleView({ model: myModel });
$(body).append(myView.render().el);
myView.onRender();
// -------------
// Now on your view:
onRender: function() {
this.viewer = this.$('#viewer');
this.viewer.on("mousemove", '.nav', function() {
$(this).draggable();
});
},
UPDATE
You can also, to make such things simpler, customize Backbone to automatically call the onRender() function after rendering, by triggering an event or something.
Marionette.js (a Backbone.js extension) has this built in and I use it all the time.
The solution finally was pretty easy:
make sure you dont use outdated versions of backbone.js and underscore!!
After i updated the versions to latest I made it work with:
render: function() {
this.viewer.on("mouseover", '.nav', function() {
if (!$(this).data("init")) {
$(this).data("init", true);
$(this).draggable();
}
});
Probably still not very elegant but i couldnt made the suggested onRender method from dc2 work.

JQM - Inject dynamic content at load time only

I'm trying to dynamically populate a select tag at load time (latest jQM version) using a custom template filling function.
If the fn is called in the "pagebeforechange" event, the select tag is properly initialized. Since this event is called on every page transition, I thought of moving the fn to the 'pageinit' event. This does not work, presumably because the DOM is not yet fully available. How can I coerce jQM to inject content in a page only once? Currently, I am using a kludge. There surely must be a smarter way. Thanks for any suggestions.
$(document).bind('pageinit', function () {
InitSelTagTest("#selActTag", "tplTag"); // Does not work.
});
$(document).bind("pagebeforechange", function (e, data) {
if ($("#selActTag").children().size() === 0) {
InitSelTagTest("#selActTag", "tplTag"); // Kludge, but it works
}
});
function InitSelTagTest(el,tpl) { // Append all tags to element el
var lstAllTags = JSON.parse($("#hidTag").val()); // Create tag array
// Retrieve html content from template.
var cbeg = "//<![" + "CDATA[", cend = "//]" + "]>";
var rslt = tmpl(tpl, { ddd: lstAllTags }).replace(cbeg, ").replace(cend,");
$(el).html(rslt).trigger("create"); // Add to DOM.
}
EDIT
In response to Shenaniganz' comment, it seems that the "pagebeforecreate" event could do the trick ie.
$("#pgAct").live("pagebeforecreate", function () {
// Populate tag select. Works. Traversed only once.
InitSelTag("#selActTag", "tplTag");
});
I'm not sure I fully understand your question but I'll throw a few things out there and you let me know if I can extend further.
To make something trigger only once on page load you can try to implement a regular JQuery $(document).ready(function(){}) aka $(function(){}) for the exact reason why JQuery Mobile users are told not to use it. It triggers only once on DOM load. Further pages don't trigger it because they're being switched via Ajax.
Other than that, on regular dynamic content loading you take a look at the following example I put together for someone else earlier:
http://jsbin.com/ozejif/1/edit

How to make Google Analytics count pop up posts

I have a wordpress site where you can view posts in two ways. You can view them on their single page or you can click on thumbs from the homepage and view them in an ajax popup that shows the full post. My problem obviously is that Analytics isn't counting the popup views. I use a template to popup the post and I'm looking for a way to make analytics recoginze it as a page view and tell me what post it was. I tried adding the analytics code to the top of the template page but that didn't do anything. Any ideas? I use the SimpleModal jquery plugin to popup posts and call them like this.
jQuery(document).ready(function() {
jQuery('a.postpopup').live('click', function(){
var id = jQuery(this).attr('rel');
jQuery('<div id="ajax-popup"></div>').hide().appendTo('body').load('http://mysite.com/ajax-handler/?id='+id).modal({
opacity:90,
position: ["0%"],
containerCss:{width:"100%"},
overlayClose:true,
onOpen: function (dialog) {
dialog.overlay.fadeIn('200', function () {
dialog.data.hide();
dialog.container.fadeIn('500', function () {
dialog.data.fadeIn('slow');
});
});
},
onClose: function (dialog) {
dialog.data.fadeOut('slow', function () {
dialog.container.hide('500', function () {
dialog.overlay.fadeOut('200', function () {
$.modal.close();
});
});
});
}
});
return false;
});
});
you can simulate a page view by using _trackPageView
Google Analytics has several options to track non-pageview related user activity that I think you could use here:
Virtual pageviews - as mentioned by user273895, you can add the call to your code when the dialog pops-up to see a "fake" pageview in your reports with a title like '/popup/post-name'
Events - you can instrument the above call with an event, for example: _trackEvent("popup", "click", "post-name"). The interaction is then trackable in the event reports and can be used to specify additional segments and filters for more advanced analysis.
Custom variables - you can set up a page or session level custom variable to record the interaction.
I'd suggest the first 2 options. Personally I tend to use events for this sort of thing but in your case it may make more sense to use a virtual pageview.

Resources