I'm having trouble triggering the "spinstop" event I have set for a jQuery UI spinner
I have created a number of spinner elements that update the sum total text box at the bottom on a stop: event. I also have a "Reset" button that will reset all the values back to 0.
Clicking on the "Reset" button will reset the values in the elements, but will not trigger the stop: event to reset the values.
I have tried changing the spinner event to spin: or change: with the same result (actually, change: doesn't have the behaviour I wanted).
Below is an example code.
HTML:
<input type="text" class="number" /><br />
<input type="text" class="number" /><br />
<input type="text" class="number" /><br />
<input type="text" class="number" /><br />
<input type="text" id="total" /><br />
<button id="reset">Reset</button>
jQuery:
$.fn.sum = function () {
var sum = 0;
this.each(function () {
sum += 1 * ($(this).val());
});
return sum;
};
$(".number").spinner({
stop: function () {
$("#total").val($(".number").sum());
}
}).val(0);
$("#total").val($(".number").sum());
$("#reset").click(function () {
$(".number").val(0).trigger("spinstop");
});
What am I doing wrong?
JSFIDDLE
Related
On iPad Safari (and desktop Safari and Chrome), I need to have horizontal sliders on a page that scrolls vertically, where the sliders can't accidentally be moved. Unfortunately, jQuery mobile sliders do respond to (imperfect) vertical swipes on iPad.
A nice way to fix this is to set up the sliders so that the only way to move the knob is to press-hold and then drag. Here's what I've hacked together (note: this only works on touch interfaces):
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<style>
div.input-container {
-webkit-user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
</style>
</head>
<body>
<div id="top">
<div>
<h1>Sliders for iPad</h1>
</div>
<div>
<label for="test1">Test:</label>
<div class="input-container">
<input type="range" name="test1" value="50" min="0" max="100" disabled>
</div>
</div>
<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
<div>
<label for="test2">Test:</label>
<div class="input-container">
<input type="range" name="test2" value="50" min="0" max="100" disabled>
</div>
</div>
<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
<div>
<label for="test3">Test:</label>
<div class="input-container">
<input type="range" name="test3" value="50" min="0" max="100" disabled>
</div>
</div>
</div>
<script>
$(function() {
$(".input-container").addClass("disable-drag")
.on("taphold", tapholdHandler)
.on('touchmove', moveHandler)
.on('touchend', endHandler)
;
function moveHandler(event) {
var containingDiv = $(".input-container");
if (!containingDiv.hasClass('disable-drag')) {
///
console.log(event);
$('a').offset({left: event.originalEvent.targetTouches[0].clientX});
var left = 68;///parseInt($('.ui-slider-track')[0].css('margin-left'));
var w = event.originalEvent.target.offsetWidth - left;
var percent = ((event.originalEvent.targetTouches[0].clientX - left) / w) * 100;
$('input').val(percent).slider('refresh');
///
}
}
function tapholdHandler(event) {
console.log(event)
var containingDiv = $(".input-container");
if (containingDiv.hasClass('disable-drag')) {
containingDiv.removeClass("disable-drag");
event.preventDefault();
event.stopPropagation();
$('input').slider('enable').slider('refresh');
}
}
function endHandler(event) {
$('input').slider('disable').slider('refresh');
$(".input-container").addClass("disable-drag")
}
});
</script>
</body>
</html>
The slider is disabled until a press-hold is detected, at which time it springs to life, updates the knob position and value while the user drags, and returns to disabled when touch ends. It kind of works, but is too brittle and I don't want to be doing essentially the same calcs that the slider already does. Between the /// markers what I'd really like is to programmatically click at the current mouse location so that the knob moves to that spot - without interrupting the press-hold-drag gesture. Is this possible? Here's what I tried but it doesn't work:
///
$(".ui-slider-track").trigger('click'); //todo: narrow the selector to the target slider
///
and
///
$(document.elementFromPoint(event.originalEvent.targetTouches[0].clientX, event.originalEvent.targetTouches[0].clientY)).click();
///
...or is there some other mobile UI library with sliders that don't respond to vertical swipes?
Ok, I figured out a fairly clean simple solution. Tested on iPad Safari. It also keeps the text field enabled so it can be edited directly.
<script>
// The following manages the sliders so that vertical swiping (scrolling) never inadvertently changes a slider value
$(function() {
var timerId;
$.event.special.tap.tapholdThreshold = 250; // default is 750, but that seems too long for this scenario
$(".input-container")
.on('taphold', tapholdHandler)
.on('touchmove', moveHandler)
.on('touchend', endHandler)
;
$('.input-container input')
.prop('disabled', false).removeClass('ui-state-disabled') // allow the user to edit the displayed value
.on('change', function() {
$(this).slider('refresh');
$(this).prop('disabled', false).removeClass('ui-state-disabled'); // override the above line's disabling of the text field
$(this).blur();
})
;
function tapholdHandler(event) {
$(event.currentTarget).find('input').slider('enable').slider('refresh');
}
function moveHandler(event) {
if (timerId)
clearTimeout(timerId); // as long as the user is interacting with the slider, don't revert it to disabled
$(document.elementFromPoint(event.originalEvent.targetTouches[0].clientX, event.originalEvent.targetTouches[0].clientY)).trigger('vmousedown');
}
function endHandler(event) {
$(document.elementFromPoint(event.originalEvent.changedTouches[0].clientX, event.originalEvent.changedTouches[0].clientY)).trigger('vmousedown').trigger('vmouseup'); // the purpose of this is to move the slider knob even if the user's touch didn't move
timerId = setTimeout(function() { // give the above induced event time to run the associated handler
$(event.currentTarget).find('input').slider('disable').slider('refresh');
$(event.currentTarget).find('input').prop('disabled', false).removeClass('ui-state-disabled'); // allow the user to edit the displayed value
}, 2000);
}
});
</script>
Using jquerymobile 1.4.5
I have a series of radio buttons
<fieldset data-role="controlgroup" data-type="horizontal">
<legend>Condition (handles etc.):</legend>
<input name="radio-choice-h-28" id="radio-choice-h-28a" value="1" type="radio">
<label for="radio-choice-h-28a">Satisfactory</label>
<input name="radio-choice-h-28" id="radio-choice-h-28b" value="2" type="radio">
<label for="radio-choice-h-28b">Unsatisfactory</label>
<input name="radio-choice-h-28" id="radio-choice-h-28c" value="0" type="radio">
<label for="radio-choice-h-28c">Not Applicable</label>
</fieldset>
and two buttons
<form>
<input id="set" data-inline="true" value="Save" type="button">
<input id="reset" data-inline="true" value="Reset" type="button">
</form>
The events are attached in the document ready area
<script type="text/javascript">
$(document).ready(function () {
$("#set").click(function() {
$('#radio-choice-h-28a').attr("checked", true).checkboxradio("refresh");
$("input[type='radio']").checkboxradio("refresh");
$('#radio-choice-h-28a').checkboxradio("refresh");
});
$("#reset").click(function () {
$("input[type='radio']").attr("checked", false).checkboxradio("refresh");
$("input[type='radio']").checkboxradio("refresh");
});
});
</script>
I can set the value of the radio the first time through, and reset all the radio buttons the first time through.
When i click the set button the second time, nothing happens.
the second click of the reset also does not fire after the first click.
firebug does not show any error messages, but the breakpoint is hit each time the buttons are clicked (both set and reset)
What am i missing?
Found it....
I changed the attr to prop and now it works...
$("#radio-choice-h-28a").prop("checked", true).checkboxradio("refresh");
try using
$(document).on('click', "#set", function(){
});
Instead of
$("#set").click(function() { });
I'm using jQuery 1.9.1, jQM 1.3 & knockout 2.2.1.
My html is as follows:
<div data-role="page" id="coloursView">
<div data-role="content">
<fieldset data-role="controlgroup">
<legend>Colour:</legend>
<input type="radio" name="colours" data-bind="checked: colour" id="radio-1" value="1" />
<label for="radio-1">Red</label>
<input type="radio" name="colours" data-bind="checked: colour" id="radio-2" value="2" />
<label for="radio-2">Blue</label>
<input type="radio" name="colours" data-bind="checked: colour" id="radio-3" value="3" />
<label for="radio-3">Green</label>
</fieldset>
</div><!--/content -->
</div><!--/page -->
My view model is also very simple:
function ColoursViewModel() {
this.template = "coloursView";
this.colour = ko.observable("1");
this.label = ko.observable(); // custom binding
}
Now, i would like to get the description of the selected colour, not the value.
It seems to me, that i need a custom binding, like this one:
ko.bindingHandlers.label = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$("radio", element).filter(function(el) { return $(el).text() === value; }).prop("checked", "checked");
}
};
But i'm not able to get the text of the related label - the label-for text.
Someone could help?
Thanks in advance
Update
Here is another approach where to find only :checked items and remove white-space in text.
Checkbox
$('input[type=checkbox]').each(function () {
if ($(this).is(':checked')) {
var checkbox = $(this).prev('label').text();
alert('Checkbox: ' + checkbox.replace(/\s+/g, ' '));
}
});
Radio
$('input[type=radio]').each(function () {
if ($(this).is(':checked')) {
var radio = $(this).prev('label').text();
alert('Radio: ' + radio.replace(/\s+/g, ' '));
}
});
Updated Demo
Checkbox
$('div.ui-checkbox').find('span.ui-btn-text').text();
Radio
$('div.ui-radio').find('span.ui-btn-text').text();
Sorry if i answer myself, but i think i got it. At least for radio inputs.
Now, i have a custom binding handler at fieldset level, to keep the markup clean and more readable, as i can:
<fieldset data-role="controlgroup" id="front-colours" data-bind="frontColourLabel: frontColour">
<legend>Front Colour: <span data-bind="text: frontColourDescription"></span> (Value: <span data-bind="text: frontColour"></span>)</legend>
<input type="radio" name="front-colours" data-bind="checked: frontColour" id="fc-radio-1" value="red" />
<label for="fc-radio-1">Red</label>
<input type="radio" name="front-colours" data-bind="checked: frontColour" id="fc-radio-2" value="blue" />
<label for="fc-radio-2">Blue</label>
<input type="radio" name="front-colours" data-bind="checked: frontColour" id="fc-radio-3" value="green" />
<label for="fc-radio-3">Green</label>
</fieldset>
this is the binding handler i come up:
ko.bindingHandlers.frontColourLabel = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.utils.unwrapObservable(valueAccessor());
var id = $('input:radio[name='+element.id+']:checked').prop("id");
var radioText = $('label[for=' + id + ']').text();
viewModel.frontColourDescription(radioText);
}
};
the only tricky part here, is that the id of the fieldset is equal to the name of the radio-group, as it's easy to filter out what radio-group i want to address.
WORKING EXAMPLE: http://jsfiddle.net/h7Bmb/1/
I try now to get the checkbox part to work. Someone can help?
I am using two groups of radio buttons
Group 1:
State
City
Group 2:
A-C
D-H
I-M
N-R
S-Z
When I toggle between state and city, I want A-C from group 2 to be set to checked while the others are set to unchecked.
I have it working in this fiddle here fiddle
HTML:
<div id="sort-radio">
<input type="radio" id="byState" name="sort-radio" checked="checked"/><label for="byState">By State</label>
<input type="radio" id="byCity" name="sort-radio"/><label for="byCity">By City</label>
</div>
<div id="alphabet-radio" style="width:300px;">
<input type="radio" id="A-C" name="alphabet-radio" checked="checked"/>
<label for="A-C">A-C</label>
<input type="radio" id="D-H" name="alphabet-radio"/>
<label for="D-H">D-H</label>
<input type="radio" id="I-M" name="alphabet-radio"/>
<label for="I-M">I-M</label>
<input type="radio" id="N-R" name="alphabet-radio"/>
<label for="N-R">N-R</label>
<input type="radio" id="S-Z" name="alphabet-radio"/>
<label for="S-Z">S-Z</label>
</div>
JavaScript:
$(function () {
$("#sort-radio").buttonset();
});
$(function () {
$("#alphabet-radio").buttonset().find('label').css('width', '19.4%');
});
document.getElementById("byState").addEventListener("click", function () {
document.getElementById("A-C").checked = true;
document.getElementById("D-H").checked = false;
document.getElementById("I-M").checked = false;
document.getElementById("N-R").checked = false;
document.getElementById("S-Z").checked = false;
}, false);
document.getElementById("byCity").addEventListener("click", function () {
document.getElementById("A-C").checked = true;
document.getElementById("D-H").checked = false;
document.getElementById("I-M").checked = false;
document.getElementById("N-R").checked = false;
document.getElementById("S-Z").checked = false;
}, false);
However, when I use this exact code in my website, it does not work (it leaves the previously selected button from group 2 selected). I am using jquery-ui-1.10.1.custom.css which displays the radio buttons nicely, as found here: jquery ui button.
Any clue why this would affect it? When I remove the line <link rel="stylesheet" href="jquery-ui-1.10.1.custom.css" /> from my index.php, it works beautifully.
A few problems:
The button widget works by responding to click events on the radio button's label. This means that the click event you are listening to on the radio buttons themselves won't get fired, since you actually aren't clicking the radio buttons themselves, but their labels. You can work around this by using the change event.
You need to call .buttonset('refresh') after manually updating the checked state of a radio button.
Just setting the checked attribute on one radio button in a group is enough to make the rest become unchecked automatically. You shouldn't need to set the checked property on each one.
You should put your event handlers inside the document.ready handler as well. You can also just use one instead of two.
With all of those things in mind, here are the changes I would make:
$(function () {
$("#sort-radio").buttonset();
$("#alphabet-radio").buttonset().find('label').css('width', '19.4%');
document.getElementById("byState").addEventListener("change", function () {
document.getElementById("A-C").checked = true;
$("#alphabet-radio").buttonset("refresh");
}, false);
document.getElementById("byCity").addEventListener("change", function () {
document.getElementById("A-C").checked = true;
$("#alphabet-radio").buttonset("refresh");
}, false);
});
Example: http://jsfiddle.net/Fzq8L/2/
Since you are using jQuery, you can simplify this a great deal by adding a class to the radio buttons - of which only one can be "set" SO listen to the change event on those. Remove the extra function at the start, pick one of the "array" of buttons to click from the second group. (since only one can be picked)
Simpler version markup :
<div id="sort-radio">
<input type="radio" class="picker" id="byState" name="sort-radio" checked='true'
/>
<label for="byState">By State</label>
<input type="radio" class="picker" id="byCity" name="sort-radio"
/>
<label for="byCity">By City</label>
</div>
<div id="alphabet-radio" style="width:300px;">
<input type="radio" class="secondgroup" id="A-C" name="alphabet-radio"
checked='true' />
<label for="A-C">A-C</label>
<input type="radio" class="secondgroup" id="D-H" name="alphabet-radio"
/>
<label for="D-H">D-H</label>
<input type="radio" class="secondgroup" id="I-M" name="alphabet-radio"
/>
<label for="I-M">I-M</label>
<input type="radio" class="secondgroup" id="N-R" name="alphabet-radio"
/>
<label for="N-R">N-R</label>
<input type="radio" class="secondgroup" id="S-Z" name="alphabet-radio"
/>
<label for="S-Z">S-Z</label>
</div>
Code:
$(document).ready(function () {
$("#sort-radio").buttonset();
$("#alphabet-radio").buttonset().find('label').css('width', '19.4%');
});
$(".picker").change(function () {
$('.secondgroup').eq($('.picker').index(this)).prop("checked", true);
$('#alphabet-radio').buttonset('refresh');
});
Working example:http://jsfiddle.net/MarkSchultheiss/8x28x/2/
Set back second group, first to item when either of first group is changed:
$(document).ready(function () {
$("#sort-radio").buttonset();
$("#alphabet-radio").buttonset().find('label').css('width', '19.4%');
});
$(".picker").change(function () {
$('.secondgroup').eq(0).prop("checked", true);
$("#alphabet-radio").buttonset("refresh");
});
Fiddle for that: http://jsfiddle.net/MarkSchultheiss/8x28x/3/
Ok so this is beginning to drive me insane. I have for several hours now searched and searched, and every single solution doesnt work for me. So yes, this question might be redundant, but i cant for the life of me get solutions to work.
I have a bunch of checkboxes being generated by a jquery template that is databound via knockout.js. However, it turns up unstyled. Afaik, it is something about jquery mobile does the styling before knockout renderes the template, so it ends up unstyled.
I have tried numerous methods to no avail, so i hope someone here can see what i am doing wrong.
(i am using jquery mobile 1.2.0 , jquery 1.8.2 and knockout 2.2.1)
This is the scripts:
<script type="text/javascript">
jQuery.support.cors = true;
var dataFromServer = "";
// create ViewModel with Geography, name, email, frequency and jobtype
var ViewModel = {
email: ko.observable(""),
geographyList: ["Hovedstaden","Sjælland","Fyn + øer","Nordjylland","Midtjylland","Sønderjylland" ],
selectedGeographies: ko.observableArray(dataFromServer.split(",")),
frequencySelection: ko.observable("frequency"),
jobTypes: ["Kontor (administration, sekretær og reception)","Jura","HR, Ledelse, strategi og udvikling","Marketing, kommunikation og PR","Handel og service (butik, service, værtinde og piccoline)","IT","Grafik og design","Lager, chauffør, bud mv.","Økonomi, regnskab og finans","Kundeservice, telefoninterview, salg og telemarketing","Sprog","Øvrige jobtyper"],
selectedJobTypes: ko.observableArray(dataFromServer.split(",")),
workTimes: ["Fulltid","Deltid"],
selectedWorkTimes: ko.observableArray(dataFromServer.split(","))
};
// function for returning checkbox selection as comma separated list
ViewModel.selectedJobTypesDelimited = ko.dependentObservable(function () {
return this.selectedJobTypes().join(",");
}, ViewModel);
var API_URL = "/webapi/api/Subscriptions/";
// function used for parsing json message before sent
function omitKeys(obj, keys) {
var dup = {};
var key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (keys.indexOf(key) === -1) {
dup[key] = obj[key];
}
}
}
return dup;
}
//Function called for inserting new subscription record
function subscribe() {
if($("#jobmailForm").valid()=== true){
//window.alert("add subscriptiooncalled");
var mySubscription = ko.toJS(ViewModel);
//var json = JSON.stringify(mySubscription);
var jsonSmall = JSON.stringify(omitKeys(mySubscription, ['geographyList','jobTypes','selectedJobTypesDelimited','workTimes']));
//window.alert(jsonSmall);
$.ajax({
url: API_URL,
cache: false,
type: 'POST',
contentType: 'application/json',
data: jsonSmall,
success: function (data) {
window.alert("success");
},
error: function (error) {
window.alert("ERROR STATUS: " + error.status + " STATUS TEXT: " + error.statusText);
}
});
}
}
function initializeViewModel() {
// Get the post from the API
var self = this; //Declare observable which will be bind with UI
// Activates knockout.js
ko.applyBindings(ViewModel);
}
// Handle the DOM Ready (Finished Rendering the DOM)
$("#jobmail").live("pageinit", function() {
initializeViewModel();
$('#jobmailDiv').trigger('updatelayout');
});
</script>
<script id="geographyTmpl" type="text/html">
<input type="checkbox" data-role="none" data-bind="attr: { value: $data }, attr: { id: $data }, checked: $root.selectedGeographies" />
<label data-bind="attr: { for: $data }"><span data-bind="text: $data"></span></label>
</script>
<script id="jobTypeTmpl" type="text/html">
<label><input type="checkbox" data-role="none" data-bind="attr: { value: $data }, checked: $root.selectedJobTypes" /><span data-bind="text: $data"></span></label>
</script>
Note, "jobmail" is the surrounding "page" div element, not shown here. And this is the markup:
<div data-role="content">
<umbraco:Item field="bodyText" runat="server"></umbraco:Item>
<form id="jobmailForm" runat="server" data-ajax="false">
<div id="jobmailDiv">
<p>
<label for="email">Email</label>
<input type="text" name="email" id="email" class="required email" data-bind="'value': email" />
</p>
<fieldset data-role="controlgroup" data-mini="true" data-bind="template: { name: 'geographyTmpl', foreach: geographyList, templateOptions: { selections: selectedGeographies } }">
<input type="checkbox" id="lol" />
<label for="lol">fkfkufk</label>
</fieldset>
<fieldset data-role="controlgroup" data-mini="true">
<p data-bind="template: { name: 'jobTypeTmpl', foreach: jobTypes, templateOptions: { selections: selectedJobTypes } }"></p>
</fieldset>
<fieldset data-role="controlgroup" data-mini="true">
<input type="radio" id="frequency5" name="frequency" value="5" data-bind="checked: frequencySelection" /><label for="frequency5">Højst 5 gange om ugen</label>
<input type="radio" id="frequency3" name="frequency" value="3" data-bind="checked: frequencySelection" /><label for="frequency3">Højst 3 gange om ugen</label>
<input type="radio" id="frequency1" name="frequency" value="1" data-bind="checked: frequencySelection" /><label for="frequency1">Højst 1 gang om ugen</label>
</fieldset>
<p>
<input type="button" value="Tilmeld" class="nice small radius action button" onClick="subscribe();">
</p>
Tilbage
</div>
</form>
Alternate method of invoking the restyling (doesnt work either):
$(document).on('pagebeforeshow', '#jobmail', function(){
// Get the post from the API
var self = this; //Declare observable which will be bind with UI
// Activates knockout.js
ko.applyBindings(ViewModel);
});
// Handle the DOM Ready (Finished Rendering the DOM)
$("#jobmail").live("pageinit", function() {
$('#jobmail').trigger('pagecreate');
});
Use a custom binding (Knockout) to trigger jQuery Mobile to enhance the dynamically created content produced by Knockout.
Here is a simple custom binding:
ko.bindingHandlers.jqmEnhance = {
update: function (element, valueAccessor) {
// Get jQuery Mobile to enhance elements within this element
$(element).trigger("create");
}
};
Use the custom binding in your HTML like this, where myValue is the part of your view model that changes, triggering the dynamic content to be inserted into the DOM:
<div data-bind="jqmEnhance: myValue">
<span data-bind="text: someProperty"></span>
My Button
<input type="radio" id="my-id" name="my-name" value="1" data-bind="checked: someOtherProperty" /><label for="my-id">My Label</label>
</div>
In my own case, myValue was part of an expression in an if binding, which would trigger content to be added to the DOM.
<!-- ko if: myValue -->
<span data-bind="jqmEnhance: myValue">
<!-- My content with data-bind attributes -->
</span>
<!-- /ko -->
Every dynamically generated jQuery Mobile content must be manually enhanced.
It can be done in few ways, but most common one can be done through the jQuery Mobile function .trigger( .
Example:
Enhance only page content
$('#page-id').trigger('create');
Enhance full page (header + content + footer):
$('#page-id').trigger('pagecreate');
If you want to find more about this topic take a look my other ARTICLE, to be more transparent it is my personal blog. Or find it HERE.