This question already has answers here:
Sort the values in the jQuery Autocomplete
(5 answers)
Closed 7 years ago.
I am having a problem with sorting an auto-complete result with JQuery UI.
from the image above when a user decides to search for garlic and starts by entering "g" it should show "garlic" first and not egg first.
See my code. The ingredients in ingredient.txt at a point is sorted alphabetically so i think that is where the problem is..
How do I go about this
$(function() {
jQuery.get('ingredients.txt', function(data) {
var availableTags = data.split(', ');
availableTags = availableTags.sort();
//console.log("The available Tag are "+ availableTags);
function split( val ) {
return val.split(",");
}
function extractLast( term ) {
return split( term ).pop().sort();
}
// don't navigate away from the field on tab when selecting an item
$( "#tags" ).bind( "keydown", function( event ) {
if ( event.keyCode === $.ui.keyCode.TAB && $( this ).autocomplete( "instance" ).menu.active ) {
event.preventDefault();
}
}).autocomplete({
minLength: 0,
source: function( request, response ) {
// delegate back to autocomplete, but extract the last term
response( $.ui.autocomplete.filter(
availableTags, extractLast( request.term ) ) );
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
// var projectDiv = '<div class="projectBody" id="selectedItems">'+ ui.item.value +'</div>';
terms.push( ui.item.value );
for(var i = 0; i < terms.length; i++) {
var elements = terms[i];
}
// add placeholder to get the comma-and-space at the end
terms.push( "" );
//$("#tags").val().wrap( "<div class='new'>nn</div>" );
this.value = terms.join( "," );
return false;
}
});
});
});
After much looking this is what i came up with.
<script type="text/javascript">
$(function() {
jQuery.get('ingredients.txt', function(data) {
var availableTags = data.split(', ');
availableTags = availableTags.sort();
//console.log("The available Tag are "+ availableTags);
function split( val ) {
return val.split(", ");
}
function extractLast( term ) {
return split( term ).pop();
}
$( "#tags" )
// don't navigate away from the field on tab when selecting an item
.bind( "keydown", function( event ) {
if ( event.keyCode === $.ui.keyCode.TAB &&
$( this ).autocomplete( "instance" ).menu.active ) {
event.preventDefault();
}
})
.autocomplete({
minLength: 0,
source: function( request, response ) {
var term = $.ui.autocomplete.escapeRegex(extractLast(request.term))
// Create two regular expressions, one to find suggestions starting with the user's input:
, startsWithMatcher = new RegExp("^" + term, "i")
, startsWith = $.grep(availableTags, function(value) {
return startsWithMatcher.test(value.label || value.value || value);
})
// ... And another to find suggestions that just contain the user's input:
, containsMatcher = new RegExp(term, "i")
, contains = $.grep(availableTags, function (value) {
return $.inArray(value, startsWith) < 0 &&
containsMatcher.test(value.label || value.value || value);
});
// Supply the widget with an array containing the suggestions that start with the user's input,
// followed by those that just contain the user's input.
response(startsWith.concat(contains));
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
// var projectDiv = '<div class="projectBody" id="selectedItems">'+ ui.item.value +'</div>';
terms.push( ui.item.value );
for(var i = 0; i < terms.length; i++)
{
var elements = terms[i];
}
console.log('<div class="projectBody" id="selectedItems">'+ ui.item.value +'</div>');
$("#selectedItems").css("backgroundColor", "blue");
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( "," );
return false;
}
});
});
});
</script>
Related
I am trying to write a jQuery method which watches for changes of inputs inside a given form element:
(function($) {
$.fn.filter = function(options) {
console.log('Outside');
var self = this;
var settings = $.extend({}, options);
this.on('change', ':input', function(e) {
console.log('Inside');
$(self).serialize(); // Here is the problem
});
return this;
}
})(jQuery);
$('#filter-form').filter();
When I use $(self).serialize();, the function being called again. I expect that the 'Outside' part only runs once on initialization and not every time the input of the form changes.
I do not understand what is happening here. I'd appreciate if someone could explain to me why this is happening!
The issue is that you are redefining jQuery's filter method, which it internally uses in its serialize method. If you change the name, it will work. The definition of serialize is shown below:
jQuery.fn.extend( {
serialize: function() {
return jQuery.param( this.serializeArray() );
},
serializeArray: function() {
return this.map( function() {
// Can add propHook for "elements" to filter or add form elements
var elements = jQuery.prop( this, "elements" );
return elements ? jQuery.makeArray( elements ) : this;
} )
.filter( function() {
var type = this.type;
// Use .is( ":disabled" ) so that fieldset[disabled] works
return this.name && !jQuery( this ).is( ":disabled" ) &&
rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
( this.checked || !rcheckableType.test( type ) );
} )
.map( function( _i, elem ) {
var val = jQuery( this ).val();
if ( val == null ) {
return null;
}
if ( Array.isArray( val ) ) {
return jQuery.map( val, function( val ) {
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
} );
}
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
} ).get();
}
} );
Working Example:
(function($) {
$.fn._filter = function(options) {
console.log('Outside');
var self = this;
var settings = $.extend({}, options);
this.on('change', ':input', function(e) {
console.log('Inside');
$(self).serialize();
});
return this;
}
})(jQuery);
$('#filter-form')._filter();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="filter-form">
<input type="text">
</form>
I'm using a custom WooCommerce checkout.js file for a plugin I'm creating but I'm having an issue whereby the billing address is getting saved as the shipping address when the order is placed.
Here is the custom 'checkout.js' code:
/* global wc_checkout_params */
jQuery( function( $ ) {
// wc_checkout_params is required to continue, ensure the object exists
if ( typeof wc_checkout_params === 'undefined' ) {
return false;
}
console.log('Start');
$.blockUI.defaults.overlayCSS.cursor = 'default';
var wc_checkout_form = {
updateTimer: false,
dirtyInput: false,
xhr: false,
$order_review: $( '#order_review' ),
$checkout_form: $( 'form.checkout' ),
init: function() {
console.log('Init');
$( document.body ).bind( 'updated_checkout', this.updated_checkout );
$( document.body ).bind( 'update_checkout', this.update_checkout );
$( document.body ).bind( 'init_checkout', this.init_checkout );
$( document.body ).bind( 'country_to_state_changed', this.upgrade_state_fields );
$( document.body ).on( 'change', 'select.country_to_state, input.country_to_state', this.upgrade_state_fields );
$( '.back-to-checkout' ).bind( 'click', this.close_drawer );
// Payment methods
this.$checkout_form.on( 'click', 'input[name="payment_method"]', this.payment_method_selected );
if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) ) {
this.$order_review.on( 'click', 'input[name="payment_method"]', this.payment_method_selected );
}
$( 'button.details-next-button' ).bind( 'click', this.prevent_checkout );
// Form submission
$( document.body ).on( 'click', '#place_order', this.submit );
this.$checkout_form.on( 'submit', this.submit );
// Inline validation
this.$checkout_form.on( 'blur change', '.input-text, select, input:checkbox', this.validate_field );
// Manual trigger
this.$checkout_form.on( 'update', this.trigger_update_checkout );
// Inputs/selects which update totals
this.$checkout_form.on( 'change', 'select.shipping_method, input[name^="shipping_method"], #ship-to-different-address input, .update_totals_on_change select, .update_totals_on_change input[type="radio"]', this.trigger_update_checkout );
this.$checkout_form.on( 'change', '.address-field select', this.input_changed );
this.$checkout_form.on( 'change', '.address-field input.input-text, .update_totals_on_change input.input-text', this.maybe_input_changed );
this.$checkout_form.on( 'change keydown', '.address-field input.input-text, .update_totals_on_change input.input-text', this.queue_update_checkout );
// Address fields
this.$checkout_form.on( 'change', '#ship-to-different-address input.mdl-switch__input', this.ship_to_different_address );
this.$checkout_form.on( 'change', '#order_notes input.mdl-switch__input', this.order_notes );
// Trigger events
this.$checkout_form.find( '#ship-to-different-address input' ).change();
this.init_payment_methods();
// Update on page load
if ( wc_checkout_params.is_checkout === '1' ) {
$( document.body ).trigger( 'init_checkout' );
}
if ( wc_checkout_params.option_guest_checkout === 'yes' ) {
$( 'input#createaccount' ).change( this.toggle_create_account ).change();
} else {
$( 'div.create-account' ).show();
}
jQuery('.woocommerce-message').remove();
},
init_payment_methods: function( selectedPaymentMethod ) {
console.log('Init payment_methods');
var $payment_methods = $( '.woocommerce-checkout' ).find( 'input[name="payment_method"]' );
// If there is one method, we can hide the radio input
if ( 1 === $payment_methods.length ) {
$payment_methods.eq(0).hide();
}
// If there was a previously selected method, check that one.
if ( selectedPaymentMethod ) {
$( '#' + selectedPaymentMethod ).prop( 'checked', true );
}
// If there are none selected, select the first.
if ( 0 === $payment_methods.filter( ':checked' ).length ) {
$payment_methods.eq(0).prop( 'checked', true );
}
// Trigger click event for selected method
$payment_methods.filter( ':checked' ).eq(0).trigger( 'click' );
wc_checkout_form.convert_payment_textinputs();
},
close_drawer: function() {
console.log('close_drawer');
jQuery('.mdl-layout__obfuscator.is-visible').click();
},
updated_checkout: function() {
console.log('updated_checkout');
jQuery('.mdl-layout__drawer .shipping input[type=radio]').each(function() {
var name = $(this).attr('name');
var id = $(this).attr('id');
$(this).attr('name','review_' + name);
$(this).attr('id','review_' + id);
});
componentHandler.upgradeDom();
window.setTimeout(function() {
jQuery('.mdl-textfield').each(function() {
if (!jQuery(this).is('.validate-required')) {
jQuery(this).removeClass('is-invalid');
}
if (jQuery(this).find('.mdl-textfield__input').val() != '') {
jQuery(this).addClass('is-dirty');
}
});
},10);
if (jQuery('.woocommerce-error').length > 0) {
var message ='';
jQuery('.woocommerce-error').find('li').each(function() {
message+= jQuery(this).text();
});
jQuery('.woocommerce-error').remove();
var snackbarContainer = document.querySelector('#error-snackbar');
var data = {
message: message,
timeout: 5000
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
}
jQuery('.woocommerce-message').remove();
},
convert_payment_textinputs: function() {
console.log('convert_payment_textinputs');
jQuery('.payment_box').find('input[type=text],input[type=tel],input[type=email],input[type=date],input[type=password]').each(function() {
if (jQuery(this).parent().is('[data-upgraded]') || jQuery(this).parent().find('input[type=text],input[type=tel],input[type=email],input[type=date],input[type=password]').length > 1) {
return;
}
jQuery(this).parent().addClass('mdl-textfield mdl-js-textfield mdl-textfield--floating-label');
jQuery(this).addClass('mdl-textfield__input');
jQuery(this).parent().find('label').addClass('mdl-textfield__label');
componentHandler.upgradeElement(jQuery(this).parent()[0]);
});
},
upgrade_state_fields: function() {
console.log('upgrade_state_fields');
$('#billing_state,#shipping_state').addClass('mdl-textfield__input');
$('#billing_state,#shipping_state,#billing_country, #shipping_country').each(function() {
jQuery(this).parent().removeAttr('data-upgraded');
jQuery(this).find('option[value=""]').text('');
componentHandler.upgradeElement(jQuery(this).parent()[0]);
});
window.setTimeout(function() {
jQuery('.mdl-textfield').each(function() { jQuery(this).find('.mdl-textfield__input').each(function() {
if (jQuery(this).val() && jQuery(this).val() != '') {
jQuery(this).parent().addClass('is-dirty');
} else {
jQuery(this).parent().removeClass('is-dirty');
}
if (jQuery(this).attr('placeholder') && jQuery(this).attr('placeholder') != '') {
jQuery(this).parent().addClass('has-placeholder');
} else {
jQuery(this).parent().removeClass('has-placeholder');
}
}); });
},100);
},
get_payment_method: function() {
console.log('get_payment_method');
return wc_checkout_form.$checkout_form.find( 'input[name="payment_method"]:checked' ).val();
},
payment_method_selected: function() {
console.log('payment_method_selected');
if ( $( '.payment_methods input.input-radio' ).length > 1 ) {
var target_payment_box = $( 'div.payment_box.' + $( this ).attr( 'ID' ) );
if ( $( this ).is( ':checked' ) && ! target_payment_box.is( ':visible' ) ) {
$( 'div.payment_box' ).filter( ':visible' ).slideUp( 250 );
if ( $( this ).is( ':checked' ) ) {
$( 'div.payment_box.' + $( this ).attr( 'ID' ) ).slideDown( 250 );
}
}
} else {
$( 'div.payment_box' ).show();
}
if ( $( this ).data( 'order_button_text' ) ) {
$( '#place_order' ).val( $( this ).data( 'order_button_text' ) );
} else {
$( '#place_order' ).val( $( '#place_order' ).data( 'value' ) );
}
},
toggle_create_account: function() {
console.log('toggle_create_account');
$( 'div.create-account' ).hide();
if ( $( this ).is( ':checked' ) ) {
$( 'div.create-account' ).slideDown();
}
},
init_checkout: function() {
console.log('init_checkout');
$( '#billing_country, #shipping_country, .country_to_state' ).change();
$( document.body ).trigger( 'update_checkout' );
},
maybe_input_changed: function( e ) {
console.log('maybe_input_changed');
if ( wc_checkout_form.dirtyInput ) {
wc_checkout_form.input_changed( e );
}
},
input_changed: function( e ) {
console.log('input_changed');
wc_checkout_form.dirtyInput = e.target;
wc_checkout_form.maybe_update_checkout();
},
queue_update_checkout: function( e ) {
console.log('queue_update_checkout');
var code = e.keyCode || e.which || 0;
if ( code === 9 ) {
return true;
}
wc_checkout_form.dirtyInput = this;
wc_checkout_form.reset_update_checkout_timer();
wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.maybe_update_checkout, '1000' );
},
trigger_update_checkout: function() {
console.log('trigger_update_checkout');
wc_checkout_form.reset_update_checkout_timer();
wc_checkout_form.dirtyInput = false;
$( document.body ).trigger( 'update_checkout' );
},
maybe_update_checkout: function() {
console.log('maybe_update_checkout');
var update_totals = true;
if ( $( wc_checkout_form.dirtyInput ).length ) {
var $required_inputs = $( wc_checkout_form.dirtyInput ).closest( 'div' ).find( '.address-field.validate-required' );
if ( $required_inputs.length ) {
$required_inputs.each( function() {
if ( $( this ).find( 'input.input-text' ).val() === '' ) {
update_totals = false;
}
});
}
}
if ( update_totals ) {
wc_checkout_form.trigger_update_checkout();
}
},
ship_to_different_address: function() {
console.log('ship_to_different_address');
if ( $( this ).is( ':checked' ) ) {
$( '#shipping_address_section' ).slideDown();
} else {
$( '#shipping_address_section' ).slideUp();
}
},
order_notes: function() {
console.log('order_notes');
if ( $( this ).is( ':checked' ) ) {
$( '#order_notes_section' ).slideDown();
} else {
$( '#order_notes_section' ).slideUp();
}
},
reset_update_checkout_timer: function() {
console.log('reset_update_checkout_timer');
clearTimeout( wc_checkout_form.updateTimer );
},
validate_field: function() {
console.log('validate_field');
var $this = $( this ),
$parent = $this.closest( '.form-row' ),
validated = true;
if ( $parent.is( '.validate-required' ) ) {
if ( 'checklox' === $this.attr( 'type' ) && ! $this.is( ':checked' ) ) {
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
validated = false;
} else if ( $this.val() === '' ) {
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
validated = false;
}
}
if ( $parent.is( '.validate-email' ) ) {
if ( $this.val() ) {
/* https://stackoverflow.com/questions/2855865/jquery-validate-e-mail-address-regex */
var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);
if ( ! pattern.test( $this.val() ) ) {
$parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-email' );
validated = false;
}
}
}
if ( validated ) {
$parent.removeClass( 'woocommerce-invalid woocommerce-invalid-required-field' ).addClass( 'woocommerce-validated' );
}
},
update_checkout: function( event, args ) {
console.log('update_checkout');
// Small timeout to prevent multiple requests when several fields update at the same time
wc_checkout_form.reset_update_checkout_timer();
wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.update_checkout_action, '5', args );
},
update_checkout_action: function( args ) {
console.log('update_checkout_action');
if ( wc_checkout_form.xhr ) {
wc_checkout_form.xhr.abort();
}
if ( $( 'form.checkout' ).length === 0 ) {
return;
}
args = typeof args !== 'undefined' ? args : {
update_shipping_method: true
};
var country = $( '#billing_country' ).val(),
state = $( '#billing_state' ).val(),
postcode = $( 'input#billing_postcode' ).val(),
city = $( '#billing_city' ).val(),
address = $( 'input#billing_address_1' ).val(),
address_2 = $( 'input#billing_address_2' ).val(),
s_country = country,
s_state = state,
s_postcode = postcode,
s_city = city,
s_address = address,
s_address_2 = address_2;
has_full_address = true;
if ( $( '#ship-to-different-address' ).find( 'input' ).is( ':checked' ) ) {
s_country = $( '#shipping_country' ).val();
s_state = $( '#shipping_state' ).val();
s_postcode = $( 'input#shipping_postcode' ).val();
s_city = $( '#shipping_city' ).val();
s_address = $( 'input#shipping_address_1' ).val();
s_address_2 = $( 'input#shipping_address_2' ).val();
}
var data = {
security: wc_checkout_params.update_order_review_nonce,
payment_method: wc_checkout_form.get_payment_method(),
country: country,
state: state,
postcode: postcode,
city: city,
address: address,
address_2: address_2,
s_country: s_country,
s_state: s_state,
s_postcode: s_postcode,
s_city: s_city,
s_address: s_address,
s_address_2: s_address_2,
has_full_address: has_full_address,
post_data: $( 'form.checkout' ).serialize()
};
console.log(data);
if ( false !== args.update_shipping_method ) {
var shipping_methods = {};
$( 'select.shipping_method, input[name^="shipping_method"][type="radio"]:checked, input[name^="shipping_method"][type="hidden"]' ).each( function() {
shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val();
} );
data.shipping_method = shipping_methods;
}
$( '.woocommerce-checkout-payment, .woocommerce-checkout-review-order-table' ).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
wc_checkout_form.xhr = $.ajax({
type: 'POST',
url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'update_order_review' ),
data: data,
success: function( data ) {
var selectedPaymentMethod = $( '.woocommerce-checkout input[name="payment_method"]:checked' ).attr( 'id' );
// Reload the page if requested
if ( 'true' === data.reload ) {
window.location.reload();
return;
}
// Remove any notices added previously
$( '.woocommerce-NoticeGroup-updateOrderReview' ).remove();
var termsCheckBoxChecked = $( '#terms' ).prop( 'checked' );
window.fragmentss = data.fragments;
// Always update the fragments
if ( data && data.fragments ) {
$.each( data.fragments, function ( key, value ) {
$( key ).replaceWith( value );
$( key ).unblock();
} );
}
// Recheck the terms and conditions box, if needed
if ( termsCheckBoxChecked ) {
$( '#terms' ).prop( 'checked', true );
}
// Check for error
if ( 'failure' === data.result ) {
var $form = $( 'form.checkout' );
// Remove notices from all sources
$( '.woocommerce-error, .woocommerce-message' ).remove();
// Add new errors returned by this event
if ( data.messages ) {
$form.prepend( '<div class="woocommerce-NoticeGroup-updateOrderReview">' + data.messages + '</div>' );
} else {
$form.prepend( data );
}
// Lose focus for all fields
$form.find( '.input-text, select, input:checkbox' ).blur();
// Scroll to top
$( 'html, body' ).animate( {
scrollTop: ( $( 'form.checkout' ).offset().top - 100 )
}, 1000 );
}
// Re-init methods
wc_checkout_form.init_payment_methods( selectedPaymentMethod );
// Fire updated_checkout e
$( document.body ).trigger( 'updated_checkout', [ data ] );
}
});
},
prevent_checkout: function(e) {
console.log('prevent_checkout');
console.log('clickprevent');
e.preventDefault();
},
submit: function(e) {
console.log('submit');
e.preventDefault();
wc_checkout_form.reset_update_checkout_timer();
var $form = $( wc_checkout_form.$checkout_form );
console.log('Form:' + $form);
if ( $form.is( '.processing' ) ) {
return false;
}
$( document )
.on(
'stripeError',
wc_checkout_form.updated_checkout
)
.on(
'checkout_error',
wc_checkout_form.updated_checkout
);
// Trigger a handler to let gateways manipulate the checkout if needed
if ( $form.triggerHandler( 'checkout_place_order' ) !== false && $form.triggerHandler( 'checkout_place_order_' + wc_checkout_form.get_payment_method() ) !== false ) {
$form.addClass( 'processing' );
var form_data = $form.data();
console.log ('Form Data:' + form_data );
if ( 1 !== form_data['blockUI.isBlocked'] ) {
$form.block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
}
// ajaxSetup is global, but we use it to ensure JSON is valid once returned.
$.ajaxSetup( {
dataFilter: function( raw_response, dataType ) {
// We only want to work with JSON
if ( 'json' !== dataType ) {
return raw_response;
}
try {
// Check for valid JSON
var data = $.parseJSON( raw_response );
if ( data && 'object' === typeof data ) {
// Valid - return it so it can be parsed by Ajax handler
return raw_response;
}
} catch ( e ) {
// Attempt to fix the malformed JSON
var valid_json = raw_response.match( /{"result.*"}/ );
if ( null === valid_json ) {
console.log( 'Unable to fix malformed JSON' );
} else {
console.log( 'Fixed malformed JSON. Original:' );
console.log( raw_response );
raw_response = valid_json[0];
}
}
return raw_response;
}
} );
console.log($form.serialize());
alert($form.serialize());
$.ajax({
type: 'POST',
url: wc_checkout_params.checkout_url,
data: $form.serialize(),
dataType: 'json',
This is the '$form.serialize()' data that is getting sent by the AJAX request:
billing_first_name=Testname&billing_last_name=Testlastname&billing_phone=0800000000&billing_email=test%40wpmad.com&billing_country=GB&billing_address_1=4+Test+Street&billing_address_2=&billing_city=Test+City&billing_state=Worcestershire&billing_postcode=DY11+1JR&shipping_first_name=Testname&shipping_last_name=Testlastname&shipping_company=&shipping_country=GB&shipping_address_1=99+Test+Street&shipping_address_2=&shipping_city=Worcester&shipping_state=Worcestershire&shipping_postcode=WR1+2DS&order_comments=&shipping_method%5B0%5D=free_shipping%3A1&terms=on&terms-field=1&_wpnonce=34d56c864b&_wp_http_referer=%2F%3Fwc-ajax%3Dupdate_order_review
As above, the order is placed, but the shipping address is replaced with the billing address on the order confirmation page and within the WooCommerce orders in the admin.
Any ideas why? Any help would be greatly appreciated!
Problem solved.
The issue was being caused as the checkbox in the WooCommerce checkout had been changed to a Google Material design checkbox/switch and was not setting the value of the checkbox to '1'.
Effectively, the $form.serialize() data didn't contain the ship_to_different_address=1 that is required in the WooCommerce AJAX request.
Please give me a piece of advice.
I'm trying to write a function from 2 similar functions by combining its common parts.
From these 2 functions, I'd like to get "selected1" & "selected2", and use these value in the next function, calc.
var selected;
var selected1;
var selected2;
$( '.type1' ).bind('change', function () {
var values = [];
$( 'input.type1:checkbox:checked' ).each(function ( _, el ) {
values.push( base64decode( $( el ).data( 'val' ) ) );
if ( values.length > 1 ) {
values[0] = $.map(values[0], function (val, i) {
return values[0][i] | values[1][i];
});
values.splice(1,1);
}
});
selected1 = result;
calc();
});
$( '.type2' ).bind('change', function () {
var values = [];
$( 'input.type2:checkbox:checked' ).each(function ( _, el ) {
values.push( base64decode( $( el ).data( 'val' ) ) );
if ( values.length > 1 ) {
values[0] = $.map(values[0], function (val, i) {
return values[0][i] | values[1][i];
});
values.splice(1,1);
}
});
selected2 = result;
calc();
});
function calc () {
var selected3 = $.map(selected1, function (val, i) {
return selected1[i] & selected2[i];
});
selected = base64encode( selected3 );
overlays();
};
When I write as above, both selected1&2 are defined as global variables, so function calc works.
However, it doesn't work on my rewrite code. The firebug says "TypeError: a is undefined".
Here is my code:
function tab (checkedTab, selected) {
var values = [];
checkedTab.each(function ( _, el ) {
values.push( base64decode ($( el ).data( 'val' ) ) );
if ( values.length > 1 ) {
values[0] = $.map(values[0], function (val, i) {
return values[0][i] | values[1][i];
});
values.splice(1,1);
}
});
selected = values[0];
};
$( '.type1' ).bind('change', function () {
tab ($( 'input.type1:checkbox:checked' ), 'selected1');
calc();
});
$( '.type2' ).bind('change', function () {
tab ($( 'input.type2:checkbox:checked' ), 'selected2');
calc();
});
Could someone please tell me why my code doesn't work?
Thank you very much for your comments, Peter.
After debugging variables, it finally works!
This may not be the best way, but I will post my rewrite code as reference.
var values;
function tab (checkedTab) {
values = [];
checkedTab.each(function ( _, el ) {
values.push( base64decode ($( el ).data( 'val' ) ) );
if ( values.length > 1 ) {
values[0] = $.map(values[0], function (val, i) {
return values[0][i] | values[1][i];
});
values.splice(1,1);
}
});
return values[0];
};
$( '.type1' ).bind('change', function () {
selected1 = tab ($( 'input.type1:checkbox:checked' ));
calc();
});
$( '.type2' ).bind('change', function () {
selected2 = tab ($( 'input.type2:checkbox:checked' ));
calc();
});
I am trying to make trigger based auto-complete in textarea using jQuery autocomplete. (when you type "#", the autocomplete starts).
Till now I am able to implement it for the case where the data is an array.
HTML
<textarea class="searchBox" rows="10" cols="20"></textarea>
jQuery
var triggered = false; // By default trigger is off
var trigger = "#"; // Defining the trigger
$(".searchBox").autocomplete({
source: [ // defining the source
"A",
"Apple",
"S",
"D",
"q"],
search: function () { // before search, if not triggerred, don't search
if (!triggered) {
return false;
}
},
select: function (event, ui) { // invokes the data source, search starts
var text = this.value;
var pos = text.lastIndexOf(trigger);
this.value = text.substring(0, pos + trigger.length) + ui.item.value;
triggered = false;
return false;
},
focus: function (event, ui) {
return false;
},
minLength: 0 // minimum length of 0 require to invoke search
});
$('.searchBox').keyup(function (e) {
if (e.keyCode == 38 || e.keyCode == 40) {
return;
}
var text = this.value;
var len = text.length;
var last;
var query;
var index;
if (triggered) {
index = text.lastIndexOf(trigger);
query = text.substring(index + trigger.length);
$(this).autocomplete("search", query);
} else if (len >= trigger.length) {
last = text.substring(len - trigger.length);
triggered = (last === trigger);
}
});
Here is its fiddle : http://jsfiddle.net/5x9ZR/4/
Now I was trying to implement it when the data is in the form of json object.
But the autocomplete isn't responding with results.
I tried it bare. ( without any trigger mechanism ). It isnt displaying any results.
Code:
HTML
<textarea class="searchBox" rows="10" cols="20"></textarea>
jQuery
var triggered = false; // By default trigger is off
var trigger = "#"; // Defining the trigger
var data = [{
"name": "needmoreinfo",
"email": "needmoreinfo"
}, {
"name": "explained",
"email": "explained"
}, {
"name": "raypipeline09",
"email": "raypipeline09"
}];
$(".searchBox").autocomplete({
source: data,
search: function () { // before search, if not triggred, don't search
if (!triggered) {
return false;
}
},
select: function (event, ui) { // invokes the data source, search starts
var text = this.value;
var pos = text.lastIndexOf(trigger);
this.value = text.substring(0, pos + trigger.length) + ui.item.email;
triggered = false;
return false;
},
focus: function () {
return false;
},
minLength: 0 // minimum length of 0 require to invoke search
})._renderItem = function (ul, item) {
return $("<li>")
.append("<a>" + item.name + "<br>" + item.email + "</a>")
.appendTo(ul);
};
$('.searchBox').keyup(function (e) {
if (e.keyCode == 38 || e.keyCode == 40) {
return;
}
var text = this.value;
var len = text.length;
var last;
var query;
var index;
if (triggered) {
index = text.lastIndexOf(trigger);
query = text.substring(index + trigger.length);
$(this).autocomplete("search", query);
} else if (len >= trigger.length) {
last = text.substring(len - trigger.length);
triggered = (last === trigger);
}
});
Here is the fiddle of what I tried. http://jsfiddle.net/HGxSX/
Where I am going wrong ? Please explain.
Solved it.
There is a bug in the jQuery due to which this was happening.
The data has to be in the form of "label" and "value". Otherwise it doesn't works.
Thanks to anyone who tried.
Here is the final working fiddle : http://jsbin.com/qakefini/7
PS: I tried making account on the jQuery website but could not login. Its saying some redirection problem. If someone has an account, please report the bug.
Doesnt seem to like json strings without a label - it uses this on its keyup to check values..
see your updated/destroyed fiddle:
http://jsfiddle.net/jFIT/HGxSX/1/
var data = [
{
value: "jquery",
label: "jQuery",
desc: "the write less, do more, JavaScript library",
icon: "jquery_32x32.png",
name: "needmoreinfo",
email: "needmoreinfo"
},{
and in your autocomplete function the renderitem was missing some things, you should update the version of jquery your using btw.. seems alot simpler in 1.9:
}).data( "autocomplete" )._renderItem = function( ul, item ) { console.log('wut');
return $( "<li>" )
.data( "item.autocomplete", item )
.append( "<a>" + item.email + "<br>" + item.name + "</a>" )
.appendTo( ul );
};
I am using jQuery autocomplete which is working fine with existing element but not with dynamically added element.
Here is my autocomplete code (Which I have changed a bit)
$(function() {
(function( $ ) {
$.widget( "ui.combobox", {
_create: function() {
var self = this,
select = this.element.hide(),
selected = select.children( ":selected" ),
value = selected.val() ? selected.text() : "";
var input = this.input = $( ".editableCombobox" ) // your input box
//.insertAfter( select )
.val( value )
.autocomplete({
delay: 0,
minLength: 0,
source: function( request, response ) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
response( select.children( "option" ).map(function() {
var text = $( this ).text();
if ( this.value && ( !request.term || matcher.test(text) ) )
return {
label: text.replace(
new RegExp(
"(?![^&;]+;)(?!<[^<>]*)(" +
$.ui.autocomplete.escapeRegex(request.term) +
")(?![^<>]*>)(?![^&;]+;)", "gi"
), "$1" ),
value: text,
option: this
};
}) );
},
select: function( event, ui ) {
ui.item.option.selected = true;
self._trigger( "selected", event, {
item: ui.item.option
});
},
change: function( event, ui ) {
if ( !ui.item ) {
var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ),
valid = false;
select.children( "option" ).each(function() {
if ( $( this ).text().match( matcher ) ) {
this.selected = valid = true;
return false;
}
});
//if ( !valid ) {
// remove invalid value, as it didn't match anything
//$( this ).val( "" );
//select.val( "" );
//input.data( "autocomplete" ).term = "";
//return false;
// }
}
}
})
.addClass( "ui-widget ui-widget-content ui-corner-left" );
input.data( "autocomplete" )._renderItem = function( ul, item ) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a>" + item.label + "</a>" )
.appendTo( ul );
};
this.button = $( "<button type='button'> </button>" )
.attr( "tabIndex", -1 )
.attr( "title", "Show All Items" )
.insertAfter( input )
.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass( "ui-corner-all" )
//.addClass( "ui-corner-right ui-button-icon" )
.live('click',function() {
// close if already visible
if ( $(this).prev().autocomplete( "widget" ).is( ":visible" ) ) {
$(this).prev().autocomplete( "close" );
return;
}
// work around a bug (likely same cause as #5265)
$( this ).blur();
// pass empty string as value to search for, displaying all results
$(this).prev().autocomplete( "search", "" );
$(this).prev().focus();
});
},
destroy: function() {
this.input.remove();
this.button.remove();
this.element.show();
$.Widget.prototype.destroy.call( this );
}
});
})( jQuery );
$( "#combobox" ).combobox();
$( "#toggle" ).live('click',function() {
$( "#combobox" ).toggle();
});
});
Here is my code which adds new element
var selectedRow = $('#contactGroup'+rowId);
var clonedRow = selectedRow.clone();
selectedRow.after(clonedRow);
After reading many similar questions I think .live might help but not sure where to use it.
EDIT:
I tried removing live.
New code for Autocomplete
$(function() {
(function( $ ) {
$.widget( "ui.combobox", {
_create: function() {
var self = this,
select = this.element.hide(),
selected = select.children( ":selected" ),
value = selected.val() ? selected.text() : "";
var input = this.input = $( ".editableCombobox" ) // your input box
//.insertAfter( select )
.val( value )
.autocomplete({
delay: 0,
minLength: 0,
source: function( request, response ) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
response( select.children( "option" ).map(function() {
var text = $( this ).text();
if ( this.value && ( !request.term || matcher.test(text) ) )
return {
label: text.replace(
new RegExp(
"(?![^&;]+;)(?!<[^<>]*)(" +
$.ui.autocomplete.escapeRegex(request.term) +
")(?![^<>]*>)(?![^&;]+;)", "gi"
), "$1" ),
value: text,
option: this
};
}) );
},
select: function( event, ui ) {
ui.item.option.selected = true;
self._trigger( "selected", event, {
item: ui.item.option
});
},
change: function( event, ui ) {
if ( !ui.item ) {
var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ),
valid = false;
select.children( "option" ).each(function() {
if ( $( this ).text().match( matcher ) ) {
this.selected = valid = true;
return false;
}
});
//if ( !valid ) {
// remove invalid value, as it didn't match anything
//$( this ).val( "" );
//select.val( "" );
//input.data( "autocomplete" ).term = "";
//return false;
// }
}
}
})
.addClass( "ui-widget ui-widget-content ui-corner-left" );
input.data( "autocomplete" )._renderItem = function( ul, item ) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a>" + item.label + "</a>" )
.appendTo( ul );
};
this.button = $( "<button type='button'> </button>" )
.attr( "tabIndex", -1 )
.attr( "title", "Show All Items" )
.insertAfter( input )
.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass( "ui-corner-all" )
.addClass( "ui-corner-right ui-button-icon" )
.click(function() {
// close if already visible
if ( $(this).prev().autocomplete( "widget" ).is( ":visible" ) ) {
$(this).prev().autocomplete( "close" );
return;
}
// work around a bug (likely same cause as #5265)
$( this ).blur();
// pass empty string as value to search for, displaying all results
$(this).prev().autocomplete( "search", "" );
$(this).prev().focus();
});
},
destroy: function() {
this.input.remove();
this.button.remove();
this.element.show();
$.Widget.prototype.destroy.call( this );
}
});
})( jQuery );
$( "#combobox" ).combobox();
$( "#toggle" ).click(function() {
$( "#combobox" ).toggle();
});
});
Binding newly added element in clone method
var selectedRow = $('#contactGroup'+rowId);
var clonedRow = selectedRow.clone();
selectedRow.after(clonedRow);
$(('#contactGroup'+rowId) .editableCombobox).autocomplete( "search", "" );
You can't use 'live' on autocomplete, as far as I know.
Place your autocomplete options in a function that expects the field as parameter, on which you want to apply the autocomplete method.
function enable_autocomplete(InputField) {
$(InputField).autocomplete({
source: availableTags
});
}
Then, after cloning the field, call this function with the cloned field.
enable_autocomplete(ClonedField);
I've written you an easy example, what makes it much easier to understand what I am trying to say ;-)
Edit: I've written another example based on the combobox example from jQueryUIs website.
You can use 'live', although these days you should be using the 'on' method.
See this thread for working examples (including one that uses 'on' if you drill down in to some of the hidden answers):
Bind jQuery UI autocomplete using .live()
Cheers
Matt