In response to
jQuery UI menu inside a jqGrid cell
My specific implementation of a grid has to call setRowData in numerous places. When setRowData is called, the formatter for the column will get called, and will return the <button>s when the row rebuilds in response to the setRowData.
But in the Menu example, the formatting of the buttons (the calls to .button() and .buttonset()) occur on the loadComplete. Since loadComplete obviously does not run after the setRowData, the buttons in the column display unformatted. So, say we add a button to the body:
<button id="setRowData">Set Row Data</button>
and a click event in the $(function() {})
$("#setRowData").click(function() {
var $grid = $("#list");
var data = $grid.jqGrid('getRowData', 1);
data.name = "Changed!";
$grid.jqGrid('setRowData', 1, data);
});
If you click on the button, the "My Action" and "Bla Bla" buttons show up unformatted.
So, I am looking for an event which I can hang off the setRowData for when after the <button>s have been added to the dom, so I can call .button() and .buttonset() on them again. I want to use an event, since I have a generalized routine which is doing the setRowData (in another library altogether).
Okay, I dug through the JQGrid code, and noticed there was a jqGridAfterGridComplete getting called after the setRowData finishes. So I added a:
$("#list").on("jqGridAfterGridComplete", function() {
... call the .button code again
});
to the ready function, and the styles are applied again. There may be a better way, and please feel free to offer one. But this seems to work.
Related
I am trying to show the loading animation during a function call that takes some time. The function call is searching a large array that is already loaded. After the search, matching items are inserted into a table. The table is cleared prior to starting the search.
The problem is the animation only displays during the brief moment when the page updates.
Here is my code:
var interval = setInterval(function ()
{
$.mobile.loading('show');
clearInterval(interval);
}, 1);
DoSearch(term, function ()
{
var interval = setInterval(function ()
{
$.mobile.loading('hide');
clearInterval(interval);
}, 1000);
});
//The search function looks like this (detail omitted for brevity):
function DoSearch(term)
{
$("table#tableICD tbody").html('');
// also tried:
/*$("table#tableICD tbody")
.html('')
.table()
.closest("table#tableICD")
.table("refresh")
.trigger("create");*/
var tr = '';
$.each(codes, function (key, value)
{
// determine which items match and add them as table rows to 'tr'
});
$("table#tableICD tbody")
.append(tr)
.closest("table#tableICD")
.table("refresh")
.trigger("create");
callback();
}
The search works properly and adds the rows to the table. I have two unexpected behaviors:
The table does not clear until the search is complete. I have tried adding .table("refresh").trigger("create") to the line where I set the tbody html to an empty string. This does not help. (see commented line in the code)
As I mentioned, the animation displays briefly while the screen is refreshing. (Notice I set the interval to 1000 in the second setInterval function just so I could even see it.)
The suggestions I have read so far are to use setInterval instead of straight calling $.mobile.loading, which I have done and placing the search in a function and using a callback, which I have also done.
Any ideas?
Let me give you a few suggestions; they will probably not solve all your issues but they may help you found a solution.
jQuery Mobile is buggy, and for some features, we will never know were they intended to work like that or are they just plain bugs
You can call $.mobile.loading('show') on its own only in pageshow jQuery Mobile event. In any other case, you need to do it in interval or timeout.
It is better to do it in timeout, mostly because you are using less code. Here an example I made several years ago: http://jsfiddle.net/Gajotres/Zr7Gf/
$(document).on('pagebeforecreate', '[data-role="page"]', function(){
setTimeout(function(){
$.mobile.loading('show');
},1);
});
$(document).on('pageshow', '[data-role="page"]', function(){
// You do not need timeout for pageshow. I'm using it so you can see loader is actualy working
setTimeout(function(){
$.mobile.loading('hide');
},300);
});
It's difficult to enhance any jQuery Markup in real time after a page was loaded. So my advice is to first generate new table content, then clean it, and then update markup using .table("refresh").
Do table refresh only once, never do it several times in the row. It is very resourced heavy method and it will last a very long time if you run it for every row
If you are searching on keypress in the input box then showing it in the table; that is the least efficient method in jQuery Mobile. jQM is that slow, it is much better to use listview component which is least resource extensive.
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();
}
});
}
I have the following:
$(document).on("pageinit", function (event) {
alert("pageinit called");
$('#logout').bind('click', function() {alert("clicked!");});
});
The first time the page runs you get a single alert 'pageinit called'. Clicking the element with id #logout fires the alert 'clicked!'. If I click any other links in this page I still get the 'pageinit called' alert (and I get it multiple times, apparently for each page I have previously navigated as well) but subsequently the handler for #logout is gone and never never re-established.
Can anyone tell me how I can get the handler for #logout to remain? I've tried:
$('#logout').die('click').live('click', function() {alert("clicked!");});
to no avail.
After looking more closely (and as commented by Omar), this problem is caused by a combination of the jquery mobile paging system AND trying to attach to a 'single' element by id.
In my case each time I clicked a link within the page it would load into the jqm paging system a separate page, each one containing its own #logout element. My solution was to query for all the buttons and attach handlers to each one:
var buttons = $("*[id='logout']");
buttons.each(function() {
// handle click or whatever here
});
Instead of:
var button = $('#logout'); // Only hooks into the first #logout element
I have a custom select menu (multiple) defined as follows:
<select name="DanceStyles" id="DanceStyles" multiple="multiple" data-native-menu="false">
Everything works fine except that I want to move the header's button icon over to the right AND display the Close text. (I have found some mobile users have a problem either realising what the X icon is for or they have trouble clicking it, so I want it on the right with the word 'Close' making too big to miss.) There don't seem to be any options for doing that on the select since its options apply to the select bar itself.
I have tried intercepting the create event and in there, finding the button anchor and adding a create handler for that, doing something like this (I have tried several variations, as you can see by the commenting out):
$('#search').live('pagecreate', function (event) {
$("#DanceStyles").selectmenu({
create: function (event, ui) {
$('ul#DanceStyles-menu').prev().find('a.ui-btn').button({
create: function (event, ui) {
var $btn = $(this);
$btn.attr('class', $btn.attr('class').replace('ui-btn-left', 'ui-btn-right'));
$btn.attr('class', $btn.attr('class').replace('ui-btn-icon-notext', 'ui-btn-icon-left'));
// $(this).button({ iconpos: 'right' });
// $btn.attr('class', $btn.attr('class').replace('ui-btn-icon-notext', 'ui-btn-icon-left'));
// // $btn.attr('data-iconpos', 'left');
$(this).button('refresh');
}
});
}
});
});
So I have tried resetting the button options and calling refresh (didn't work), and changing the CSS. Neither worked and I got weird formatting issues with the close icon having a line break.
Anyone know the right way to do this?
I got this to work cleanly after looking at the source code for the selectmenu plugin. It is not in fact using a button; the anchor tag is the source for the buttonMarkup plugin, which has already been created (natch) before the Create event fires.
This means that the markup has already been created. My first attempt (see my question) where I try to mangle the existing markup is too messy. It is cleaner and more reliable to remove the buttonMarkup and recreate it with my desired options. Note that the '#search' selector is the id of the JQ page-div, and '#DanceStyles' is the id of my native select element. I could see the latter being used for the id of the menu, which is why I select it first and navigate back up and down to the anchor; I couldn't see any other reliable way to get to the anchor.
$('#search').live('pagecreate', function (event) {
$("#DanceStyles").selectmenu({
create: function (event, ui) {
$('ul#DanceStyles-menu').prev().find('a.ui-btn')
.empty()
.text('Done')
.attr('class', 'ui-btn-right')
.attr("data-" + $.mobile.ns + "iconpos", '')
.attr("data-" + $.mobile.ns + "icon", '')
.attr("title", 'Done')
.buttonMarkup({ iconpos: 'left', icon: 'arrow-l' });
}
});
});
The buttonMarkup plugin uses the A element's text and class values when creating itself but the other data- attributes result from the previous buttonMarkup and have to be removed, as does the inner html that the buttonMarkup creates (child span, etc). The title attribute was not recreated, for some reason, so I set it myself.
PS If anyone knows of a better way to achieve this (buttonMarkup('remove')? for example), please let us know.
the way i achieved it was changing a bit of the jquery mobile code so that the close button always came to the right, without an icon and with the text, "Close"
not the best way i agree. but works..
I got a similar case, and I did some dirty hack about this :P
$("#DanceStyles-button").click(function() {
setTimeout(function(){
$("#DanceStyles-dialog a[role=button]").removeClass("ui-icon-delete").addClass("ui-icon-check");
$("#DanceStyles-dialog .ui-title").html("<span style='float:left;margin-left:25px' id='done'>Done</span>Dance Styles");
$("#DanceStyles-dialog .ui-title #done").click(function() {
$("#DanceStyles").selectmenu("close")
});
},1);
} );
In my jquery-mobile (1.0RC2) application, I have two pages: test1.html, test2.html
The first page, test1.html, includes an collabsible-set, where I expand one collabsible item via script in the pageinit event listener (tried both pageshow and pageinit):
$('#page1').live( 'pageinit', initPage);
function initPage() {
alert('initPage!'); // this line seems to be always getting executed
$('#my_expandable').trigger('expand'); // ... but this line doesn't when coming back via a back link!
}
This works fine on the first call of the page.
Then I have a link that leads me to the second page test2.html, as follows:
<script>
function goPage2(criteria) {
$('#page1').die( 'pageinit', initPage);
$.mobile.changePage( "test2.html", {reverse: false, reloadPage: true} );
}
</script>
page 2
When I then go back to the first page via
<script>
function goPage1() {
$.mobile.changePage( "test1.html", { reverse: true, reloadPage: true} );
}
</script>
test
only the alert message in the pageinit event listener of test1.html is executed, but the collapsible is not being expanded through the
You can see the sample in action here: http://bit.ly/rr0dq3
How to reproduce the problem:
load test1.html at http://bit.ly/rr0dq3
you will get an alert message, and the collapsible will be expanded
click on the button 'GoTo page2' and you will come to the second page test2.html
on this second page, click on the gray button 'test', and you will come back to the first page test1.html
the problem now: as you can see, the alert command of the pageinit event of test1.html is being executed, but expanding the collapsible isn't - why not? Obviously the pageinit event listener method is being entered properly, but only the collapsible seems to have a problem here.
I think it might be a bug (filed a report here https://github.com/jquery/jquery-mobile/issues/2791), but maybe somebody else has an idea for that.
Workaround:
Both the alert and the collapsible expanding is being executed when I use a different way to to open the second page test2.html, using
window.location.href = "test2.html";
instead of
changePage(...);
But it's not very satisfying. Why does it not work properly if I use the the page injection way? I already call the die() method when I open the different pages in order not to have multiple pageinit event listeners keeping hanging around.
Have you tried using the data-attribute for collapsible content areas that makes them load expanded:
data-collapsed="false"
Here is a link to the docs for this behavior: http://jquerymobile.com/demos/1.0rc2/docs/content/content-collapsible.html