Extend jQuery with modified .click function - javascript

I am building a site for both mobile/touch devices as well as desktop non-touch devices.
To help with eliminating the 300ms delay on jQuery .click events for touch devices I am using the tappy library, which works beautifully.
https://github.com/filamentgroup/tappy
That being said, I only want to bind this 'tap' event where necessary and want to bind a regular .click event if the device is non-touch. I know tappy's 'tap' event will fire for non-touch devices, but I'd rather only use it where necessary since there are a few quirks documented.
Up to now I have been writing switch functions each time I want to do this, which looks like this:
if ($('html').hasClass('no-touch')) {
$('.sidebar').find('.strip').find('.explore-wrapper').click(function() {
$(this).closest('.sidebar').addClass('active');
});
} else {
$('.sidebar').find('.strip').find('.explore-wrapper').bind('tap', function() {
$(this).closest('.sidebar').addClass('active');
});
}
I'd like to extend jQuery and write my own version of .click, maybe called 'tapClick' so that my code could function like:
$('.sidebar').find('.strip').find('.explore-wrapper').tapClick(function() {
$(this).closest('.sidebar').addClass('active');
});
I started to try and write my own extension, but couldn't get it...any help would be appreciated:
// Custom tap/click bind
$.extend({
tapClick : function(callbackFnk) {
if ($('html').hasClass('touch')) {
$(this).bind('tap', function() {
if(typeof callbackFnk == 'function') {
callbackFnk.call(this);
}
});
} else {
$(this).click(function() {
if(typeof callbackFnk == 'function') {
callbackFnk.call(this);
}
});
}
}
});

Try substituting jQuery.fn.extend() for jQuery.extend()
e.g.,
$.extend({tapClick:function() {console.log(this)}});
$.fn.extend({tapClick:function() {console.log(this)}});
$.tapClick(); // function (a,b){return new e.fn.init(a,b,h)}
$("html").tapClick(); // [html, ...]
$.extend({tapClick:function() {console.log(this)}});
$.fn.extend({tapClick:function() {console.log(this)}});
$.tapClick();
$("html").tapClick();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

This is the code that I ended up using that solves the problem.
$.fn.extend({
tapClick:function(callback) {
if ($('html').hasClass('touch')) {
return $(this).click(callback);
} else {
return $(this).bind('tap', callback);
}
}
});

Related

Need help Converting a jQuery event to a Drupal Behavior

I have a Drupal 7 web site that is using jQuery animations to fadeIn div tags. I need an event to capture a fadeIn when it is completed. I have found a sample jQuery example that does what I need it to, but I have not been able to successfully convert it to a Drupal 7 behavior and I'm not quite sure what I might be missing.
Fiddle Example
Below is my Drupal JS file, fadeInEvent.js.
Drupal.behaviors.fadeInEvent= {
attach: function (context, settings) {
var _old = jQuery.fn.fadeIn;
jQuery.fn.fadeIn = function() {
var self = this;
_old.apply(this.arguments).promise().done(function(){
self.trigger('fadeIn');
});
};
jQuery('.tab-pane').bind('fadeIn', function() {
alert('fadeIn Done.');
});
}
};
In the above JS code, I never get my alert that the fadeIn has finished on the item I have selected.
Firs of all, while using jQuery in noconflict mode, you may use a closure to access it by $
(function($) {
// jQuery code using $ object.
}(jQuery));
Regarding the .fadeIn(), consider my snippet:
/**
* $.fn.fadeInNew plugin for triggering 'fadeInDone', when .fadeIn is done.
*
* #param speed
* #param easing
* #param callback
* #returns {$.fn}
*/
$.fn.fadeInNew = function(speed, easing, callback) {
var self = this;
self.animate({
opacity: "show"
}, speed, easing, function() {
self.trigger('fadeInDone');
if ($.isFunction(callback)) {
callback.apply(self);
}
});
return self;
}
$('.tab-pane').on('fadeInDone', function() {
alert('Alarm!');
});
$('.button').on('click', function(e) {
$('.tab-pane').fadeInNew();
});
.tab-pane {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tab-pane">Have a good day!</div>
<button class="button">Show something!</button>
Since I don't really like the idea of overriding the native methods of any libraries, I have made a plugin .fadeInNew(), which will trigger custom fadeInDone event on the element. The code behind animation is almoast the same as in native implementation see the source here.
Also, you don't need to use Drupal.behaviors to define something like that. You should use attach only for things that are being loaded with Drupal's ajax framework, see includes/ajax.php

JQuery Plugin - triggering internal functions by callback

Skip to bottom for question
JQuery plugin:
$.fn.myPlugin = function( options ) {
var options = $.extend({
myOption: true,
edit: function() {},
done: function() {}
}, options);
options.edit.call(this);
options.done.call(this);
//plugin guts removed to prevent over complication
return {
edit: function(obj) {
$(obj).closest('#myParent').find('#myInput').autosizeInput(); //plugin to autosize an input
},
done: function(obj) {
$(this).closest('tr').find('td').not('.not').each(function(i) {
//do some things
});
}
}
});
Bear in mind this is a cut down version of my plugin.
Called from page:
$(document).ready(function() {
var myPlugin = $('.editable').myPlugin({
edit: $(this).on('click', '.edit-td', function(e) {
e.preventDefault();
//do some page specific stuff
myPlugin.edit( $(this) ); //call the edit returned function
}),
done: $(this).on('click', '.done-td', function(e) {
e.preventDefault();
//do some page specific stuff
myPlugin.done( $(this) ); //call the done returned function
});
});
});
This works great for the most part, however, what i really want is have functions called from inside my plugin every time a specific callback is triggered - without the need to call from outside the plugin.
I have tried including delegated events in my plugin:
$(this).on('click', '.edit-td', function(e) {
e.preventDefault();
$(this).closest('#myParent').find('#myInput').autosizeInput();
});
$(this).on('click', '.done-td', function(e) {
e.preventDefault();
$(this).closest('tr').find('td').not('.not').each(function(i) {
//do some things
});
});
But when the .edit-td is triggered it propagates and triggers the .done-td event, if i put e.stopPropagation() in the edit-td function (because it has been delegated) edit-td stops firing completely.
And non-delegated method:
$(this).find('.done-td').click(function(e, this) {});
But I can't parse the returned object (this) to the internal function before the internal function has completed. (just comes up undefined or missing formal parameter).
*Skip to here
To avoid the question becoming to localised -
I need to have functions called from inside my
plugin every time a specific callback is triggered.
Without calling it using closures
Something like:
if( $.fn.myPlugin.callback().is('edit') ) {
//fire function
}
I needed to return a function(s) like so:
return {
enable: function(arg) {
//do something
},
disable: function(arg) {
//do something
}
}
That way I can call it from inside my plugin by referencing itself like this:
this.myPlugin().disable();

jQuery - how to delegate a toggle

How can I provide a toggle for a dynamically created element?
My code does not work:
JS
$("body").on('toggle', ".buttonA", function(){
function() {
..do stuff
},
function() {
.. revert stuff
}
});
Try this:
$('body').on('click','.buttonA', function () {
var toggled = $(this).data('toggled');
$(this).data('toggled', !toggled);
if (!toggled) {
//..do stuff
}
else {
//.. revert stuff
}});
If you are using jQuery,you can use .live() methods for binding a dynamically created element.
$('#hello').live("click", function() {
alert( "Goodbye!" ); // jQuery 1.3+
});
I didn't use jQuery for a long time.So I don't know the method is valid or not.But it is very easy to write a new method to resolve this requirement.The more important thing is you bind the element whether or not.

jQuery: Call a function twice

I'm trying to run a function twice. Once when the page loads, and then again on click. Not sure what I'm doing wrong. Here is my code:
$('div').each(function truncate() {
$(this).addClass('closed').children().slice(0,2).show().find('.truncate').show();
});
$('.truncate').click(function() {
if ($(this).parent().hasClass('closed')) {
$(this).parent().removeClass('closed').addClass('open').children().show();
}
else if ($(this).parent().hasClass('open')) {
$(this).parent().removeClass('open').addClass('closed');
$('div').truncate();
$(this).show();
}
});
The problem is on line 13 where I call the truncate(); function a second time. Any idea why it's not working?
Edit jsFiddle here: http://jsfiddle.net/g6PLu/
That's a named function literal.
The name is only visible within the scope of the function.
Therefore, truncate doesn't exist outside of the handler.
Instead, create a normal function and pass it to each():
function truncate() { ...}
$('div').each(truncate);
What's the error message do you get?
You should create function and then call it as per requirement
Define the function
function truncate(){
$('div').each(function(){
});
}
Then call the function
truncate();
Another approach is to establish, then trigger, a custom event :
$('div').on('truncate', function() {
$(this).......;
}).trigger('truncate');
Then, wherever else you need the same action, trigger the event again.
To truncate all divs :
$('div').trigger('truncate');
Similarly you can truncate just one particular div :
$('div#myDiv').trigger('truncate');
The only prerequisite is that the custom event handler has been attached, so ...
$('p').trigger('truncate');
would do nothing because a truncate handler has not been established for p elements.
I know there's already an accepted answer, but I think the best solution would be a plugin http://jsfiddle.net/g6PLu/13/ It seems to be in the spirit of what the OP wants (to be able to call $('div').truncate). And makes for much cleaner code
(function($) {
$.fn.truncate = function() {
this.addClass('closed').children(":not('.truncate')").hide().slice(0,2).show();
};
$.fn.untruncate = function() {
this.removeClass('closed').children().show();
};
})(jQuery);
$('div').truncate();
$('.truncate').click(function() {
var $parent = $(this).parent();
if ($parent.hasClass('closed')) {
$parent.untruncate();
} else {
$parent.truncate();
}
});

Overwriting a Backbone Models Change Event

I think what I want to do is pretty simple I just don't know how to do it. I would like to fire my own event when one of my models attributes changes for the purpose of passing some data to the event handler (whether the change was an increase or decrease in value).
Basically I want my handler to do this in the view
handler: function(increased) {
if(increased) {
alert("the value increased")
}
else {
alert("the value decreased")
}
}
// ...
this.model.on("change:attr", this.handler, this);
Here you go: You basically listen for change:myvar. When a change occurs you use your model's previous() to get the old value. Depending on whether it increased or decreased you fire the appropriate event. You can listen to these events as shown in the initialize().
(function($){
window.MyModel = Backbone.Model.extend({
initialize: function () {
this.on('change:myvar', this.onMyVarChange);
this.on('increased:myvar', function () {
console.log('Increased');
});
this.on('decreased:myvar', function () {
console.log('Decreased');
});
},
onMyVarChange: function () {
if (this.get('myvar') > this.previous('myvar')) {
this.trigger('increased:myvar');
} else {
this.trigger('decreased:myvar');
}
}
});
window.mymodel = new MyModel({myvar: 1});
mymodel.set({myvar: 2});
mymodel.set({myvar: 3});
mymodel.set({myvar: 1});
})(jQuery);​
Running the above will print "Increased", "Increased", "Decreased" to your console.
Just look at previousAttributes()
You can then compare:
If(this.get(attr) > this.previousAttributes()[attr]){
console.log('bigger');
} else {
console.log('smaller');
}
If you use that in your change event handler you're all set. No need for a custom trigger or a ton of code.
EDIT
This is from my Backbone.Validators project and how I obtain the list of all attributes which have changed during the validation step:
var get_changed_attributes = function(previous, current){
var changedAttributes = [];
_(current).each(function(val, key){
if(!_(previous).has(key)){
changedAttributes.push(key);
} else if (!_.isEqual(val, previous[key])){
changedAttributes.push(key);
}
});
return changedAttributes;
};
This requires Underscore 1.3.1 because it's using _.has. If you can't upgrade that's an easy thing to replace though. In your case you'd passing this.previousAttributes() and this.attributes
What if you fire your own custom event after listening to the change event?
handler: function(increased) {
this.model.trigger('my-custom-event', stuff, you, want);
},
myHandler: function(stuff, you, want){
// Do it...
}
// ...
this.model.on("change:attr", this.handler, this);
this.model.on('my-custom-event, this.myHandler, this);

Categories