Related
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.
NOTE:: I have replaced some code with comments to make it much shorter and more readable
I am working with Javascript (Ajax) and PHP (Laravel). I have run into a predicament! When calling an ajax function I have two listener to show a loading symbol while the ajax is processing, and then hide it again once it's done.
$( document ).ajaxStart( function() {
$( '#loading' ).css( 'display', 'block' );
} );
$( document ).ajaxStop( function() {
$( '#loading' ).css( 'display', '' );
} );
When you click on the submit button, the HTML onclick says to go to this function which is working just fine:
function submit( button ) {
var table = $( button ).closest( '.popup' ).find( 'table:first' );
var verified = verifyTable( table );
if ( verified == -1 ) {
return -1;
}
if ( table.hasClass( 'campaigns' ) ) {
campaign = table.find( '.campaign:first' );
submitNewCampaign( campaign );
} else if ( table.hasClass( 'groups' ) ) {
groups = table.find( 'tr.group' );
for ( var i = 0; i < groups.length; ++i ) {
submitNewGroup( groups.eq( i ) );
}
} else if ( table.hasClass( 'keywords' ) ) {
keywords = table.find( 'tr.keyword' );
for ( var i = 0; i < keywords.length; ++i ) {
submitNewKeyword( keywords.eq( i ) );
}
}
closePopup();
}
If from there you are sent to submitNewCampaign(), everything works just fine.
function submitNewCampaign( campaign ) {
// Set campaign variables
$.ajax({
url : '/ajax/addCampaign',
type : 'POST',
data : { // set campaign data },
success : function( result ) {
// get all groups in campaigns and loop through
for ( var i = 0; i < groups.length; ++i ) {
// set group variables
$.ajax({
url : '/ajax/addGroup',
type : 'POST',
async : false,
data : { // set group data },
success : function( result ) {
// get all keywords in group and loop
for ( var i = 0; i < keywords.length; i++ ) {
// set keyword variables
$.ajax({
url : '/ajax/addKeyword',
type : 'POST',
async : false,
data : { // set keyword data },
success : function( result ) { // done },
error : function( result ) {
alert( "error adding new keyword (name - " + keyword_name + ")");
console.log( result );
}
);
}
},
error : function( result ) {
alert( "error adding new group (name - " + group_name + ")" );
console.log( result );
}
});
}
},
error : function( result ) {
alert( "error adding new campaign (name - " + campaign_name + "): " + JSON.parse( xhr.responseText ) );
}
});
}
However if you are send to either submitNewGroup() or submitNewKeyword(), the loading image does not appear.
function submitNewGroup ( group ) {
// set group variables
$.ajax({
url : '/ajax/addGroup',
type : 'POST',
async : false,
data : { // set group data },
success : function( result ) {
// get all keywords in group and loop
for ( var i = 0; i < keywords.length; ++i ) {
// set all keyword variables
$.ajax({
url : '/ajax/addKeyword',
type : 'POST',
async : false,
data : { // set all keyword data },
success : function( result ) { // done },
error : function( result ) {
console.log( result );
}
});
}
},
error : function( result ) {
console.log( result );
}
});
}
function submitNewKeyword( keyword ) {
// set keyword variables
$.ajax({
url : '/ajax/addKeyword',
type : 'POST',
async : false,
data : { // set keyword data },
success : function( result ) { //done },
error : function( result ) {
console.log( result );
}
});
}
IMPORTANT: No errors appear in the log when any of the three functions are called. All three complete their assigned tasks with no issues.
I tried a few things and the one that made it work was: I removed async from the outer most ajax call for both group and keyword
i am struggling with importing the following JS code
'use strict';
;( function ( document, window, index )
{
var inputs = document.querySelectorAll( '.inputfile' );
Array.prototype.forEach.call( inputs, function( input )
{
var label = input.nextElementSibling,
labelVal = label.innerHTML;
input.addEventListener( 'change', function( e )
{
var fileName = '';
if( this.files && this.files.length > 1 )
fileName = ( this.getAttribute( 'data-multiple-caption' ) || '' ).replace( '{count}', this.files.length );
else
fileName = e.target.value.split( '\\' ).pop();
if( fileName )
label.querySelector( 'span' ).innerHTML = fileName;
else
label.innerHTML = labelVal;
});
// Firefox bug fix
input.addEventListener( 'focus', function(){ input.classList.add( 'has-focus' ); });
input.addEventListener( 'blur', function(){ input.classList.remove( 'has-focus' ); });
});
}( document, window, 0 ));
On this website: https://pimarketing.flywheelsites.com/careers/
The error i get in the debugger is: TypeError: label is null.
I guess something goes wrong with input.nextElementSibling. It should show the inputfile name but it doesn't. Any help would be appriciated
I think null error mean it did no find that element which mean it is better to check if that element exist or not first than do rest of thing after. Please Try following code and let me know if it works for you.
'use strict';
( function ( document, window, index )
{
var inputs = document.querySelectorAll( '.inputfile' );
Array.prototype.forEach.call( inputs, function( input )
{
var label = input.nextElementSibling;
if(label){ //checking if label exist or not
var labelVal = label.innerHTML;
input.addEventListener( 'change', function( e )
{
var fileName = '';
if( this.files && this.files.length > 1 )
fileName = ( this.getAttribute( 'data-multiple-caption' ) || '' ).replace( '{count}', this.files.length );
else
fileName = e.target.value.split( '\\' ).pop();
if( fileName )
label.querySelector( 'span' ).innerHTML = fileName;
else
label.innerHTML = labelVal;
});
}
// Firefox bug fix
input.addEventListener( 'focus', function(){ input.classList.add( 'has-focus' ); });
input.addEventListener( 'blur', function(){ input.classList.remove( 'has-focus' ); });
});
}( document, window, 0 ));
I hope this will help you out.
I worked it out! It seems that it indeed couldnt find the element. I made the querySelector more specific and changed the label in HTML label for="file-0">
'use strict';
;( function ( document, window, index )
{
var inputs = document.querySelectorAll('#file-0');
Array.prototype.forEach.call( inputs, function( input )
{
var label = input.nextElementSibling;
if (!label) {
return;
}
var labelVal = label.innerHTML;
input.addEventListener( 'change', function( e )
{
var fileName = '';
if( this.files && this.files.length > 1 )
fileName = ( this.getAttribute( 'data-multiple-caption' ) || '' ).replace( '{count}', this.files.length );
else
fileName = e.target.value.split( '\\' ).pop();
if( fileName )
label.querySelector( 'span' ).innerHTML = fileName;
else
label.innerHTML = labelVal;
});
// Firefox bug fix
input.addEventListener( 'focus', function(){ input.classList.add( 'has-focus' ); });
input.addEventListener( 'blur', function(){ input.classList.remove( 'has-focus' ); });
});
}( document, window, 0 ));
I have a very simple menu, no sub menus and was curious how I would get the menu to close when I click on one of the links?
Plugin Home Page ->
Here is the code directly out of the .js file that comes in the zip when you download this menu. I really need to know what code to add and where to add it so that when I click a link it closes the menu.
;( function( $, window, undefined ) {
'use strict';
// global
var Modernizr = window.Modernizr, $body = $( 'body' );
$.DLMenu = function( options, element ) {
this.$el = $( element );
this._init( options );
};
// the options
$.DLMenu.defaults = {
// classes for the animation effects
animationClasses : { classin : 'dl-animate-in-1', classout : 'dl-animate-out-1' },
// callback: click a link that has a sub menu
// el is the link element (li); name is the level name
onLevelClick : function( el, name ) { return false; },
// callback: click a link that does not have a sub menu
// el is the link element (li); ev is the event obj
onLinkClick : function( el, ev ) { return false; }
};
$.DLMenu.prototype = {
_init : function( options ) {
// options
this.options = $.extend( true, {}, $.DLMenu.defaults, options );
// cache some elements and initialize some variables
this._config();
var animEndEventNames = {
'WebkitAnimation' : 'webkitAnimationEnd',
'OAnimation' : 'oAnimationEnd',
'msAnimation' : 'MSAnimationEnd',
'animation' : 'animationend'
},
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
};
// animation end event name
this.animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ] + '.dlmenu';
// transition end event name
this.transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ] + '.dlmenu',
// support for css animations and css transitions
this.supportAnimations = Modernizr.cssanimations,
this.supportTransitions = Modernizr.csstransitions;
this._initEvents();
},
_config : function() {
this.open = false;
this.$trigger = this.$el.children( '.dl-trigger' );
this.$menu = this.$el.children( 'ul.dl-menu' );
this.$menuitems = this.$menu.find( 'li:not(.dl-back)' );
this.$el.find( 'ul.dl-submenu' ).prepend( '<li class="dl-back">BACK</li>' );
this.$back = this.$menu.find( 'li.dl-back' );
},
_initEvents : function() {
var self = this;
this.$trigger.on( 'click.dlmenu', function() {
if( self.open ) {
self._closeMenu();
}
else {
self._openMenu();
}
return false;
} );
this.$menuitems.on( 'click.dlmenu', function( event ) {
event.stopPropagation();
var $item = $(this),
$submenu = $item.children( 'ul.dl-submenu' );
if( $submenu.length > 0 ) {
var $flyin = $submenu.clone().css( 'opacity', 0 ).insertAfter( self.$menu ),
onAnimationEndFn = function() {
self.$menu.off( self.animEndEventName ).removeClass( self.options.animationClasses.classout ).addClass( 'dl-subview' );
$item.addClass( 'dl-subviewopen' ).parents( '.dl-subviewopen:first' ).removeClass( 'dl-subviewopen' ).addClass( 'dl-subview' );
$flyin.remove();
};
setTimeout( function() {
$flyin.addClass( self.options.animationClasses.classin );
self.$menu.addClass( self.options.animationClasses.classout );
if( self.supportAnimations ) {
self.$menu.on( self.animEndEventName, onAnimationEndFn );
}
else {
onAnimationEndFn.call();
}
self.options.onLevelClick( $item, $item.children( 'a:first' ).text() );
} );
return false;
}
var link = $item.find('a').attr('href');
var hash = link.substring(link.indexOf('#')+1);
if( hash != "" ){
var elem = jQuery('div[data-anchor="'+hash+'"]');
autoScroll(elem);
self._closeMenu();
}else{
self.options.onLinkClick( $item, event );
}
} );
this.$back.on( 'click.dlmenu', function( event ) {
var $this = $( this ),
$submenu = $this.parents( 'ul.dl-submenu:first' ),
$item = $submenu.parent(),
$flyin = $submenu.clone().insertAfter( self.$menu );
var onAnimationEndFn = function() {
self.$menu.off( self.animEndEventName ).removeClass( self.options.animationClasses.classin );
$flyin.remove();
};
setTimeout( function() {
$flyin.addClass( self.options.animationClasses.classout );
self.$menu.addClass( self.options.animationClasses.classin );
if( self.supportAnimations ) {
self.$menu.on( self.animEndEventName, onAnimationEndFn );
}
else {
onAnimationEndFn.call();
}
$item.removeClass( 'dl-subviewopen' );
var $subview = $this.parents( '.dl-subview:first' );
if( $subview.is( 'li' ) ) {
$subview.addClass( 'dl-subviewopen' );
}
$subview.removeClass( 'dl-subview' );
} );
return false;
} );
},
closeMenu : function() {
if( this.open ) {
this._closeMenu();
}
},
_closeMenu : function() {
var self = this,
onTransitionEndFn = function() {
self.$menu.off( self.transEndEventName );
self._resetMenu();
};
this.$menu.removeClass( 'dl-menuopen' );
this.$menu.addClass( 'dl-menu-toggle' );
this.$trigger.removeClass( 'dl-active' );
if( this.supportTransitions ) {
this.$menu.on( this.transEndEventName, onTransitionEndFn );
}
else {
onTransitionEndFn.call();
}
this.open = false;
},
openMenu : function() {
if( !this.open ) {
this._openMenu();
}
},
_openMenu : function() {
var self = this;
// clicking somewhere else makes the menu close
$body.off( 'click' ).on( 'click.dlmenu', function() {
self._closeMenu() ;
} );
this.$menu.addClass( 'dl-menuopen dl-menu-toggle' ).on( this.transEndEventName, function() {
$( this ).removeClass( 'dl-menu-toggle' );
} );
this.$trigger.addClass( 'dl-active' );
this.open = true;
},
// resets the menu to its original state (first level of options)
_resetMenu : function() {
this.$menu.removeClass( 'dl-subview' );
this.$menuitems.removeClass( 'dl-subview dl-subviewopen' );
}
};
var logError = function( message ) {
if ( window.console ) {
window.console.error( message );
}
};
$.fn.dlmenu = function( options ) {
if ( typeof options === 'string' ) {
var args = Array.prototype.slice.call( arguments, 1 );
this.each(function() {
var instance = $.data( this, 'dlmenu' );
if ( !instance ) {
logError( "cannot call methods on dlmenu prior to initialization; " +
"attempted to call method '" + options + "'" );
return;
}
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
logError( "no such method '" + options + "' for dlmenu instance" );
return;
}
instance[ options ].apply( instance, args );
});
}
else {
this.each(function() {
var instance = $.data( this, 'dlmenu' );
if ( instance ) {
instance._init();
}
else {
instance = $.data( this, 'dlmenu', new $.DLMenu( options, this ) );
}
});
}
return this;
};
} )( jQuery, window );
After looking at their demo, it seems you can call method plugins. Replace dl-menu with the id of your menu.
$('#dl-menu a').click(function(e) {
$('#dl-menu').dlmenu('closeMenu');
});
I am working on JavaScript accordion and somehow i struck on targeting accordion ID instead of its <li>
Basically, I want below to generate two accordion if there are two markups like this
<div id="st-accordion" class="st-accordion">
<ul>
<li>
{tag_name_nolink}
<div class="st-content">
{tag_Partners Module tag}
</div>
</li>
</ul>
</div>
If there are two <div id="st-accordion" class="st-accordion">.......</div> then it should display two working accordions but it is making two working accordions when there are two <li> in the div.
Here is the JS and Fiddles
WORKING | NOT WORKING
(function( window, $, undefined ) {
var $event = $.event, resizeTimeout;
$event.special.smartresize = {
setup: function() {
$(this).bind( "resize", $event.special.smartresize.handler );
},
teardown: function() {
$(this).unbind( "resize", $event.special.smartresize.handler );
},
handler: function( event, execAsap ) {
// Save the context
var context = this,
args = arguments;
// set correct event type
event.type = "smartresize";
if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }
resizeTimeout = setTimeout(function() {
jQuery.event.handle.apply( context, args );
}, execAsap === "execAsap"? 0 : 100 );
}
};
$.fn.smartresize = function( fn ) {
return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
};
$.Accordion = function( options, element ) {
this.$el = $( element );
// list items
this.$items = this.$el.children('ul').children('li');
// total number of items
this.itemsCount = this.$items.length;
// initialize accordion
this._init( options );
};
$.Accordion.defaults = {
// index of opened item. -1 means all are closed by default.
open : -1,
// if set to true, only one item can be opened. Once one item is opened, any other that is opened will be closed first
oneOpenedItem : false,
// speed of the open / close item animation
speed : 600,
// easing of the open / close item animation
easing : 'easeInOutExpo',
// speed of the scroll to action animation
scrollSpeed : 900,
// easing of the scroll to action animation
scrollEasing : 'easeInOutExpo'
};
$.Accordion.prototype = {
_init : function( options ) {
this.options = $.extend( true, {}, $.Accordion.defaults, options );
// validate options
this._validate();
// current is the index of the opened item
this.current = this.options.open;
// hide the contents so we can fade it in afterwards
this.$items.find('div.st-content').hide();
// save original height and top of each item
this._saveDimValues();
// if we want a default opened item...
if( this.current != -1 )
this._toggleItem( this.$items.eq( this.current ) );
// initialize the events
this._initEvents();
},
_saveDimValues : function() {
this.$items.each( function() {
var $item = $(this);
$item.data({
originalHeight : $item.find('a:first').height(),
offsetTop : $item.offset().top
});
});
},
// validate options
_validate : function() {
// open must be between -1 and total number of items, otherwise we set it to -1
if( this.options.open < -1 || this.options.open > this.itemsCount - 1 )
this.options.open = -1;
},
_initEvents : function() {
var instance = this;
// open / close item
this.$items.find('a:first').bind('click.accordion', function( event ) {
var $item = $(this).parent();
// close any opened item if oneOpenedItem is true
if( instance.options.oneOpenedItem && instance._isOpened() && instance.current!== $item.index() ) {
instance._toggleItem( instance.$items.eq( instance.current ) );
}
// open / close item
instance._toggleItem( $item );
return false;
});
$(window).bind('smartresize.accordion', function( event ) {
// reset orinal item values
instance._saveDimValues();
// reset the content's height of any item that is currently opened
instance.$el.find('li.st-open').each( function() {
var $this = $(this);
$this.css( 'height', $this.data( 'originalHeight' ) + $this.find('div.st-content').outerHeight( true ) );
});
// scroll to current
if( instance._isOpened() )
instance._scroll();
});
},
// checks if there is any opened item
_isOpened : function() {
return ( this.$el.find('li.st-open').length > 0 );
},
// open / close item
_toggleItem : function( $item ) {
var $content = $item.find('div.st-content');
( $item.hasClass( 'st-open' ) )
? ( this.current = -1, $content.stop(true, true).fadeOut( this.options.speed ), $item.removeClass( 'st-open' ).stop().animate({
height : $item.data( 'originalHeight' )
}, this.options.speed, this.options.easing ) )
: ( this.current = $item.index(), $content.stop(true, true).fadeIn( this.options.speed ), $item.addClass( 'st-open' ).stop().animate({
height : $item.data( 'originalHeight' ) + $content.outerHeight( true )
}, this.options.speed, this.options.easing ), this._scroll( this ) )
},
// scrolls to current item or last opened item if current is -1
_scroll : function( instance ) {
var instance = instance || this, current;
( instance.current !== -1 ) ? current = instance.current : current = instance.$el.find('li.st-open:last').index();
$('html, body').stop().animate({
scrollTop : ( instance.options.oneOpenedItem ) ? instance.$items.eq( current ).data( 'offsetTop' ) : instance.$items.eq( current ).offset().top
}, instance.options.scrollSpeed, instance.options.scrollEasing );
}
};
var logError = function( message ) {
if ( this.console ) {
console.error( message );
}
};
$.fn.accordion = function( options ) {
if ( typeof options === 'string' ) {
var args = Array.prototype.slice.call( arguments, 1 );
this.each(function() {
var instance = $.data( this, 'accordion' );
if ( !instance ) {
logError( "cannot call methods on accordion prior to initialization; " +
"attempted to call method '" + options + "'" );
return;
}
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
logError( "no such method '" + options + "' for accordion instance" );
return;
}
instance[ options ].apply( instance, args );
});
}
else {
this.each(function() {
var instance = $.data( this, 'accordion' );
if ( !instance ) {
$.data( this, 'accordion', new $.Accordion( options, this ) );
}
});
}
return this;
};
})( window, jQuery );
somebody help please?
You have 2 elements with the same ID, and ID must be unique, so all you have to do is using class instead of ID :
$(function() {
$('.st-accordion').accordion({
oneOpenedItem : true
});
});
here is the jsFiddle