jQuery Mobile: Markup Enhancement of dynamically added content
Intro:
There are several ways of enhancing dynamically created content markup. It is just not enough to dynamically add new content to jQuery Mobile page, new content must be enhanced with classic jQuery Mobile styling. Because this is rather processing heavy task there need to be some priorities, if possible jQuery Mobile needs to do as less enhancing as possible. Don't enhance whole page if only one component need's to be styled.
What does this all means? When page plugin dispatches a pageInit event, which most widgets use to auto-initialize themselves. it will automatically enhance any instances of the widgets it finds on the page.
However, if you generate new markup client-side or load in content via Ajax and inject it into a page, you can trigger the create event to handle the auto-initialization for all the plugins contained within the new markup. This can be triggered on any element (even the page div itself), saving you the task of manually initializing each plugin (listview button, select, etc.).
With this in mind lets discuss enhancement levels. There are three of them and they are sorted from the less resource demanding to higher ones:
- Enhance a single component/widget
- Enhance a page content
- Enhance a full page content (header, content, footer)
Enhance a single component/widget:
Important: The below enhancement methods are to be used only on current/active page. For dynamically inserted pages, those pages and their contents will be enhanced once inserted into DOM. Calling any method on dynamically created pages / other than the active page, will result an error.
Every jQuery Mobile widget can be enhanced dynamically:
- Listview :
Markup enhancement:
$('#mylist').listview('refresh');
Removing listview elements:
$('#mylist li').eq(0).addClass('ui-screen-hidden');
Enhancement example: http://jsfiddle.net/Gajotres/LrAyE/
Note that the refresh() method only affects new nodes appended to a list. This is done for performance reasons.
One of a listview high-points is a filtering functionality. Unfortunately, for some reason, jQuery Mobile will fail to dynamically add filter option to an existing listview. Fortunately there's a workaround. If possible, remove current listview and add another one with a filer option turned on.
Here's a working example: https://stackoverflow.com/a/15163984/1848600
$(document).on('pagebeforeshow', '#index', function(){
$('<ul>').attr({'id':'test-listview','data-role':'listview', 'data-filter':'true','data-filter-placeholder':'Search...'}).appendTo('#index [data-role="content"]');
$('<li>').append('<a href="#">Audi</a>').appendTo('#test-listview');
$('<li>').append('<a href="#">Mercedes</a>').appendTo('#test-listview');
$('<li>').append('<a href="#">Opel</a>').appendTo('#test-listview');
$('#test-listview').listview().listview('refresh');
});
- Button
Markup enhancement:
$('[type="button"]').button();
Enhancement example: http://jsfiddle.net/Gajotres/m4rjZ/
One more thing, you don't need to use a input element to create a button, it can be even done with a basic div, here's an example: http://jsfiddle.net/Gajotres/L9xcN/
- Navbar
Markup enhancement:
$('[data-role="navbar"]').navbar();
Enhancement example: http://jsfiddle.net/Gajotres/w4m2B/
Here's a demo how to add dynamic navbar tab: http://jsfiddle.net/Gajotres/V6nHp/
And one more in pagebeforecreate event: http://jsfiddle.net/Gajotres/SJG8W/
- Text inputs, Search inputs & Textareas
Markup enhancement:
$('[type="text"]').textinput();
Enhancement example: http://jsfiddle.net/Gajotres/9UQ9k/
- Sliders & Flip toggle switch
Markup enhancement:
$('[type="range"]').slider();
Enhancement example: http://jsfiddle.net/Gajotres/caCsf/
Enhancement example during the pagebeforecreate event: http://jsfiddle.net/Gajotres/NwMLP/
Sliders are little bit buggy to dynamically create, read more about it here: https://stackoverflow.com/a/15708562/1848600
- Checkbox & Radiobox
Markup enhancement:
$('[type="radio"]').checkboxradio();
or if you want to select/deselect another Radiobox/Checkbox element:
$("input[type='radio']").eq(0).attr("checked",false).checkboxradio("refresh");
or
$("input[type='radio']").eq(0).attr("checked",true).checkboxradio("refresh");
Enhancement example: http://jsfiddle.net/Gajotres/VAG6F/
- Select menu
Markup enhancement:
$('select').selectmenu();
Enhancement example: http://jsfiddle.net/Gajotres/dEXac/
- Collapsible
Unfortunately collapsible element can't be enhanced through some specific method, so trigger('create') must be used instead.
Enhancement example: http://jsfiddle.net/Gajotres/ck6uK/
- Table
Markup enhancement:
$(".selector").table("refresh");
While this is a standard way of table enhancement, at this point I can't make it work. So instead use trigger('create').
Enhancement example: http://jsfiddle.net/Gajotres/Zqy4n/
- Panels - New
Panel Markup enhancement:
$('.selector').trigger('pagecreate');
Markup enhancement of content dynamically added to Panel:
$('.selector').trigger('pagecreate');
Example: http://jsfiddle.net/Palestinian/PRC8W/
Enhance a page content:
In case we are generating/rebuilding whole page content it is best to do it all at once and it can be done with this:
$('#index').trigger('create');
Enhancement example: http://jsfiddle.net/Gajotres/426NU/
Enhance a full page content (header, content, footer):
Unfortunately for us trigger('create') can not enhance header and footer markup. In that case we need big guns:
$('#index').trigger('pagecreate');
Enhancement example: http://jsfiddle.net/Gajotres/DGZcr/
This is almost a mystic method because I can't find it in official jQuery Mobile documentation. Still it is easily found in jQuery Mobile bug tracker with a warning not to use it unless it is really really necessary.
Note, .trigger('pagecreate'); can suppose be used only once per page refresh, I found it to be untrue:
http://jsfiddle.net/Gajotres/5rzxJ/
3rd party enhancement plugins
There are several 3rd party enhancement plugins. Some are made as an update to an existing method and some are made to fix broken jQM functionalities.
- Button text change
Unfortunately cant found the developer of this plugin. Original SO source: Change button text jquery mobile
(function($) {
/*
* Changes the displayed text for a jquery mobile button.
* Encapsulates the idiosyncracies of how jquery re-arranges the DOM
* to display a button for either an <a> link or <input type="button">
*/
$.fn.changeButtonText = function(newText) {
return this.each(function() {
$this = $(this);
if( $this.is('a') ) {
$('span.ui-btn-text',$this).text(newText);
return;
}
if( $this.is('input') ) {
$this.val(newText);
// go up the tree
var ctx = $this.closest('.ui-btn');
$('span.ui-btn-text',ctx).text(newText);
return;
}
});
};
})(jQuery);
Working example: http://jsfiddle.net/Gajotres/mwB22/
Get correct maximum content height
In case page header and footer has a constant height content div can be easily set to cover full available space with a little css trick:
#content {
padding: 0;
position : absolute !important;
top : 40px !important;
right : 0;
bottom : 40px !important;
left : 0 !important;
}
And here's a working example with Google maps api3
demo: http://jsfiddle.net/Gajotres/7kGdE/
This method can be used to get correct maximum content height, and it must be used with a pageshow event.
function getRealContentHeight() {
var header = $.mobile.activePage.find("div[data-role='header']:visible");
var footer = $.mobile.activePage.find("div[data-role='footer']:visible");
var content = $.mobile.activePage.find("div[data-role='content']:visible:visible");
var viewport_height = $(window).height();
var content_height = viewport_height - header.outerHeight() - footer.outerHeight();
if((content.outerHeight() - header.outerHeight() - footer.outerHeight()) <= viewport_height) {
content_height -= (content.outerHeight() - content.height());
}
return content_height;
}
And here's a live jsFiddle example: http://jsfiddle.net/Gajotres/nVs9J/
There's one thing to remember. This function will correctly get you maximum available content height and at the same time it can be used to stretch that same content. Unfortunately it cant be used to stretch img to full content height, img tag has an overhead of 3px.
Methods of markup enhancement prevention:
This can be done in few ways, sometimes you will need to combine them to achieve a desired result.
- Method 1:
It can do it by adding this attribute:
data-enhance="false"
to the header, content, footer container.
This also needs to be turned in the app loading phase:
$(document).one("mobileinit", function () {
$.mobile.ignoreContentEnabled=true;
});
Initialize it before jquery-mobile.js is initialized (look at the example below).
More about this can be found here:
http://jquerymobile.com/test/docs/pages/page-scripting.html
Example: http://jsfiddle.net/Gajotres/UZwpj/
To recreate a page again use this:
$('#index').live('pagebeforeshow', function (event) {
$.mobile.ignoreContentEnabled = false;
$(this).attr('data-enhance','true');
$(this).trigger("pagecreate")
});
- Method 2:
Second option is to do it manually with this line:
data-role="none"
Example: http://jsfiddle.net/Gajotres/LqDke/
- Method 3:
Certain HTML elements can be prevented from markup enhancement:
$(document).bind('mobileinit',function(){
$.mobile.page.prototype.options.keepNative = "select, input";
});
Example: http://jsfiddle.net/Gajotres/gAGtS/
Again initialize it before jquery-mobile.js is initialized (look at the example below).
Markup enhancement problems:
Sometimes when creating a component from scratch (like listview) this error will occur:
cannot call methods on listview prior to initialization
It can be prevented with component initialization prior to markup enhancement, this is how you can fix this:
$('#mylist').listview().listview('refresh');
Markup overrding problems:
If for some reason default jQuery Mobile CSS needs to be changed it must be done with !important
override. Without it default css styles can not be changed.
Example:
#navbar li {
background: red !important;
}
jsFiddle
example: http://jsfiddle.net/Gajotres/vTBGa/
Changes:
- 01.02.2013 - Added a dynamic navbar demo
- 01.03.2013 - Added comment about how to dynamically add filtering to a listview
- 07.03.2013 - Added new chapter: Get correct maximum content height
- 17.03.2013 - Added few words to the chapter: Get correct maximum content height
- 29.03.2013 - Added new content about dynamically created sliders and fix an example bug
- 03.04.2013 - Added new content about dynamically created collapsible elements
- 04.04.2013 - Added 3rd party plugins chapter
- 20.05.2013 - Added Dynamically added Panels and contents
- 21.05.2013 - Added another way of setting full content height
- 20.06.2013 - Added new chapter: Markup overrding problems
- 29.06.2013 - Added an important note of WHEN to use enhancement methods
JQM How to Enhance a full page content that is being dynamically injected (as result of redirect)?
I eventually got it to work. In case it might help someone in the future, let me post the answer here.
- JQM elements has to be enhanced and themed. In this "trap & display redirect response content" case, we have to do it programmatically;
- In order to get #1 to work, it eventually came to light that we need to load the "response content" into DOM programmatically
Code Listing:
if (textStatus == parsererror_textStatus ) {
displayResponseContent(XMLHttpRequest.responseText);
}
function displayResponseContent(fullResponse){
loadIntoDOM( fullResponse);
enhancePageMarkup();
}
//The response has to be loaded into DOM for later manipulation
function loadIntoDOM(fullResponse){
var startIdx = fullResponse.indexOf("<body>");
var endIdx = fullResponse.indexOf("</body>");
var bodyTxt = fullResponse.substring(startIdx, endIdx + 7);
//The main thing here is to load the "body" into DOM
var bodyDomNodes = $.parseHTML(bodyTxt, true);
$(document.body).empty().append(bodyDomNodes);
}
//enhance the markup of dynamically added content(e.g: "page" in our case)
function enhancePageMarkup(){
$('div[data-role=page]').trigger('pagecreate');
$(document.documentElement).enhanceWithin();
$('div[data-role=page]').addClass("ui-page-active");
}
load dynamic values in form elements in jquerymobile
You forgot to enhance look of select box, it is done like this:
$('select[id=group_published]').selectmenu('refresh', true);
Read more about it here.
Update:
I didn't look carefully so I didn't so your question was about flipswitch slider.
Working example: http://jsfiddle.net/Gajotres/vds2U/75/
HTML:
<label for="flip-1">Flip switch:</label>
<select name="flip-1" id="flip-1" data-role="slider" data-default-value="off">
<option value="off" defaultSelected>Off</option>
<option value="on">On</option>
</select>
JavaScript:
$(document).on('pagebeforeshow', '#index', function(){
$(document).on('click', '#reset', function(){
$("#flip-1").val('off').slider('refresh');
});
});
Dynamically Generating Content Supporting The JQuery Mobile Theme
To create [data-role=header]
and [data-role=footer]
dynamically, you need to enhance their markup by using $('[data-role=page]').trigger('pagecreate');
.
Each widget in jQuery Mobile should be enhanced using create
, pagecreate
, updatelayout
or refresh
. Using those methods depends on the items you want to enhance. Check this for more about Enhancing Dynamic Contents.
Demo
After appending the new header, add
$('[data-role=page]').trigger('pagecreate');
JQuery Mobile sliders inserted dynamically
This is just one of jQuery Mobile bugs. Also there's an error in your code, label must point to the correct slider, and in your example it is not a case.
Dynamic slider can be created in two ways, none of them includes slider()
method:
Example 1
Do it during the pagebeforecreate
or pagecreate
event.
Working example: http://jsfiddle.net/Gajotres/caCsf/
$(document).on('pagebeforeshow', '#index', function(){
// Add a new input element
$('[data-role="content"]').append('<input type="range" name="slider-2" id="slider-2" value="25" min="0" max="100" />');
});
Example 2
Do it during the pagebeforeshow
or pageshow
event and use trigger('create')
to style sliders.
Working example: http://jsfiddle.net/Gajotres/NwMLP/
$(document).on('pagebeforeshow', '#index', function(){
// Add a new input element
$('[data-role="content"]').append('<div data-role="fieldcontain"></div>');
$('[data-role="fieldcontain"]').append('<fieldset data-role="controlgroup"></fieldset>');
$('[data-role="controlgroup"]').append('<label for="slider-2">Slider 2</label>');
$('[data-role="controlgroup"]').append('<input type="range" name="slider-2" id="slider-2" value="25" min="0" max="100" />');
// Enhance new input element, unfortunately slider() function is not goinf to work correctly
//$('[type="range"]').slider();
$('#index').trigger('create');
});
In this example, if we try to use slider()
only everything will be style except the input box.
More about this and some other related stuff can be found in my other ARTICLE, or find it HERE.
Dynamically Add Buttons Via JQuery Mobile
Here's an working example out of your code: http://jsfiddle.net/Gajotres/NuCs2/
Before you can refresh the button/s it first must be initialized with .button() function. Just like this:
$("#approve"+i).button().button('refresh');
$("#reject"+i).button().button('refresh');
There's also another solution but you should use it only if you are recreating a whole page:
$("#index").trigger("pagecreate");
And here's an example for the second solution: http://jsfiddle.net/Gajotres/mpFJn/
If you want to find out more about methods of markup enhancement take a look at my other ARTICLE, let me be transparent, it is my personal blog. Or find it HERE.
jQuery Mobile does not apply styles after dynamically adding content
Try calling .trigger("create")
on the element with the new content.
According to the jQuery Mobile docs, "The create
event is suited for enhancing raw markup that contains one or more widgets."
EDIT: As of jQuery Mobile 1.4, .trigger('create')
is deprecated, and you should use .enhanceWithin()
instead. (Thanks to John Mc for the heads-up.)
jQuery Mobile disable enhancement on certain tags?
There are few ways of disabling markup enhancement in jQuery Mobile
but in your case there's only one single line solution:
$(document).on('pagebeforeshow', '#index', function(){
$('a').removeClass('ui-link');
});
jsFiddle
example: http://jsfiddle.net/Gajotres/L4KUT/
Other solutions can be found in my other ARTICLE, to be transparent it is my personal blog. Or find it HERE. Search for the chapter called: Methods of markup enhancement prevention.
There you will find a answer how to disable it on a selector level, unfortunately it only works on native form elements and a tag is not a native form element:
$(document).bind('mobileinit',function(){
$.mobile.page.prototype.options.keepNative = "select, input";
});
Related Topics
Submit Form Without Page Reloading
How to Check Whether a Checkbox Is Checked in Jquery
How to Get the Data-Id Attribute
Html5 Local Storage Vs. Session Storage
Google Maps API V3: How to Remove All Markers
Fastest Method to Escape HTML Tags as HTML Entities
Electron Require() Is Not Defined
How to Create a Link Using JavaScript
Why Does Forms With Single Input Field Submit Upon Pressing Enter Key in Input
How to Run an .Exe or .Bat File on 'Onclick' in Html
How to Call PHP Functions by JavaScript
Html-Encoding Lost When Attribute Read from Input Field
How to Programmatically Tell an HTML Select to Drop Down (For Example, Due to Mouseover)
Custom Attributes - Yea or Nay
Make Header and Footer Files to Be Included in Multiple HTML Pages
How to Render HTML String as Real Html
Changing CSS Values With JavaScript
Should Setting an Image Src to Data Url Be Available Immediately