Is it possible to use links in a JQuery Mobile Form Label? - jquery-mobile

I have just encountered a strange problem when using jQuery Mobile.
I have a link inside a form element label - a checkbox label to be exact but the link does not work.
I have tried reading the docs but can't seem to find anything on it.
Here is my markup:
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<input type="checkbox" class="cbox" name="OptIn" id="OptIn"/>
<label for="OptIn">Receive E-mails From Us</label>
<input type="checkbox" value="1" class="cbox" name="tandc" id="tandc"/>
<label for="tandc">I agree to the <a href="/tandcs.html" target="_BLANK" >Terms & Conditions</a></label>
</fieldset>
</div>
When the link is clicked it just toggles the checkbox state.
UPDATE
Just realised I can open the link by right clicking but obviously on a mobile device that's not very useful....

this is the correct solution for mobile and non mobile browsers
$('.ui-checkbox a').bind("tap click", function( event, data ){
event.stopPropagation();
$.mobile.changePage($(this).attr('href'));
});

Had the same problem and solved it using:
$('.ui-btn-text a').click(function(event) {
var $this = $(this);
window.open($this.attr('href'), $this.attr('target'));
});
So if any link within a button-text is clicked it will be opened in a new window. If you want it in the same window just use $.mobile.changePage as Phil showed.

I tried the above mentioned solutions on jQuery Mobile 1.1.0 with jQuery 1.7.2 without success.
After a bit of tinkering and reading into the new jQuery event functions I came up with my own solution to make all anchors in labels clickable without loosing jQuery Mobile default behaviour on the rest of the label:
jQuery('label').each(function(){
var e = jQuery(this).data('events');
jQuery('.agree label').undelegate();
jQuery('.agree label *:not(a)').delegate(e);
});

use on() and off() instead
$('label').each(function(){
var e = $(this).data('events');
$('label').off();
$('label').not('a').on(e);
});

There a some improvements that can be made but here is a rough draft:
http://jsfiddle.net/KADqA/
JS
$('.ui-btn-text').click(function(event) {
var checked = $("#tandc[type='checkbox']").is(":checked");
var $this = $(this);
if($this.children('a').length) {
$.mobile.changePage('#tc', {
transition : 'pop',
role : 'dialog'
});
}
stateOfCheckbox(checked);
});
function stateOfCheckbox(checked) {
$('#home').live( 'pagebeforeshow',function(event){
$("#tandc[type='checkbox']").attr("checked",checked).checkboxradio("refresh");
});
}
HTML
<div data-role="page" id="home">
<div data-role="fieldcontain">
<fieldset data-role="controlgroup">
<input type="checkbox" class="cbox" name="OptIn" id="OptIn"/>
<label for="OptIn">Receive E-mails From Us</label>
<input type="checkbox" value="1" class="cbox" name="tandc" id="tandc"/>
<label for="tandc">I agree to the <a href="#tc" data-rel="dialog" >Terms & Conditions</a></label>
</fieldset>
</div>
</div>
<div data-role="page" id="tc">
<div data-role="header">
<h1>T and C</h1>
</div>
Read me
</div>​

You could also just override the event:
$('.ui-checkbox a').click(function(e) {
e.stopPropagation();
})

On Android the solutions above did not work on Android.
It only works when using on pagecreate and without event delegation.
$(document).on('pagecreate', function(event, ui) {
$(".ui-checkbox a").on("click tap", function() {
$(':mobile-pagecontainer').pagecontainer('change', this.href);
return false;
});
} );

This is posible solution if you want to open the link in a popup.
$('.ui-checkbox a').bind('click tap', function (event) {
event.stopPropagation();
$($(this).attr('href')).popup('open');
});

Add Id or class into parent label have a tag
and using script of #alex dms
$('#field-contain label a').bind("tap click", function( event, data ){
event.stopPropagation();
$.mobile.changePage($(this).attr('href'));
});
Try it, work perfecly on my mobile and desktop
https://jsfiddle.net/vulieumang/p91zhmnp/

Related

DirtyForms does not work properly with $.blockUI

I'm using DirtyForms and $.blockUI plugin, the latter to change pages when clicking on links (in my app, some pages take a couple of seconds more to load and a visual feedback is fine).
When I change field content and then click any link, DirtyForms is triggered: but when I cancel the process to stay on the page, $.blockUI starts its game, resulting in a stuck page
$('form[method="post"]').dirtyForms();
$('a').on('click', function(){
$.blockUI();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.dirtyforms/2.0.0/jquery.dirtyforms.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js"></script>
<p>Change the field content to activate DirtyForms, then click on the link.<br>
When the popup appears, click on "cancel" to stay on the page.<br>
Watch blockUI getting fired as the link is going to be followed</p>
<form action="#" method="post">
<input type="text" name="username" required>
<button type="submit">send</button>
</form>
click me after changing field content
Please, any solution?
EDIT: I also tried with stay.dirtyforms and afterstay.dirtyforms events, but they have no effect. defer.dirtyforms seems to work but the event is triggered twice (I put a console.log() to check) and I am not sure this is the way to go...
I've edit my answer: I've added some line of code to disable first the onbeforeunload dialog alert, taken from here. And at the end a link to an answer with another idea you can try.
My idea: you have to prevent the default link action and use the $.blockUI Modal Dialogs methods to open a custom dialog, then catch the link attribute href from the link put it inside a variable and use the variable value for the #yes button of the dialog.
See if this solution can meet your needs
/* beforeunload bind and unbind taken from https://gist.github.com/woss/3c2296d9e67e9b91292d */
// call this to restore 'onbeforeunload'
var windowReloadBind = function(message) {
window.onbeforeunload = function(event) {
if (message.length === 0) {
message = '';
};
if (typeof event == 'undefined') {
event = window.event;
};
if (event) {
event.returnValue = message;
};
return message;
}
};
// call this to prevent 'onbeforeunload' dialog
var windowReloadUnBind = function() {
window.onbeforeunload = function() {
return null;
};
};
var linkToFollow; // href to follow
$('form[method="post"]').dirtyForms();
$('a').on('click', function(e){
e.preventDefault();
windowReloadUnBind(); // prevent dialog
$.blockUI({ message: $('#question'), css: { width: '275px' } });
linkToFollow = $(this).attr('href');
});
$('#no').click(function() {
$.unblockUI();
return false;
});
$('#yes').click(function() {
$(window.location).attr('href', linkToFollow);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.dirtyforms/2.0.0/jquery.dirtyforms.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js"></script>
<p>Change the field content to activate DirtyForms, then click on the link.<br>
When the popup appears, click on "cancel" to stay on the page.<br>
Watch blockUI getting fired as the link is going to be followed</p>
<form action="#" method="post">
<input type="text" name="username" required>
<button type="submit">send</button>
</form>
click me after changing field content
<div id="question" style="display:none; cursor: default">
<h6>Would you like to contine?.</h6>
<input type="button" id="yes" value="Yes" />
<input type="button" id="no" value="No" />
</div>
Other idea taken from another answer: Other idea would be to make a simple jQuery.ajax({}) call before return value in beforeunload as seen in this answer

Dynamic checkbox control group using jquery mobile and knockout

I'm trying to dynamically create and filter a jquery mobile control group containing checkboxes using knockout binding. The basic idea is that the user selects an option which filters the list of checkboxes in the control group. I've seen similar questions on here but they all seem to be a one-time binding where once bound by ko and enhanced by jqm they remain unchanged. I have that behavior working, the issue occurs when the underlying viewModel changes and ko updates the list of checkboxes in the control group. A full demo of the behavior can be found on jsfiddle here: http://jsfiddle.net/hkrauss2/JAvLk/15/
I can see that the issue is due to jqm creating a wrapper div when enhancing the control group. Ko then puts new elements above the wrapper div when updating the DOM. Basically I'm asking if anyone has solved this issue and also if anyone thinks I'm asking for trouble by integrating these two libraries? Thanks to everyone in advance.
Here is the Html:
<div id="home" data-role="page">
<div data-role="header">
<h2>Knockout Test</h2>
</div>
<div data-role="content">
<ul id="parent-view" data-role="listview" data-inset="true" data-bind="foreach: parentCategories">
<li></li>
</ul>
<p>
To reproduce the issue select Restaurants, come back and select Nightlife or Bars
</p>
</div>
</div>
<div id="list" data-role="page">
<div data-role="header">
<h2>Knockout Test</h2>
<a data-rel="back" data-icon="carat-l" data-iconpos="notext">Back</a>
</div>
<div data-role="content">
<form>
<div id="child-view" data-role="controlgroup" data-bind="foreach: childCategories, jqmRefreshControlGroup: childCategories">
<input type="checkbox" name="checkbox-v-2a" data-bind="attr: {id: 'categoryId' + id}" />
<label data-bind="text: description, attr: {for: 'categoryId' + id}" />
</div>
</form>
</div>
</div>
And the basic javascript. Note there are two external js files not listed here. One sets $.mobile.autoInitializePage = false; on the mobileinit event. The other brings in data in the form of a JSON array which is used to initialize the Categories property in the AppViewModel.
// Custom binding to handle jqm refresh
ko.bindingHandlers.jqmRefreshControlGroup = {
update: function (element, valueAccessor) {
ko.utils.unwrapObservable(valueAccessor());
try {
$(element).controlgroup("refresh");
} catch (ex) { }
}
}
function GetView(name) {
return $(name).get(0);
}
// Define the AppViewModel
var AppViewModel = function () {
var self = this;
self.currentParentId = ko.observable(0);
self.Categories = ko.observableArray(Categories); // Categories comes from sampledata.js
self.parentCategories = ko.computed(function () {
return ko.utils.arrayFilter(self.Categories(), function (item) {
return item.parentId == 0;
});
});
self.childCategories = ko.computed(function () {
return ko.utils.arrayFilter(self.Categories(), function (item) {
return item.parentId == self.currentParentId();
});
});
self.OnClick = function (viewModel, $event) {
self.currentParentId(viewModel.id);
return true;
};
};
// Create the AppViewModel
var viewModel = new AppViewModel();
// Apply bindings and initialize jqm
$(function () {
ko.applyBindings(viewModel, GetView('#parent-view'));
ko.applyBindings(viewModel, GetView('#child-view'));
$.mobile.initializePage();
});
Update
My old solution wraps each element in a ui-controlgroup-controls div, which adds unnecessary markup. However, the enhancement part is essential.
$(element).enhanceWithin().controlgroup("refresh"); /* line 16 in fiddle */
The new solution is more dynamic to maintain clean markup with no additional wrappers:
First step: Once controlgroup is created controlgroupcreate (event), add data-bind to its' container .controlgroup("container")
Second step: Add checkbox consisted of input and label. At the same time, for each element, add data-bind
Third step: Apply bindings ko.applyBindings().
The static structure of the controlgroup should be basic, it shouldn't contain any elements statically. If a checkbox is added statically, each dynamically created checkbox will be wrapped in an additional .ui-checkbox div.
<div id="child-view" data-role="controlgroup">
<!-- nothing here -->
</div>
JS
$(document).on("controlgroupcreate", "#child-view", function (e) {
$(this)
.controlgroup("container")
.attr("data-bind", "foreach: childCategories, jqmRefreshControlGroup: childCategories")
.append($('<input type="checkbox" name="checkbox" />')
.attr("data-bind", "attr: {id: 'categoryId' + id}"))
.append($('<label />')
.attr("data-bind", "text: description, attr: {for: 'categoryId' + id}"));
ko.applyBindings(viewModel, GetView('#child-view'));
});
Demo
Old solution
As of of jQuery Mobile 1.4, items should be appended to .controlgroup("container") not directly to $("[data-role=controlgroup]").
First, you need to wrap inner elements of controlgroup in div with class ui-controlgroup-controls which acts as controlgroup container.
<div id="child-view" data-role="controlgroup" data-bind="foreach: childCategories, jqmRefreshControlGroup: childCategories">
<div class="ui-controlgroup-controls">
<input type="checkbox" name="checkbox-v-2a" data-bind="attr: {id: 'categoryId' + id}" />
<label data-bind="text: description, attr: {for: 'categoryId' + id}" />
</div>
</div>
Second step, you need to enhance elements inserted into controlgroup container, using .enhanceWithin().
$(element).enhanceWithin().controlgroup("refresh"); /* line 16 in fiddle */
Demo
Omar's answer above works very well. As he mentions in the comments however it does wrap each input/label combination in their own div. This doesn't seem to affect anything visually or functionally but there is another way as outlined below. Basically it uses the containerless control flow syntax to bind the list.
New Html
<div id="child-view" data-role="controlgroup">
<!-- ko foreach: childCategories, jqmRefreshControlGroup: childCategories, forElement: '#child-view' -->
<input type="checkbox" name="checkbox-v-2a" data-bind="attr: {id: 'categoryId' + id}"></input>
<label data-bind="text: description, attr: {for: 'categoryId' + id}"></label>
<!-- /ko -->
</div>
Using the containerless syntax means that we lose the reference to the controlgroup div in the custom binding handler. To help get that back I added the id as '#child-view' in a custom binding named forElement. The magic still all happens in the custom binding handler and Omar's enhanceWithin suggestion remains the secret ingredient. Note: I needed to change the argument list to include all arguments passed by ko.
ko.bindingHandlers.jqmRefreshControlGroup = {
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
ko.utils.unwrapObservable(valueAccessor());
try {
$(allBindings.get('forElement')).enhanceWithin().controlgroup("refresh");
} catch (ex) { }
}
}
Final note: To use a custom handler on a virtual element ko needs to be notified that it is ok. The following is the updated start up statements:
// Apply bindings and initialize jqm
$(function () {
ko.virtualElements.allowedBindings.jqmRefreshControlGroup = true; // This line added
ko.applyBindings(viewModel, GetView('#parent-view'));
ko.applyBindings(viewModel, GetView('#child-view'));
$.mobile.initializePage();
});

jquery mobile a link can not work while using knockoutjs data-bind

I write a link by jquery mobile like:
< a href="#detail" />
it work first time.
then I modify it use ko bind,like:
< a href="#detail" data-bind="click:newAdvice">
it can not chagePage, I do not know why?who can help me?
Try to set for link data-bind click:
<a data-bind="click: showHomepage" data-role="button">Homepage</a>
This is javascript:
self.showHomepage= function () {
$.mobile.changePage("#Homepage", {
transition: "slide"
});
return false;
};
and html for Homepage is:
<div data-role="page" id="Dashboard">
...
</div>

Change position of input filed for jQuery Mobile Filter List

Im using the jQuery Mobile Filter List:
http://jquerymobile.com/test/docs/lists/lists-search-with-dividers.html
Is it possible to move the position of the input field so I can put it into a div already on my page?
Using jQuery's appendTo seems to work fine but its kind of a hacky solution. Thanks
this is what I found while searching for solution to a similar problem.
HTML code example:
<div data-role="page" id="page-id">
<div data-role="content">
<div data-role="fieldcontain">
<input type="search" name="password" id="search" value="" />
</div>
<div class="filter-div" data-filter="one">1</div>
<div class="filter-div" data-filter="two">2</div>
<div class="filter-div" data-filter="three">3</div>
<div class="filter-div" data-filter="four">4</div>
<div class="filter-div" data-filter="five">5</div>
</div>
</div>
JS code example:
$(document).delegate('#page-id', 'pageinit', function () {
var $filterDivs = $('.filter-div');
$('#search').bind('keyup change', function () {
if (this.value == '') {
$filterDivs.slideDown(500);
} else {
var regxp = new RegExp(this.value),
$show = $filterDivs.filter(function () {
return ($(this).attr('data-filter').search(regxp) > -1);
});
$filterDivs.not($show).slideUp(500);
$show.slideDown(500);
}
});
});
This way you can place your input text box anywhere on your page and it should properly filter a list of items you want.
JSFiddle DEMO: http://jsfiddle.net/cZW5r/30/

How to make height of jquery ui autocomplete and button the same?

I use default jquery's ui autocomplete and button and their height is different:
Overriding the button padding with:
CSS
input.ui-button {
padding-top:0;
padding-bottom:0;
}
work for me - see demo
HTML
<div class="ui-widget">
<label for="tags">Tags: </label>
<input id="tags">
<input type="submit" value="Go">
</div>
JavaScript
var availableTags = ['Demo'];
$('#tags').autocomplete({
source: availableTags
});
$('input:submit').button();
(obviously needs jQuery and jQueryUI libraries as well).

Resources