In a nutshell..
the popupbeforeposition event fires every time I open a popup on a page in my jquery mobile project UNTIL I use
$.mobile.changePage(urlObj.hrefNoSearch, {data: postData, type: 'post', reloadPage: forceReload});
to force a refresh of the current page, after-which the 'popupbeforeposition' event never fires.
To elaborate and show my code..
I have a page (id="page_manage-buildings") in my jquery mobile project.
This page contains a link that opens a popup:
open popup
The html for the 'manage_buildings_image_picker' popup contents is pretty straight forward:
<div data-role="popup" id="manage_buildings_image_picker" data-transition="pop" class="ui-content" data-history="false" data-corners="false" data-theme="d" data-overlay-theme="a" style="width:300px; height:150px;">
... popup contents here ...
</div>
I have the 'pageinit' event bound to the '#page_manage-buildings' page and within that I am binding the 'popupbeforeposition' event to the 'manage_buildings_image_picker' popup, like so:
$(document).on("pageinit", "#page_manage-buildings", function() {
console.log('#page_manage-buildings page - pageinit event -- (only once for THIS page)');
$( "#manage_buildings_image_picker" ).on({
popupbeforeposition: function(event, ui) {
console.log('popupbeforeposition event');
}
});
});
Everything works fine until I click a a button labeled 'refresh' that actually runs custom code to reload the current page using $.mobile.changePage to force a reload of the current page. (I need to retain a session variable that is passed from page to page). Anyway, after I click on my refresh button, the 'popupbeforeposition' event is no longer fired when opening the 'manage_buildings_image_picker' popup.
This is the code that is executed when the 'refresh' button is clicked:
convert_get_to_post($(location).attr('href'), true);
function convert_get_to_post(urlStr, forceReload){
postData = new Object;
var urlObj = $.mobile.path.parseUrl(urlStr);
if(urlObj.search){
// -- ensure any query string parameters are sent as post data
var dataStr = urlObj.search.toString();
dataStr = dataStr.replace(/^\?/,'');
dataPairs = dataStr.split('&');
//console.log('here is the dataPairs');
//console.log(dataPairs);
var avsessionFound=0;
for (x in dataPairs) {
dataPair = dataPairs[x];
//console.log(dataPair);
var dataSet = dataPair.split('=');
//console.log('here is the dataSet');
//console.log(dataSet);
postData[dataSet[0]]=dataSet[1];
if(dataSet[0]=='avsession'){
avsessionFound=1;
console.log('avsession found: '+dataSet[1]);
}
}
if(avsessionFound==0){
// inject the avsession value into the post data
postData['avsession'] = $.jStorage.get('avsession', '');
}
//console.log('here is the postData');
//console.log(postData);
// send this request as a post
$.mobile.changePage(urlObj.hrefNoSearch, {data: postData, type: 'post', reloadPage: forceReload});
}else{
// no query string to worry about jsut send this as a post with the avsession value injected
postData['avsession'] = $.jStorage.get('avsession', '');
$.mobile.changePage(urlObj.hrefNoSearch, {data: postData, type: 'post', reloadPage: forceReload});
}
}
UPDATE #1
I changed the syntax of my ON binding and it fixed the 'popupbeforeposition' event issue but now the 'popupafterclose' event is firing twice when I close the popup. I can deal with that but was wondering if it was caused from something obvious?
$(document).on("pageinit", "#page_manage-buildings", function() {
console.log('#page_manage-buildings page - pageinit event -- (only once for THIS page)');
$(document).on("popupbeforeposition", "#manage_buildings_image_picker", function(event, ui) {
console.log('*** popupbeforeposition event ***');
});
$(document).on("popupafterclose", "#manage_buildings_image_picker", function(event, ui) {
console.log('*** popupafterclose event ***');
});
});
Update #2
Actually the code in update #1 caused the bind events to get bound pageview+(n-1) times where n is equal to the number of times that page was viewed. This is because the events are bound at the document level (which stays the same in JQM) and is only ever updated as pages are loaded/unloaded. I used Omar's suggestion and unbound the event with OFF befor binding the event which ensures there is only ever one event firing.
$(document).off("popupbeforeposition", "#manage_buildings_image_picker").on("popupbeforeposition", "#manage_buildings_image_picker", function(event, ui) {
console.log('*** popupbeforeposition event ***');
});
binding events used only on a specific page at the document level seems messy and I would still like to know why it won't work when binding it to an element that exists within that page ??
Related
I'm using Polymer for my web-app and I'm currently having some issues with page linking. It's the first site I'm making using all ajax/javascript, thus I haven't used the history functions of javascript a lot yet.
Anyhow, I have a main menu in the left sidebar. When one of those is pressed, it should change the url of the browser and also put it in the history of the browser. To do so I have the following code:
Polymer('my-app', {
mainMenu: function(){
this.$.mainPages.selected = this.$.mainMenu.selected;
console.log("Pusing state " + this.$.mainMenu.selected);
history.pushState(null , "title", this.$.mainMenu.selected);
}
});
Now the problem is that it gets called twice, so if you click once the pushState is called twice. Needless to say this is not good.
I have made a sample code here: click me.
In this sample code you can see after pressing a couple of menu-items, first of all the event gets fired two times. I've also noticed when pressing the back-button, updating the page seems to also refire the pushState.
So in short, my first concern is that the event of pressing an element from the core-menu, the mainMenu function is called twice. Second concern is that I am repushing states when going back, which I presume should be prevented as well.
Updated code:
Polymer('my-app', {
ready: function(){
var link = document.URL.split("/");
this.$.mainMenu.selected = link[link.length-1];
console.log("ready with link: "+link[link.length-1]);
window.onpopstate = function(){
var link = document.URL.split("/");
console.log("Calling onpopstate. New link is: "+link[link.length-1]);
this.$.mainMenu.selected = link[link.length-1];
this.$.mainPages.selected = this.$.mainMenu.selected;
}
},
nav: function(){
this.$.drawerPanel.togglePanel();
},
back: function(){
window.history.back();
},
mainMenu: function(){
console.log("Pusing state " + this.$.mainMenu.selected);
this.$.mainPages.selected = this.$.mainMenu.selected;
history.pushState(null , "title", this.$.mainMenu.selected);
}
}
You are much better off using data-binding. The idea here is to make your element model-driven, an model-view-presenter (MVP) pattern. The model is made up of properties in your element, the view is described by the template, and the presenter is in your script. Data-binding allows us to loosely couple the view from the logic.
Let's decide the current page will be controlled by a property called page.
Then we can set up our UI to be driven by the page property:
<core-menu selected="{{page}}" valueattr="id">
...
</core-menu>
<core-animated-pages class="main" selected="{{page}}" valueattr="id" transitions="slide-from-right">
This is good because we have decoupled the UI from the code. The code never talks directly to the core-animated-pages or the core-menu (notice I removed the ids). We don't listen to any events. This way we can change the UI at will without having to modify the script.
Now, we want the page to be initially selected by the URL, so we'll initialize it that way, or default to 'home'. We also want to synchronize page to back events, so we'll listen to `onpopstate'.
ready: function() {
// scrape the initial page off the window location
this.page = location.hash.slice(1) || 'home';
// listen for 'back' events
addEventListener('popstate', this.popstate.bind(this));
},
popstate: function(event) {
// comes here whether we went 'back' from code or UI
this.poppedPage = this.page = event.state;
}
We keep track of poppedPage so we can differentiate back and forward. We only want to push a new state when we go forward.
Now we need the page to be reflected in the history, so we need to push state when the page changes. As above, the one caveat is that we need to only push the state if we are going forward.
pageChanged: function() {
// if the selected page changes, push a state (unless we are going backward)
if (this.poppedPage !== this.page) {
history.pushState(this.page, "Title", '#' + this.page);
}
}
Here it is all put together:
http://jsbin.com/luwitudu/9/edit
You get two events, one for the deselection of the currently selected item and one for the new selected item. Check the event object that is passed to your callback function:
mainMenu: function(e) {
console.log(e.detail.isSelected);
console.log(e.detail.item);
}
But if you use on-core-activate your callback is only called once when a menu item is tapped by the user (it is not called when you change the menu selection programmatically.) This prevents the unwanted window history push when you change the menu selection in your program.
Then you should setup a window.onpopstate callback in which you select the (previous) menu item. If you call document.URL in you back() function, it will return the current URL, not the new one set by window.history.back(). Instead only pop from the history in this function.
This should work:
<core-menu ... on-core-activate="{{mainMenu}}">
Polymer("my-app", {
ready: function() {
var self = this;
this.$.mainMenu.selected = "home";
history.pushState(null, "Title", this.$.mainMenu.selected);
window.onpopstate = function() {
var link = document.URL.split("/");
self.$.mainMenu.selected = link[link.length-1];
self.$.mainPages.selected = self.$.mainMenu.selected;
};
},
mainMenu: function() {
this.$.mainPages.selected = this.$.mainMenu.selected;
history.pushState(null, "Title", this.$.mainMenu.selected);
},
back: function() {
window.history.back();
}
});
I am using jquery mobile for sideswipe gestures on ipad.
The below code is in a file referenced in my html file.
My html file has:
<div data-role="page" id="device1">
<!--content for this part of html page -->
</div>
<!--more divs with incrementing id -->
<div data-role="page" id="device4">
<!--content for this part of html page -->
</div>
This format is used in multiple html files.
I use this code (found on stackoverflow) - didnt want to post on old thread.
$(document).ready(function() {
$('.ui-slider-handle').on('touchstart', function(){
// When user touches the slider handle, temporarily unbind the page turn handlers
doUnbind();
});
$('.ui-slider-handle').on('mousedown', function(){
// When user touches the slider handle, temporarily unbind the page turn handlers
doUnbind();
});
$('.ui-slider-handle').on('touchend', function(){
//When the user let's go of the handle, rebind the controls for page turn
// Put in a slight delay so that the rebind does not happen until after the swipe has been triggered
setTimeout( function() {doBind();}, 100 );
});
$('.ui-slider-handle').on('mouseup', function(){
//When the user let's go of the handle, rebind the controls for page turn
// Put in a slight delay so that the rebind does not happen until after the swipe has been triggered
setTimeout( function() {doBind();}, 100 );
});
// Set the initial window (assuming it will always be #1
window.now = 1;
//get an Array of all of the pages and count
windowMax = $('div[data-role="page"]').length;
doBind();
});
// Functions for binding swipe events to named handlers
function doBind() {
$('div[data-role="page"]').on("swipeleft", turnPage);
$('div[data-role="page"]').on("swiperight", turnPageBack);
}
function doUnbind() {
$('div[data-role="page"]').die("swipeleft", turnPage);
$('div[data-role="page"]').die("swiperight", turnPageBack);
}
// Named handlers for binding page turn controls
function turnPage(){
// Check to see if we are already at the highest numbers page
if (window.now < windowMax) {
window.now++
$.mobile.changePage("#device"+window.now, "slide", false, true);
}
}
function turnPageBack(){
// Check to see if we are already at the lowest numbered page
if (window.now != 1) {
window.now--;
$.mobile.changePage("#device"+window.now, "slide", true, true);
}
}
// Named handlers for binding page turn controls
function navigate_without_swipe(page){
// Check to see if we are already at the highest numbers page
$.mobile.changePage("#device"+page, "slide");
}
Please tell me why I need to reload the page for this javascript to work
Because you are using $(document).ready
Thats a JQuery event.
Jquery Mobile has its own loading events because pages are loaded by JQM using AJAX which means that event is not fired.
I think you probably want to do that in a pageinit but check the documentation to see if there is amore appropriate event for your situation.
JQuery Mobile documentation
Im building a small application and I have some click events binded to some span tags that trigger AJAX requests to a PHP file which queries a MySQL database and spits out the results to populate the targeted area.
However, sometimes i will be clicking the buttons and I have conditionals in place to stop multiple clicking to prevent duplicate content being added numerous times.
I click on a button and firebug tells me that the ajax request had actioned more than once, sometimes it will multiply - so it will start by doing it 2 times or another time it will carry our the request 8 times on one click and obviously flood my content area with duplicate data.
Any ideas?
EDIT
Code for a button is as follows:
<span class="btn"><b>Material</b></span>
This would be enabled by
$('.btn').bind('click', matOption);
and this would be controlled by something like this
var matOption = function() {
$(this).addClass('active');
// remove colours if change of mind on materials
if($('#selectedColour').val() >= 1) {
$('.colour').slideUp(500).children().remove();
$('#selectedColour').val('');
$('.matColOpt .btn').html('<b>Material Colour</b>').removeClass('active').css('opacity', 0.55);
$('.btn').eq(2).unbind('click', colOption); // add click to colour
$('#stage h1 span').eq(2).fadeOut(500);
$('.paperOpt .btn').css('opacity', 0.55).unbind('click', selectPaper);
}
// ajax req for available materials
var cid = $('#selectedColour').val();
var target = $('#notebookOpts .matOpt ul');
$.ajax({
type: "GET",
url: ajaxFile+"?method=getMaterials",
beforeSend: function() {if($('.mats').children('li').size() >= 1) { return false; }},
success: function(data) {
target.append(data).slideDown(500);
$('.mats li').bind('click', matSelect);
},
error: function() {alert('An unexpected error has occurred! Please try again.');}
});
};
You're probably binding your matOption function more than once.
if(!window.matOptionBound){
$('.btn').bind('click', matOption);
window.matOptionBound = true;
}
If you have a code that binds an event handler to a DOM element repeatedly then that event handler does gets executed repeatedly on the event. so if your code such
$("span").click(myHandlerFunction)
gets executed thrice, then you have just told jQuery to fire myHandlerFunction thrice on every click of span. It would be good to make sure there is no such condition goign on in your code. If that is not true then please post your code so that I can help further.
PS: The safest way to do this will be as
$("span").unbind("click",myHandlerFunction).bind("click",myHandlerFunction)
I am using jQuery to show/hide a div container (#pluginOptionsContainer), and load a page (./plugin_options.php) inside it with the required POST vars sent.
What POST data is sent is based on the value of a select list (#pluginDD) and the click of a button (#pluginOptionsBtn)...
It works fine in Firefox, but doesn't work in IE.. The '$("#pluginOptionsContainer").load()' request never seems to finish in IE - I only see the loading message forever...
bind(), empty() and append() all seem to work fine in IE.. But not load()..
Here is my code:
// wait for the DOM to be loaded
$(document).ready(function() {
// hide the plugin options
$('#pluginOptionsContainer').hide();
// This is the hack for IE
if ($.browser.msie) {
$("#pluginDD").click(function() {
this.blur();
this.focus();
});
}
// set the main function
$(function() {
// the button shows hides the plugin options page (and its container)
$("#pluginOptionsBtn") .click(function() {
// show the container of the plugin options page
$('#pluginOptionsContainer').empty().append('<div style="text-align:center;width:99%;">Loading...</div>');
$('#pluginOptionsContainer').toggle();
});
// set the loading message if user changes selection with either the dropdown or button
$("#pluginDD,#pluginOptionsBtn").bind('change', function() {
$('#pluginOptionsContainer').empty().append('<div style="text-align:center;width:99%;">Loading...</div>');
});
// then update the page when the plugin is changed when EITHER the plugin button or dropdown or clicked or changed
$("#pluginDD,#pluginOptionsBtn").bind('change click', function() {
// set form fields as vars in js
var pid = <?=$pid;?>;
var cid = <?=$contentid;?>;
var pDD = $("#pluginDD").val();
// add post vars (must use JSON) to be sent into the js var 'dataString'
var dataString = {plugin_options: true, pageid: pid, contentid: cid, pluginDD: pDD };
// include the plugin option page inside the container, with the required values already added into the query string
$("#pluginOptionsContainer").load("/admin/inc/edit/content/plugin_options.php#pluginTop", dataString);
// add this to stop page refresh
return false;
}); // end submit function
}); // end main function
}); // on DOM load
Any help would be GREATLY appreciated! I hate IE!
IE sometimes caches responses. You can check by watching the requests IE makes to the server. Fiddler2 is a tool that's good for watching http requests.
If you notice that you hit submit and don't see any http requests being made, IE is caching the result.
If that's the case, you can add a random number to the url to cache bust it:
$("#pluginOptionsContainer").load("url.php?random=" + Math.random()*99999, dataString);
IE is sometimes pickier then FF when it comes to duplicate element ids.
Check if every ID you use is used only once and is not created anew during the ajax call.
If caching is the issue, then try setting cache = false in jquery's ajax setup...
$.ajaxSetup ({
cache: false,
});
I have a page which displays multiple blocks with results details. Inside each block I have some <a> tags with thickbox jQuery plugin attached: class="thickbox"
Here is an example of one kind of ampersant tag:
<a class="thickbox" title="Please Sign In" href="userloginredir.php?height=220&width=350&deal=3">
Problem comes when I added a jQuery pagination to the page because of too many results displaying on the page.
The div component with the results inside is updated through ajax load() event.
Below is the pagination script:
$(document).ready(function(){
//References
var pages = $("#menu_deals li");
var loading = $("#loading_deals");
var content = $("#content_deals");
//show loading bar
function showLoading(){
loading
.css({visibility:"visible"})
.css({opacity:"1"})
.css({display:"block"})
;
}
//hide loading bar
function hideLoading(){
loading.fadeTo(1000, 0);
};
//Manage click events
pages.live('click',function(){
//show the loading bar
showLoading();
//Highlight current page number
pages.css({'background-color' : ''});
$(this).css({'background-color' : 'yellow'});
//Load content
var pageNum = this.id;
var targetUrl = "ajax_search_results.php?page=" + pageNum + "&" + $("#dealsForm").serialize() + " #content_d";
content.load(targetUrl, hideLoading);
});
//default - 1st page
$("#1").css({'background-color' : 'yellow'});
var targetUrl = "ajax_search_results.php?page=1&" + $("#dealsForm").serialize() + " #content_d";
showLoading();
content.load(targetUrl, hideLoading);
});
When I added pagination (code above), the thickbox events are not recognized anymore and instead of poping out a window with the login form inside it opens the results in new page (is acting like clicking on a normal link)
From my jQuery knowledge this means that the components are not defined in the DOM because the content is updated after document ready triggered.
I'm trying to bind the load event with something like this:
content.bind('load', ???);
But I don't know how to pass the load params, targetUrl and the callback function hideLoading, when binding the load event.
Please help me out in this matter, it already took me more time than possible allowed.
tb_init('a.thickbox, area.thickbox, input.thickbox');//pass where to apply thickbox
call that in the callback of the ajax.
You have used the hideLoading function as the callback, instead replace it with this where you intialise the thickbox and also hide the loading.
content.load(targetURL, function(){ tb_init('a.thickbox'); hideLoading(); });