Unable to validate a custom group using Knockout Validation - javascript

I cannot work out how to get the Knockout Validation plugin to validate a custom selection of viewmodel properties. I can call isValid() to validate the entire viewmodel successfully however.
I have followed the documentation set out here which covers the scenario and also checked all the answers I can find on stack overflow.
My code looks like this:
function MyViewModel() {
var self = this;
self.myproperty = ko.observableArray().extend({ minLength: { message: 'You must specify at least one item.'} })
self.anotherproperty = ko.observable().extend({ required: { params: true, message: 'You must supply a value.'} });
self.IsEntireModelValid = function() {
if (!self.isValid()) {
self.errors.showAllMessages();
return false;
}
else {
return true;
}
self.IsAnotherPropertyValidOnly = function() {
var errors = ko.validation.group(self.anotherproperty);
if (errors.length > 0) {
errors.showAllMessages();
return false;
}
else {
return true;
}
}
When I call self.IsAnotherPropertyValidOnly() the errors variable contains no errors, but when I call self.IsEntireModelValid() I get the correct response.
Could someone point out what I'm doing wrong?

You need to use errors().length.
self.IsAnotherPropertyValidOnly = function() {
var errors = ko.validation.group(self.anotherproperty);
if (errors().length > 0) {
errors.showAllMessages();
return false;
}
else {
return true;
}
}
http://jsfiddle.net/WY7V3/2/

Related

jQuery - Checking if array is empty or has attributes

I'm getting an array of Strings, and if the array has items I want to do one thing and if not I want to do the other. I'm not sure how to check if the array is empty of not. Also when stepping through my code in chrome debugger even if the array has items in it the length is still 0 so I can't use formErrors.length > 0.
Here's my code for getting the errors. This works fine and returns an array of error strings or an empty array:
var formErrors = validateFormData(formData);
function validateFormData(data) {
var errors = [];
if (data["title"].length == 0) {
errors["title"] = "Project title required";
}
if (data["client"].length == 0) {
errors["client"] = "Client name required";
}
if (data["date"].length == 0) {
errors["date"] = "Date required";
} else if (!isValidDateFormat(data["date"])) {
errors["date"] = "Date format invalid - Format: dd/mm/yyyy";
}
if (data["status"] == "") {
errors["status"] = "Please select current status for this project";
}
if (data["type"] == "") {
errors["type"] = "Please select a project type";
}
if (data["extras"].length == 0) {
errors["extras"] = "You must select at least one extra for this project";
}
return errors;
}
Then I want to do one thing if there's no errors and another if there is. But this is the bit that won't work for me.
if (formErrors !== {}) {
displayFormErrors(formErrors);
event.preventDefault();
}
else {
clearForm();
}
I've tried multiple ways and nothing has worked so far. Any help is appreciated, thank you!
EDIT
I can't use the .length on the array cause the length is 0 even when it has data.
Screenshot of chrome debugger
I'm slightly confused about what people are asking sorry, i'm not an expert here is my full code to get a better understanding of what i'm trying to do.
$(document).ready(function () {
$('#submit').on("click", onSubmitForm);
function onSubmitForm(event) {
clearErrorMessages();
var formData = getFormData();
var formErrors = validateFormData(formData);
if (formErrors) {
displayFormErrors(formErrors);
event.preventDefault();
}
else {
clearForm();
// Do other stuff
}
}
function clearForm() {
$('#title').val("");
$('#client').val("");
$('#date').val("");
$('#status').val("planning");
$('#description').val("");
$('.type').prop('checked', false);
$('.extra').prop('checked', false);
$('#title').focus();
}
function clearErrorMessages() {
$(".uk-text-danger").html("");
}
function getFormData () {
var data = [];
data["title"] = $('#title').val();
data["client"] = $('#client').val();
data["date"] = $('#date').val();
data["status"] = $('select#status option:selected').val();
data["description"] = $('#description').val();
if ($("input[name='type']:checked").length > 0) {
data["type"] = $("input[name='type']:checked").val();
}
else {
data["type"] = "";
}
data["extras"] = [];
$.each($("input[name='extras[]']:checked"), function(index, radio) {
data["extras"].push(radio.value);
});
return data;
}
function validateFormData(data) {
var errors = [];
if (data["title"].length == 0) {
errors["title"] = "Project title required";
}
if (data["client"].length == 0) {
errors["client"] = "Client name required";
}
if (data["date"].length == 0) {
errors["date"] = "Date required";
} else if (!isValidDateFormat(data["date"])) {
errors["date"] = "Date format invalid - Format: dd/mm/yyyy";
}
if (data["status"] == "") {
errors["status"] = "Please select current status for this project";
}
if (data["type"] == "") {
errors["type"] = "Please select a project type";
}
if (data["extras"].length == 0) {
errors["extras"] = "You must select at least one extra for this project";
}
return errors;
}
function displayFormErrors(errors) {
for (var field in errors) {
var errorElementId = field + "Error";
$('#' + errorElementId).html(errors[field]);
}
} });
Sorry if this is too much i'm not sure what else to do.
An empty array, string or object is "falsy" in JavaScript.
That is, you can pass the array, string or object directly into the if conditional and it will run depending on if something is in there or not.
if ([]) {
// this will never run
}
if ('') {
// this won't run either
}
if ({}) {
// nor will this
}
var errors = {}; inside the validateFormData function.
And then compare the the object like this.
if (JSON.stringify( formErrors ) !== '{}') { //do something}else { //do something}
Where are you verifying if the formErrors is empty? This verification (the if-else) should be inside the function which submits the form.
Also try using:
if (formErrors.length > 0)
instead of:
if (formErrors !== {})

How can I use Global variable across multiple functions in JavaScript?

I have a piece of code which is intended to check the permission level and group membership of a use and launch a dialog box if the user has the correct permissions to access that section of the site.
function bindSettingsButton() {
$("#mt-ngw-personalsettings").on("click", function() {
RequestNewSite();
});
}
function RequestNewSite() {
var HasPermission = false;
var isGroupMember = false;
CheckCurrentUserMembership();
CheckUserHasEditPermissions();
CheckUserPermissions();
}
function CheckCurrentUserMembership() {
var clientContext = new SP.ClientContext.get_current();
this.currentUser = clientContext.get_web().get_currentUser();
clientContext.load(this.currentUser);
this.userGroups = this.currentUser.get_groups();
clientContext.load(this.userGroups);
clientContext.executeQueryAsync(OnQuerySucceeded, OnQueryFailed);
}
function OnQuerySucceeded() {
var isMember = false;
var groupsEnumerator = userGroups.getEnumerator();
while (groupsEnumerator.moveNext()) {
var group = groupsEnumerator.get_current();
if(group.get_title() == "Create Site OptOut") {
isMember = true;
this.isGroupMember = true;
break;
}
}
}
function OnQueryFailed()
{
alert("Couldn't check user group membership. Please contact to resolve this issue.");
}
function CheckUserHasEditPermissions() {
context = new SP.ClientContext.get_current();
web = context.get_web();
this._currentUser = web.get_currentUser();
this._theList = web.get_lists().getByTitle('siterequests');
context.load(this._currentUser);
context.load(this._theList, 'EffectiveBasePermissions')
context.executeQueryAsync(Function.createDelegate(this, this.onPermissionsSuccessMethod), Function.createDelegate(this, this.onPermissionsFailureMethod));
}
function onPermissionsSuccessMethod(sender, args) {
if (this._theList.get_effectiveBasePermissions().has(SP.PermissionKind.editListItems))
{
this.HasPermission = true;
}
else
{
this.HasPermission = false;
}
}
function onPermissionsFailureMethod()
{
alert("Couldn't check permissions. Please contact to resolve this issue.");
}
function CheckUserPermissions() {
if(this.isGroupMember == true)
{
alert("You do not have permission to create sites. If you believe you should have access to this functionality, please contact .");
}
else if(this.HasPermission == false)
{
alert("You do not have permission to create sites. If you believe you should have access to this functionality, please contact .");
}
else
{
showDialogue();
document.getElementById("next-stage").focus();
}
}
Unfortunately when it reaches the end this section the variables HasPermission and isGroupMember are still undefined so the dialogue launches automatically for every user.
I have a feeling I have misused the .this keywords and this is a scoping error but I am not expert enough in JS to know for certain or be able to fix it. Can anyone tell me exactly what I've done wrong and how to fix it please?
You are performing async functions, which means the rest of the code will keep executing even though the stuff you have started first are not completed yet.
You will have to call the CheckUserPermissions after onPermissionsSuccessMethod and the OnQuerySucceeded function has completed.
In addition to this the HasPermission and isGroupMember variables are local to the RequestNewSite function, which means they are out of the scope of the CheckUserPermissions function.
var HasPermission = false;
var isGroupMember = false;
var CompletedCallbacks = 0;
function bindSettingsButton() {
$("#mt-ngw-personalsettings").on("click", function() {
RequestNewSite();
});
}
function RequestNewSite() {
CheckCurrentUserMembership();
CheckUserHasEditPermissions();
}
function CheckCurrentUserMembership() {
var clientContext = new SP.ClientContext.get_current();
this.currentUser = clientContext.get_web().get_currentUser();
clientContext.load(this.currentUser);
this.userGroups = this.currentUser.get_groups();
clientContext.load(this.userGroups);
clientContext.executeQueryAsync(OnQuerySucceeded, OnQueryFailed);
}
function OnQuerySucceeded() {
var isMember = false;
var groupsEnumerator = userGroups.getEnumerator();
while (groupsEnumerator.moveNext()) {
var group = groupsEnumerator.get_current();
if(group.get_title() == "Create Site OptOut") {
isMember = true;
isGroupMember = true;
break;
}
}
CompletedCallbacks++;
CheckUserPermissions();
}
function OnQueryFailed()
{
alert("Couldn't check user group membership. Please contact SPCOE#capita.co.uk to resolve this issue.");
}
function CheckUserHasEditPermissions() {
context = new SP.ClientContext.get_current();
web = context.get_web();
this._currentUser = web.get_currentUser();
this._theList = web.get_lists().getByTitle('siterequests');
context.load(this._currentUser);
context.load(this._theList, 'EffectiveBasePermissions')
context.executeQueryAsync(Function.createDelegate(this, this.onPermissionsSuccessMethod), Function.createDelegate(this, this.onPermissionsFailureMethod));
}
function onPermissionsSuccessMethod(sender, args) {
if (this._theList.get_effectiveBasePermissions().has(SP.PermissionKind.editListItems))
{
HasPermission = true;
}
else
{
HasPermission = false;
}
CompletedCallbacks++;
CheckUserPermissions();
}
function onPermissionsFailureMethod()
{
alert("Couldn't check permissions. Please contact SPCOE#capita.co.uk to resolve this issue.");
}
function CheckUserPermissions() {
if(CompletedCallbacks != 2) return;
if(isGroupMember == true)
{
alert("You do not have permission to create sites. If you believe you should have access to this functionality, please contact SPOCOE#capita.co.uk.");
}
else if(HasPermission == false)
{
alert("You do not have permission to create sites. If you believe you should have access to this functionality, please contact SPOCOE#capita.co.uk.");
}
else
{
showDialogue();
document.getElementById("next-stage").focus();
}
}
This code should work.
$("#mt-ngw-personalsettings").on("click", function() {
RequestNewSite();
});
If you are expecting to use RequestNewSite as a constructor, you need to use new to allocate it. If you call as a function no object (and thus state) is created.
Additionally all members of the type need to be created explicitly on this.
So
function RequestNewSite() {
var HasPermission = false;
var isGroupMember = false;
CheckCurrentUserMembership();
CheckUserHasEditPermissions();
[...]
Needs to be
function RequestNewSite() {
this.HasPermission = false;
this.isGroupMember = false;
this.CheckCurrentUserMembership();
this.CheckUserHasEditPermissions();
[...]

JQuery plugin not working on codepen, works great elsewhere

I have a plugin that I just wrote, which is so far acting just I had hoped while developing. I attempted to take this code out of my scripts directory and post it on Codepen.io. I get a script error when running it there, the error is cannot read 'Action' of undefined. It gets instantiated by a click event which passes an event. Both using jQuery 2.1. Anyone know whats happening here?
Heres the codepen:
http://codepen.io/nicholasabrams/pen/uJKrL
// $ DOC
$.fn.dataValidate = function(event, userSettings) {
"use strict";
event.preventDefault();
var api = {
// Script definition defaults defined in object below
notNull: {
errorText: 'This field is required',
symbol: false,
Action: function(dataToCheck, instance) {
if (dataToCheck === '' || dataToCheck === null || dataToCheck === 'undefined' || dataToCheck.length === 0 ) {
// if true return true to caller
alert('null');
// Retrieve errorText
// Wrap in error template
this.errorForNotNull = new api.ErrorInjector(instance);
return false;
}
else {
return true;
}
}
},
isNaN: {
errorText: 'Numbers not allowed here',
symbol: false,
Action: function(dataToCheck, instance) {
api.notNull.Action(dataToCheck, instance); /* Reuse the notNull method as a screening service before entering into the method specific filtering (assuming null fields would be inappropriate in any types of check) */
if (isNaN(dataToCheck)){ // Check if the not null field is also non a number
return true;
}
else {
this.errorForIsNan = new api.ErrorInjector(instance);
return false;
}
}
},
isNum: {
errorText: 'Please enter a number',
symbol: false,
Action: function(dataToCheck, instance) {
api.notNull.Action(dataToCheck, instance);
if (!isNaN(dataToCheck)){ // Check if the not null field is also non a number
return true;
}
else {
this.errorForIsNan = new api.ErrorInjector(instance);
return false;
}
}
},
isEmail: {
errorText: 'Please enter a valid email address',
symbol: false,
Action: function(dataToCheck, instance) {
api.notNull.Action(dataToCheck, instance);
var checkEmailRegEx = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (checkEmailRegEx.test(dataToCheck)){
}
else {
this.errorForIsEmail = new api.ErrorInjector(instance);
}
}
},
isPw: {
errorText: 'Please enter a password',
symbol: false,
Action: function(dataToCheck, instance) {
api.notNull.Action(dataToCheck, instance);
console.log(dataToCheck);
if (dataToCheck.length > 4){
var isPwRegEx = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[0-9a-zA-Z]{8,}$/;
if(isPwRegEx.test(dataToCheck)){
// Multiple pw checkpoints here
// At least one upper case English letter
// At least one lower case English letter
// At least one digit
// At least one special character
return false;
}
else {
this.errorForIsPw = new api.ErrorInjector(instance);
return true;
}
}
} // End length check for isPw
},
isPhoneNumber: {
errorText: 'Please enter a valid phone number',
symbol: false,
Action: function(dataToCheck, instance) {
api.notNull.Action(dataToCheck, instance);
this.errorForIsPhoneNumber = new api.ErrorInjector(instance);
}
},
isUsername: {
errorText: 'Please enter a valid username',
symbol: false,
Action: function(dataToCheck, instance) {
api.notNull.Action(dataToCheck, instance);
var checkUsernameRegEx = /^[a-zA-Z0-9.\-_$#*!]{3,30}$/;
if (checkUsernameRegEx.test(dataToCheck)){
alert('valid username');
}
else {
this.errorForIsEmail = new api.ErrorInjector(instance);
}
}
},
isNamePart: {
errorText: 'Please enter a valid name',
symbol: false,
Action: function(dataToCheck, instance) {
api.notNull.Action(dataToCheck, instance);
var checkNamePartRegEx = /^[a-zA-Z ]+$/;
if (checkNamePartRegEx.test(dataToCheck)){
alert('valid name part');
}
else {
this.errorForIsEmail = new api.ErrorInjector(instance);
}
}
},
// New method would be added here
errorOutput: 'validated',
targets: ['[data-validate="notNull"]', '[data-validate="isNaN"]',
'[data-validate="isNum"]', '[data-validate="isEmail"]', '[data-validate="isPw"]', '[data-validate="isPhoneNumber"]', '[data-validate="isUsername"]','[data-validate="isNamePart"]'],
// Target selectors, can be modified on initialization to that of your liking, as well as have new ones added. Add a new selector target at the end of the array above
placeholder: {
// Template shared by each validation output error
template: {
defaultPlaceholderContainerStyle: 'position: relative;background:#ccc;',
defaultPlaceholderStyle: 'position: absolute;left:0;top:0;width:100%;line-height:26px;height:100%;',
// The above styles may be easily detached by simply tranfering the above CSS to a style rule matching the errorOutput class outlined above in this same object, or set on instantiation
},
},
ErrorInjector: function(instance) {
var errorNs = instance.data('validate');
var error = '<div data-validate="output" class="' + api.errorOutput + '">' + api[errorNs].errorText + '<\/div>';
instance.wrap('<div data-validate="output_container" class="' + api.errorOutput + '_container"><\/div>');
instance.before(error);
},
acceptedTypes : ['input[type="text"]','input[type="email"]','input[type="password"]','input[type="checkbox"]','input[type="radio"]','input[type="tel"]'],
results: {} // NS for all validation results and debugging info (see below)
};
// Merge the caller sent options object with the defaults. Any options set in on init from the caller will overwrite the default/internal settings
this._overrideApiWithUserSettings = (function() {
$.extend(true, api, userSettings);
})();
var targetsAll = api.targets;
// Private utility for removing the validationOutput errors from the DOM
this._removeThisErrorFocusThisInput = function() {
var activeOutputPlaceholder = $(this);
activeOutputPlaceholder.unwrap();
activeOutputPlaceholder.remove();
$.each(api.acceptedTypes, function(){
var eachTypeInAcceptedTypes = this;
activeOutputPlaceholder.find(eachTypeInAcceptedTypes).focus();
});
$('body').unbind('click', '.' + api.errorOutput);
};
$('body').on('click', '.' + api.errorOutput, this._removeThisErrorFocusThisInput);
// Fire each module off conditionally, based on the presence of the targets set on init
this._instantiateByDataValues = (function() { // The core of the script, carefully loadings only each modular bit of functionality by its request in the DOM via data-validate=""
$.each(targetsAll, function( /*iteration*/ ) { /* Iterate through all of the selectors in the targets array, doing the following with each instance of them found in the DOM, passing iteration for debugging purposed only */
var selectorTargetFromArray = $(this);
$.each(selectorTargetFromArray, function() {
var instance = $(this),
thisFnFromDomDataAttrNS = instance.data('validate');
if (instance.length) { // If any of the selectors in the targets array are found to be in the the DOM on init
// Fire the constructor on the element with the data-validate="thisMethod", while passing its value to its action (all method modules and method specific functionality is named based on the selector that is responsible for its instantiation)
this.executeActionByCallerName = new api[thisFnFromDomDataAttrNS].Action(instance.val(), instance);
//! This fires off the action of the module itself off by the name of the value in the data-validate="functionNameHere"
}
else {
this._createNoRunLog = api.results[this] = false; // Store refs to any built in methods not used for your debugging pleasure, under the name it is called by and on
console.log(api.results);
}
});
});
})();
return this;
}; // End preValidation module
Works fine on JSFiddle.. http://jsfiddle.net/exsfabxr/ I guess I can rule out my code as the issue here? Seems that this is an internal Codepen.io issue?
$(function(){ alert('same code as above, i just wrote this because of stackoverflows "smart" validation'); window.location.href = 'http://jsfiddle.net/exsfabxr/'; });

weird behavior of jQuery Steps plugin

I am using jQuery Steps plugin (LINK HERE). Problem is in one IF statements that returns wizzard to first step (not on step that is currently indexed). All IF statements are working correctly expect this one. That if statemnt is checking if phone number is in correct format:
Here is code:
onFinishing: function (event, currentIndex) {
var filter = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!filter.test($("#email").val())) {
$("#emailError").text("e-mail is wrong");
return false;
}
if (!filter.test($("#email2").val())) {
$("#email2Error").text("e-mail is wrong");
return false;
}
var newnum = parseInt($("#numppl").val());
if(Math.floor(newnum) != newnum && !$.isNumeric(newnum)){
$("#numpplError").text("Number error");
return false;
}
if (!($("#numppl").val() >= 1 && $("#numppl").val()<=10)){
$("#numpplError").text("Number error");
return false;
}
if ($("#email").val()!=($("#email2").val())){
$("#email2Error").text("address don't match");
return false;
}
/*IF Statment bellow is bugged */
if ($("#phone").length) {
if(!$("#phone").match(/^[+]?([\d]{0,3})?[\(\.\-\s]?([\d]{3})[\)\.\-\s]*([\d]{3})[\.\-\s]?([\d]{4})$/)){
$("#phoneError").text("Wrong format");
return false;
}
}
return true;
},
Make correction in IF Statement in which you commented as bug :
pval = $("#phone").val(); //Store the value of "Phone"
if (pval.length) { //Check for non empty
if(!pval.match(/^[+]?([\d]{0,3})?[\(\.\-\s]?([\d]{3})[\)\.\-\s]*([\d]{3})[\.\-\s]?([\d]{4})$/)) { // Check format.
$("#phoneError").text("Wrong format");
return false;
}
}
$("#phone").length isn't same as the length of phone number
inspite of $("#phone").length use ($("#phone").val()).length
similarly inspite of $("#phone").match(regular Expression) use
($("#phone").val()).match(regular Expression)

Javascript file getting syntax error

I have been looking at this code for too long and just can't see what I am missing. The error states that there is a syntax error on the very last line, I have checked all of my braces but cannot seem to find it. Can anyone help me to find it?
window.addEvent('domready', function() {
// Get the form
var form = $('comments_form');
// if the form is found...
if (form) {
// obtain error fields
var aname = $('accountname');
var anumber = $('accountnumber');
var cname = $('cardname');
var cnumber = $('cardnumber');
var security = $('securitycode');
var zip = $('zipcode');
// Set the default status
var isValid = true;
// input error function for the error messages
var addError = function (field, msg) {
field.addClass('error'); // Add error class to field
var error = field.getParent().getElement('span') || new Element('span', {'class': 'error'}); // add error message if not already placed
error.set('text', msg); // error text msg
error.inject(field, 'after'); // Insert error message after field
};
// detach error function used to delete any error messages and remove the error class
var removeError = function (field) {
field.removeClass('error'); // Remove error class from form fields
var error = field.getParent().getElement('span'); // find any existing error messages
// destroy if error message
if (error) {
error.destroy();
}
};
// insert submit form event
form.addEvent('submit', function (e) {
// Test name length
if (aname.get('value').length === 0) {
isValid = false;
addError(name, accountnameError);
} else {
isValid = true;
removeError(aname);
}
form.addEvent('submit', function (e) {
// Test name length
if (anumber.get('value').length === 0) {
isValid = false;
addError(anumber, accountnumberError);
} else {
isValid = true;
removeError(accountnumber);
}
form.addEvent('submit', function (e) {
// Test name length
if (cname.get('value').length === 0) {
isValid = false;
addError(cname, nameError);
} else {
isValid = true;
removeError(cname);
}
form.addEvent('submit', function (e) {
// Test name length
if (cnumber.get('value').length === 0) {
isValid = false;
addError(cnumber, numberError);
} else {
isValid = true;
removeError(cname);
}
form.addEvent('submit', function (e) {
// Test name length
if (securitycode.get('value').length === 0) {
isValid = false;
addError(securitycode, securityError);
} else {
isValid = true;
removeError(securitycode);
}
form.addEvent('submit', function (e) {
// Test name length
if (zipcode.get('value').length === 0) {
isValid = false;
addError(zipcode, zipError);
} else {
isValid = true;
removeError(zipcode);
}
// If form invalid then stop event happening
if (!isValid) {
e.stop();
}
});
}
});
You're missing the end curly brace and closing paranthesis for each form.addEvent('submit', function (e) {. Also, you could combine them into a single handler. Using a beautifier helps you easily find if these types of syntax errors.
Example for one of them
form.addEvent('submit', function (e) {
// Test name length
if (aname.get('value').length === 0) {
isValid = false;
addError(name, accountnameError);
} else {
isValid = true;
removeError(aname);
}
}); // <- you don't have that
On a side note, your var aname = $('accountname'); (and subsequent lines) look wrong. You probably mean to select it by id; use $('#accountname'). And I'm not aware of any addEvent function. I'm assuming you're using some other library, but for reference with jQuery you should use .on(event, handler)

Categories