Backbone and JQuery Mobile: Unbind events after a page transition - jquery-mobile

I want to unbind all events when I change a page. I took this solution which extends the View's close function with this.unbind() call and I tried to combine it with JQM page transitions in a changePage function in the Router from here:
changePage: function(page){
$(page.el).attr("data-role", "page");
page.render();
$("body").append($(page.el));
var transition = $.mobile.defaultPageTransition;
if(this.firstPage){
transition = "none",
this.firstPage = false;
}
$.mobile.changePage($(page.el), {changeHash: false, transition: transition});
}
Then changePage looks like this:
changePage: function(page){
if (this.currentView)
this.currentView.close();
$(page.el).attr("data-role", "page");
this.currentView = page;
page.render();
$("body").append($(page.el));
var transition = $.mobile.defaultPageTransition;
if(this.firstPage){
transition = "none",
this.firstPage = false;
}
$.mobile.changePage($(page.el), {changeHash: false, transition: transition});
}
But then I get the JQM error:
Uncaught TypeError: Cannot call method '_trigger' of undefined jquery.mobile-1.1.0.js:2788
transitionPages jquery.mobile-1.1.0.js:2788
$.mobile.changePage jquery.mobile-1.1.0.js:3390
window.AppRouter.Backbone.Router.extend.changePage
I also have jqm-config.js which removes the page's DOM on pagehide event. Could I unbind all events here like: $(event.currentTarget).unbind(); ? But this doesn't work either.
$('div[data-role="page"]').live('pagehide', function (event, ui) {
$(event.currentTarget).remove();
});

I had the same problem. The JQM error occurs because you try to call this.remove() in close() backbone extended method, but the event "pagehide" has already removed the view from the DOM.
Backbone.View.prototype.close = function () {
if (this.beforeClose) {
this.beforeClose();
}
this.remove();
this.unbind();
};
If you comment this.remove() on close method, it works.
Another option is to comment $(event.currentTarget).remove(); on pagehide jqmobile event and doesn't comment this.remove() on close method.
You can't do both, you should choose one of the two options. I prefer the second option, but I think that it's similar to first option. I don't recommend call unbind() on pagehide event.

I was facing the same problem, for some reason the pagechange event was not being fired and the previous pages were not removed from the DOM. Once I removed the non-active pages the CSS was back in action.
So I added
$('div[data-role="page"]').bind('pagehide', function (event, ui) {
$(event.currentTarget).remove();
});
inside
$(document).bind('pagechange', function() {
});
So my jqm-config.js looks like this
$(document).bind("mobileinit", function () {
console.log('mobileinit');
$.mobile.ajaxEnabled = false;
$.mobile.linkBindingEnabled = false;
$.mobile.hashListeningEnabled = false;
$.mobile.pushStateEnabled = false;
//$.mobile.defaultPageTransition = "none";
});
$(document).bind('pagechange', function() {
$('div[data-role="page"]').bind('pagehide', function (event, ui) {
console.log("Removing page");
$(event.currentTarget).remove();
});
});
It took me a few hours and this SO thread to get to this. Hope this helps someone.

Related

Which page was just shown?

In 1.4.2, I have this:
$(document).on('pagecontainershow', PageShown);
function PageShown(myEvent, myUI ) {
log(this)
log(myEvent)
log(myUI)
};
I can't determine which page was just shown.
If I add more specificity to the selector, the event doesn't fire.
Update
As of jQuery Mobile 1.4.2 you can access previous .prevPage and next page .toPage.
$(document).on("pagecontainerhide", function (e, ui) {
var activePage = ui.toPage,
previousPage = ui.prevPage;
});
Both are jQuery objects so $() isn't needed.
To determine which page is currently active, you have two options:
Listen to pagecontainerhide and check ui.nextPage object emitted by that event
$(document).on("pagecontainerhide", function (e, ui) {
var activePage = $(ui.nextPage);
});
On pagecontainershow, use the below function which will return active page.
var activePage = $.mobile.pageContainer.pagecontainer("getActivePage");
Read more about page events.

Angularjs: jquery selectable

i have created a directive to handle selectable provided by Jquery
mydirectives.directive('uiSelectable', function ($parse) {
return {
link: function (scope, element, attrs, ctrl) {
element.selectable({
stop: function (evt, ui) {
var collection = scope.$eval(attrs.docArray)
var selected = element.find('div.parent.ui-selected').map(function () {
var idx = $(this).index();
return { document: collection[idx] }
}).get();
scope.selectedItems = selected;
scope.$apply()
}
});
}
}
});
to use in html
<div class="margin-top-20px" ui-selectable doc-array="documents">
where documents is an array that get returned by server in ajax response.
its working fine i can select multiple items or single item
Issue: i want to clear selection on close button
http://plnkr.co/edit/3cSef9h7MeYSM0cgYUIX?p=preview
i can write jquery in controller to remove .ui-selected class but its not recommended approach
can some one guide me whats the best practice to achieve these type of issue
Update:
i fixed the issue by broadcasting event on cancel and listening it on directive
$scope.clearSelection=function() {
$scope.selectedItems = [];
$timeout(function () {
$rootScope.$broadcast('clearselection', '');
}, 100);
}
and in directive
scope.$on('clearselection', function (event, document) {
element.find('.ui-selected').removeClass('ui-selected')
});
is this the right way of doing it or what is the best practice to solve the issue.
http://plnkr.co/edit/3cSef9h7MeYSM0cgYUIX?p=preview

Combining knockout with sammyjs/pathjs and jquery mobile

I'm trying to combine some of JS libraries to create a mobile SPA website. I'm working with knockoutJS that misses routing engine so I take it from SammyJS or PathJS (haven't decided yet). And I'd like to use jQuery Mobile to get the controls and the mobile design from it.
The thing is that whenever I include the jquery mobile js file into my page the routing engine stops working. Actually it does work, but the window.location.hash get changed not only by me but with jquery mobile itself.
So here is how the code looks like:
in the html file I got a div that I binded to a template
(function ($) {
infuser.defaults.templateUrl = "templates";
console.log('just before pageinit');
$(document).bind('pagecreate', function () {
// disable autoInit so we can navigate to bookmarked hash url
$.mobile.autoInitializePage = false;
// let PathJS handle navigation
$.mobile.ajaxEnabled = false;
$.mobile.hashListeningEnabled = false;
$.mobile.pushStateEnabled = false;
});
$(document).bind('pagebeforechange', function (e, data) {
var to = data.toPage;
if (typeof to === 'string') {
/* var u = $.mobile.path.parseUrl(to);
to = u.hash || '#' + u.pathname;
// manually set hash so PathJS will be triggered
location.hash = to;
// prevent JQM from handling navigation*/
e.preventDefault();
}
});
$(document).bind('pagechange', function (e, data) {
});
var Model = function () {
this.items = ko.observable(null);
this.chosenItemData = ko.observable();
this.state = ko.observable('items');
this.goToItemDetails = function (item) {
location.hash = '/details/' + item.id;
};
};
window.currentModel = new Model();
ko.applyBindings(window.currentModel);
Path.map('#home').to(function () {
currentModel.state(window.templates.items);
currentModel.items(window.dummyData);
});
Path.map('#home/details/:id').to(function () {
var self = this;
$(currentModel.items()).each(function (index, item) {
if (item.id.toString() == self.params['id']) {
currentModel.chosenItemData(item);
currentModel.state(window.templates.itemDetail);
}
});
});
Path.root('#home');
$(function () {
Path.listen();
})
})(jQuery);
Now, you can see that $.mobile.hashListeningEnabled = false; is set to false so the jquery mobile should not listen or react to hash changes whatsoever.
But!
lets say I move from localhost/sammy/#home to localhost/sammy/#home/detail/1
the hash change happens and changes right away to localhost/sammy/home/detail/1
for some reason the hash itself is ommited and the route doesn't get executed.
I sorry if I didnt explain myself better. I'm working on publishing it on a server for everyone to be able to look at it, but, unfortunately it takes time.
Meanwhile, if anyone has any idea what i can do to fix this it will be awesome!
So apparently (and it's written in the jQuery Mobile website the "initmobile" event fires as the script for jquery mobile it attached. To be able attach the event the following lines should be included before the jQuery Mobile script.
<script type="text/javascript">
$(document).on('mobileinit', function () {
$.mobile.ajaxEnabled = false;
$.mobile.hashListeningEnabled = false;
$.mobile.pushStateEnabled = false;
$.mobile.linkBindingEnabled = false;
});
then the onchangehash event in jquery mobile will be disabled.

How to get static information about page transition ended [duplicate]

Are there any events fired by an element to check whether a css3 transition has started or end?
W3C CSS Transitions Draft
The completion of a CSS Transition generates a corresponding DOM Event. An event is fired for each property that undergoes a transition. This allows a content developer to perform actions that synchronize with the completion of a transition.
Webkit
To determine when a transition completes, set a JavaScript event listener function for the DOM event that is sent at the end of a transition. The event is an instance of WebKitTransitionEvent, and its type is webkitTransitionEnd.
box.addEventListener( 'webkitTransitionEnd',
function( event ) { alert( "Finished transition!" ); }, false );
Mozilla
There is a single event that is fired when transitions complete. In Firefox, the event is transitionend, in Opera, oTransitionEnd, and in WebKit it is webkitTransitionEnd.
Opera
There is one type of transition event
available. The oTransitionEnd event
occurs at the completion of the
transition.
Internet Explorer
The transitionend event occurs at the completion of the transition. If the transition is removed before completion, the event will not fire.
Stack Overflow: How do I normalize CSS3 Transition functions across browsers?
Update
All modern browsers now support the unprefixed event:
element.addEventListener('transitionend', callback, false);
https://caniuse.com/#feat=css-transitions
I was using the approach given by Pete, however I have now started using the following
$(".myClass").one('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd',
function() {
//do something
});
Alternatively if you use bootstrap then you can simply do
$(".myClass").one($.support.transition.end,
function() {
//do something
});
This is becuase they include the following in bootstrap.js
+function ($) {
'use strict';
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
// ============================================================
function transitionEnd() {
var el = document.createElement('bootstrap')
var transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd otransitionend',
'transition' : 'transitionend'
}
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return { end: transEndEventNames[name] }
}
}
return false // explicit for ie8 ( ._.)
}
$(function () {
$.support.transition = transitionEnd()
})
}(jQuery);
Note they also include an emulateTransitionEnd function which may be needed to ensure a callback always occurs.
// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false, $el = this
$(this).one($.support.transition.end, function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration)
return this
}
Be aware that sometimes this event doesn’t fire, usually in the case
when properties don’t change or a paint isn’t triggered. To ensure we
always get a callback, let’s set a timeout that’ll trigger the event
manually.
http://blog.alexmaccaw.com/css-transitions
All modern browsers now support the unprefixed event:
element.addEventListener('transitionend', callback, false);
Works in the latest versions of Chrome, Firefox and Safari. Even IE10+.
In Opera 12 when you bind using the plain JavaScript, 'oTransitionEnd' will work:
document.addEventListener("oTransitionEnd", function(){
alert("Transition Ended");
});
however if you bind through jQuery, you need to use 'otransitionend'
$(document).bind("otransitionend", function(){
alert("Transition Ended");
});
In case you are using Modernizr or bootstrap-transition.js you can simply do a change:
var transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd otransitionend',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
},
transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
You can find some info here as well http://www.ianlunn.co.uk/blog/articles/opera-12-otransitionend-bugs-and-workarounds/
Just for fun, don't do this!
$.fn.transitiondone = function () {
return this.each(function () {
var $this = $(this);
setTimeout(function () {
$this.trigger('transitiondone');
}, (parseFloat($this.css('transitionDelay')) + parseFloat($this.css('transitionDuration'))) * 1000);
});
};
$('div').on('mousedown', function (e) {
$(this).addClass('bounce').transitiondone();
});
$('div').on('transitiondone', function () {
$(this).removeClass('bounce');
});
If you simply want to detect only a single transition end, without using any JS framework here's a little convenient utility function:
function once = function(object,event,callback){
var handle={};
var eventNames=event.split(" ");
var cbWrapper=function(){
eventNames.forEach(function(e){
object.removeEventListener(e,cbWrapper, false );
});
callback.apply(this,arguments);
};
eventNames.forEach(function(e){
object.addEventListener(e,cbWrapper,false);
});
handle.cancel=function(){
eventNames.forEach(function(e){
object.removeEventListener(e,cbWrapper, false );
});
};
return handle;
};
Usage:
var handler = once(document.querySelector('#myElement'), 'transitionend', function(){
//do something
});
then if you wish to cancel at some point you can still do it with
handler.cancel();
It's good for other event usages as well :)

Getting JQuery UI's datepicker widget as a EmberJS Mixin to work

In this JsFiddle : http://jsfiddle.net/maxl/mCXND/
(copied and modified from http://jsfiddle.net/ud3323/XMgwV/)
I try to create an Ember DatePicker based on JQuery's.
The first problem I run into is this line :
var ui = jQuery.ui[this.get('uiType')](options, this.get('element'));
jQuery.ui[this.get('uiType')] doesn't return a function, so I suppose that the solution
that I started with works for some jQueryUI widgets, but not all.
I would like a solution that will work for all JQuery-UI widgets,
and in particular JQueryUI's Datepicker.
Thanks
If you look at the jqueryui code, you see that some of them are invoked as a function, others not. You can solve it using this:
var ui;
if (typeof jQuery.ui[this.get('uiType')] === 'function') {
ui = jQuery.ui[this.get('uiType')](options, this.get('element'));
} else {
ui = this.$()[this.get('uiType')](options);
}
working example: http://jsfiddle.net/PzsrT/7/
One more thing about jQuery UI's datepicker widget as a EmberJS Mixin.
If you want to supply a callback function to handle the beforeShowDay event, you will raise this error:
Uncaught TypeError: Cannot read property '0' of undefined
even if your callback function (in your ember view) return an array, like it's specified in the jqueryui doc
beforeShowDay: function(date){
some code...
return [true, ''];
};
This happens because nothing is returned after the callback.call in the _gatherEvents function
_gatherEvents: function(options) {
var uiEvents = this.get('uiEvents') || [], self = this;
uiEvents.forEach(function(event) {
var callback = self[event];
if (callback) {
// You can register a handler for a jQuery UI event by passing
// it in along with the creation options. Update the options hash
// to include any event callbacks.
options[event] = function(event, ui) { callback.call(self, event, ui); };
}
});
}
I fix this by adding a return statement before the callback.call.
_gatherEvents: function(options) {
var uiEvents = this.get('uiEvents') || [], self = this;
uiEvents.forEach(function(event) {
var callback = self[event];
if (callback) {
// You can register a handler for a jQuery UI event by passing
// it in along with the creation options. Update the options hash
// to include any event callbacks.
options[event] = function(event, ui) { return callback.call(self, event, ui); };
}
});
}
working example http://jsfiddle.net/thibault/qf3Yu/

Resources