jQuery custom plugin callback - new question - javascript

I know a lot of people ask questions about plug-ins and callbacks (and I've read lots of them - that's how i got this far) so please, bear with me. I tried my hand at a very simple hide/show accordion type plugin for FAQs and was successful at getting it to do what I wanted. However, as I am still learning, I am not really sure how some things work.
I was able to add a callback to the plugin after reading this question and a few others.
My question is: Is this code correct and is there a better way to implement this callback?
Here's a working sample and the code below.
Thank you for your time.
( function($) {
$.fn.simpleFAQ = function( options, callback ) {
// define default options
var defaults = {
textExpand : "Expand all",
textCollapse : "Collapse all",
displayAll : false,
toggleSpeed : 250
};
var options = $.extend( defaults, options );
// callback
if( typeof callback != "function" ) { callback = function(){} }
this.each( function () {
obj = $(this);
// insert FAQ expand all/collapes all text before FAQ
var txt = '<span class="simple_jfaqText">' + options.textExpand + ' / ' + options.textCollapse + '</span>';
$( txt ).insertBefore( obj );
// add class to desired FAQ element
obj.addClass( 'simple_jfaq' );
// show/hide faq answers according to displayAll option
( options.displayAll == false ) ? ddDisplay = 'none' : ddDisplay = 'block';
obj.children( 'dd' ).css( 'display', ddDisplay );
// add classes according to <dd> state (hidden/visible)
obj.children( 'dd:visible' ).prev( 'dt' ).addClass( 'expanded' );
obj.children( 'dd:hidden' ).prev( 'dt' ).addClass( 'collapsed' );
obj.children( 'dt' )
.click( function() {
// show/hide all answers (dd elements) on click
$(this).nextUntil( 'dt' ).slideToggle( options.toggleSpeed, callback );
// dt class change on click
$(this).toggleClass( 'collapsed' ).toggleClass( 'expanded' ); })
.hover( function() { $(this).toggleClass( 'hover' ); }, function(){ $(this).toggleClass( 'hover' ); });
});
// Expand All
obj.prev( 'span' ).children( 'a[rel=jfaq_expand]' ).click( function() {
// show all answers
$(this).parent( 'span' ).next( '.simple_jfaq' ).children( 'dd:hidden' ).slideToggle( options.toggleSpeed );
setTimeout( callback, options.toggleSpeed )
// change classes
$(this).parent( 'span' ).next( '.simple_jfaq' ).children( 'dt' ).removeClass( 'collapsed' ).addClass( 'expanded' );
});
// Collapse all
obj.prev( 'span' ).children( 'a[rel=jfaq_collapse]' ).click( function() {
// hide all answers
$(this).parent( 'span' ).next( '.simple_jfaq' ).children( 'dd:visible' ).slideToggle( options.toggleSpeed );
setTimeout( callback, options.toggleSpeed );
// change classes
$(this).parent( 'span' ).next( '.simple_jfaq' ).children( 'dt' ).removeClass( 'expanded' ).addClass( 'collapsed' );
});
};
})( jQuery );

I would recommend using jQuery's built in event system for this. You can trigger an event on any node, then the user of the FAQ code could bind to that event on that node. You can even pass data to the binding function.
Of course, my real recommendation is to use my plug-in of the same name. :)

It might be nice if the callback would execute in the context of some relevant element.
var clicked = this;
setTimeout(function() { callback.call(clicked); }, options.toggleSpeed );

Related

Conflict between jQuery UI and Bootstrap also for "buttonset"

I have the same problem as in this question here, which is the conflict between jQuery UI and Bootstrap. The given answer from Darkseal
$.widget.bridge('uibutton', $.ui.button);
completely solved my problem for the "button"-widget. But it seems to me, that also the "buttonset"-widget reveals a conflict between the two libraries but
$.widget.bridge('uibuttonset', $.ui.buttonset);
does not do the trick for me. Am I doing something wrong?
This is an older question but I thought I'd share my fix for this.
You don't need the..
$.widget.bridge('uibuttonset', $.ui.buttonset);
There isn't any conflict there with Bootstrap (v3.3.6). The issue is $.ui.buttonset makes calls to .button() which isn't the the new name you declared so the conflict lives on. To fix this, I updated the calls to .button() inside .buttonset() to match the new name, "uibutton" or whatever you declare it as.
Below is my code for version jQuery UI 1.11.4. Perhaps there is an easier fix, but I've not come across it.
$.widget( "ui.buttonset", {
version: "1.11.4",
options: {
items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
},
_create: function() {
this.element.addClass( "ui-buttonset" );
},
_init: function() {
this.refresh();
},
_setOption: function( key, value ) {
if ( key === "disabled" ) {
this.buttons.uibutton( "option", key, value );
}
this._super( key, value );
},
refresh: function() {
var rtl = this.element.css( "direction" ) === "rtl",
allButtons = this.element.find( this.options.items ),
existingButtons = allButtons.filter( ":ui-button" );
// Initialize new buttons
allButtons.not( ":ui-button" ).uibutton();
// Refresh existing buttons
existingButtons.uibutton( "refresh" );
this.buttons = allButtons
.map(function() {
return $( this ).uibutton( "widget" )[ 0 ];
})
.removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
.filter( ":first" )
.addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
.end()
.filter( ":last" )
.addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
.end()
.end();
},
_destroy: function() {
this.element.removeClass( "ui-buttonset" );
this.buttons
.map(function() {
return $( this ).uibutton( "widget" )[ 0 ];
})
.removeClass( "ui-corner-left ui-corner-right" )
.end()
.uibutton( "destroy" );
}
});
I hope that helps you and everyone else.

WooCommerce event not triggering when using Select2 instead of actual select

I've built a custom website using Wordpress and WooCommerce and have installed Select2 to generate custom selects which is working fine. The issue I am having is with some of the selects on the WooCommerce pages, specifically those that trigger an event on change.
The custom selects successfully change the option selected, but the issue arises with selects that are meant to trigger an event. For example, the colour variation dropdown on the product page or the 'Sort By' select on the store page.
I've looked through the WooCommerce JS files and discovered some WooCommerce specific events that are triggered when a selection is made using the actual select box but I'm not sure how to implement this when using Select2 instead.
Here is a copy of the WooCommerce JS in relation to the event I'm talking about (in this case the change to the select for product variations):
.on( 'change', '.variations select', function() {
$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).change();
$form.find( '.wc-no-matching-variations' ).remove();
if ( $use_ajax ) {
if ( $xhr ) {
$xhr.abort();
}
var all_attributes_chosen = true;
var some_attributes_chosen = false;
var data = {};
$form.find( '.variations select' ).each( function() {
var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
if ( $( this ).val().length === 0 ) {
all_attributes_chosen = false;
} else {
some_attributes_chosen = true;
}
data[ attribute_name ] = $( this ).val();
});
if ( all_attributes_chosen ) {
// Get a matchihng variation via ajax
data.product_id = $product_id;
$xhr = $.ajax( {
url: wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ),
type: 'POST',
data: data,
success: function( variation ) {
if ( variation ) {
$form.find( 'input[name="variation_id"], input.variation_id' )
.val( variation.variation_id )
.change();
$form.trigger( 'found_variation', [ variation ] );
} else {
$form.trigger( 'reset_data' );
$form.find( '.single_variation_wrap' ).after( '<p class="wc-no-matching-variations woocommerce-info">' + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + '</p>' );
$form.find( '.wc-no-matching-variations' ).slideDown( 200 );
}
}
} );
} else {
$form.trigger( 'reset_data' );
}
if ( some_attributes_chosen ) {
if ( $reset_variations.css( 'visibility' ) === 'hidden' ) {
$reset_variations.css( 'visibility', 'visible' ).hide().fadeIn();
}
} else {
$reset_variations.css( 'visibility', 'hidden' );
}
} else {
$form.trigger( 'woocommerce_variation_select_change' );
$form.trigger( 'check_variations', [ '', false ] );
$( this ).blur();
}
// Custom event for when variation selection has been changed
$form.trigger( 'woocommerce_variation_has_changed' );
} )
And then my own attempt to utilise this event:
$('#pa_colour').select2();
$('#pa_colour').on('change', function(){
var $form = $(this).parents('form');
$form.trigger( 'woocommerce_variation_select_change' );
$form.trigger( 'woocommerce_variation_has_changed' );
});
Unfortunately the site isn't live yet so I can't provide a link but hopefully you get the idea.
If someone can help me here I'd be so appreciative, I'm not exactly sure how Wordpress hooks (if this is what this is) work and I may be just missing something obvious.
Thanks,
Kathryn
This isn't a solution exactly, but I ended up replacing the Select2 plugin with the Selectric plugin and that works perfectly. Oh well! Thanks guys. http://lcdsantos.github.io/jQuery-Selectric/
I came across the same issue and found a solution in the last comment in this thread Select2 not showing selected value
The comment by Matt inspired by Kevin suggested wrapping the select2 call in $(window).bind("load", function() {...}); which worked for me.
Kudos to those guys.

Woocommerce "Add to cart" ajax

On the product category page, when someone clicks "Add to cart", woocommerce adds "View cart" below this button through Ajax. I found that the script which handle this is /assets/js/frontend/add-to-cart.js
Now, I want to add also "Procceed to checkout", so someone can go to checkout immediately.
This is the output of the script:
jQuery( function( $ ) {
// wc_add_to_cart_params is required to continue, ensure the object exists
if ( typeof wc_add_to_cart_params === 'undefined' )
return false;
// Ajax add to cart
$( document ).on( 'click', '.add_to_cart_button', function(e) {
// AJAX add to cart request
var $thisbutton = $( this );
if ( $thisbutton.is( '.product_type_simple' ) ) {
if ( ! $thisbutton.attr( 'data-product_id' ) )
return true;
$thisbutton.removeClass( 'added' );
$thisbutton.addClass( 'loading' );
var data = {
action: 'woocommerce_add_to_cart',
};
$.each( $thisbutton.data(), function( key, value ) {
data[key] = value;
});
// Trigger event
$( 'body' ).trigger( 'adding_to_cart', [ $thisbutton, data ] );
// Ajax action
$.post( wc_add_to_cart_params.ajax_url, data, function( response ) {
if ( ! response )
return;
var this_page = window.location.toString();
this_page = this_page.replace( 'add-to-cart', 'added-to-cart' );
if ( response.error && response.product_url ) {
window.location = response.product_url;
return;
}
// Redirect to cart option
if ( wc_add_to_cart_params.cart_redirect_after_add === 'yes' ) {
window.location = wc_add_to_cart_params.cart_url;
return;
} else {
$thisbutton.removeClass( 'loading' );
fragments = response.fragments;
cart_hash = response.cart_hash;
// Block fragments class
if ( fragments ) {
$.each( fragments, function( key, value ) {
$( key ).addClass( 'updating' );
});
}
// Block widgets and fragments
$( '.shop_table.cart, .updating, .cart_totals' ).fadeTo( '400', '0.6' ).block({
message: null,
overlayCSS: {
opacity: 0.6
}
});
// Changes button classes
$thisbutton.addClass( 'added' );
// View cart text
if ( ! wc_add_to_cart_params.is_cart && $thisbutton.parent().find( '.added_to_cart' ).size() === 0 ) {
$thisbutton.after( ' <a href="' + wc_add_to_cart_params.cart_url + '" class="added_to_cart wc-forward" title="' +
wc_add_to_cart_params.i18n_view_cart + '">' + wc_add_to_cart_params.i18n_view_cart + '</a>' );
}
// Replace fragments
if ( fragments ) {
$.each( fragments, function( key, value ) {
$( key ).replaceWith( value );
});
}
// Unblock
$( '.widget_shopping_cart, .updating' ).stop( true ).css( 'opacity', '1' ).unblock();
// Cart page elements
$( '.shop_table.cart' ).load( this_page + ' .shop_table.cart:eq(0) > *', function() {
$( '.shop_table.cart' ).stop( true ).css( 'opacity', '1' ).unblock();
$( 'body' ).trigger( 'cart_page_refreshed' );
});
$( '.cart_totals' ).load( this_page + ' .cart_totals:eq(0) > *', function() {
$( '.cart_totals' ).stop( true ).css( 'opacity', '1' ).unblock();
});
// Trigger event so themes can refresh other areas
$( 'body' ).trigger( 'added_to_cart', [ fragments, cart_hash, $thisbutton ] );
}
});
return false;
}
return true;
});
Is there anybody who has done something similar?
If you look here from the Woocommerce repo, you can see that add-to-cart.js is localized from that class.
Unfortunately, there isn't a filter to just add your own link. What you could try is copying add-to-cart.js to your theme and set the new src of the registered add-to-cart.js to your new local copy, by using this method.
From there you can alter the this conditional found in Woocommerce repo.
So, technically yes, you could could this, but there are caveats:
You would need to repeat this process for variation products
If translation is a concern, you need to address that as well
Any time the plugin updates, you now have to comb through these files for any differences that could break functionality or cause a security issue.

Calling a jQuery function multiple times

So, I have this function in jQuery:
$(function(){
$( ".unfocused" ).click(function ClickHeader () {
$( this ).addClass( "focused" );
$( this ).removeClass( "unfocused" );
$(".header").not(this).addClass( "unfocused" );
$(".header").not(this).removeClass( "focused" );
});
});
It works perfectly when a header is clicked the first time, but when I try to click another unfocused header, the function doesn't work anymore. Is it because it runs on document .ready?
Thanks for your help!
Change it like this:
$( document ).on("click", ".unfocused", function() {
$( this ).addClass( "focused" );
$( this ).removeClass( "unfocused" );
$(".header").not(this).addClass( "unfocused" );
$(".header").not(this).removeClass( "focused" );
});
This basically registers the event on the document. When you click a header, the event bubbles up to the document. There, the given selector is validated and the function is executed if needed.
Here is a jsfiddle using the delegate operation for handling the event like you need.
http://jsfiddle.net/MN9Zt/2/
$("body").delegate(".unfocused", "click", function() {
$(this).addClass("focused");
$(this).removeClass("unfocused");
$(".header").not(this).addClass("unfocused");
$(".header").not(this).removeClass("focused");
});

How to select the multiple rows in jQuery datatable using the mouse drag option

I am using the jQuery DataTables plug-in in my application. I need to select the multiple rows in a jQuery datatable using the mouse drag option. How is it possible?
Use jQuery-UI selectable and code similar to the following:
$( "#yourTable" ).selectable(
{
distance: 10,
stop: function()
{
$( this ).find( "tr" ).each(
function () {
if ( $( this ).hasClass( 'ui-selected' ) )
$( this ).addClass( 'row-selected' );
else
$( this ).removeClass( 'row-selected' );
});
}
});
I use 'distance: 10' because I found that otherwise my mousedown handler for the table wouldn't get events - this may not be important for you.

Categories

Resources