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();
Related
I'm calling via ajax additional content where I add a jquery on() function for a click event. Each time I renew the content the event is also set again so at the end it get executed several times. How can I avoid this behavior?
How do I test if the click event is already set on the document?
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Click
<script>
// first ajax load
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
// second ajax load
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
</script>
I already try to just the jQuery.isFunction(), but I don't anderstand how to apply it in this case.
You can Unbind the click event , if you getting more than one time exectuated.
$(document).unbind('click').on("click", ".open-alert", function () {
//do stuff here
});
Or you can also use it
$(document).off("click", ".open-alert").on("click", ".open-alert", function () {
});
Using
$(document).on('click', '#element_id', function() {
//your code
});
Will check the DOM for matching elements every time you click (usually used for dynamically created elements with ajax)
But using
$('#element_id').on('click', function() {
//your code
});
Will only bind to existing elements.
If you use the 1st example, you only need to call it once, you can even call it before your ajax call since it will recheck for matching elements on each click.
<script>
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
// first ajax load
// second ajax load
...
</script>
In case you cannot bind the event to the specific DOM element (which might happen if you use Turbolinks for example) you can use a variable to check whether you set the event or not.
Local scope
var clickIsSet = false;
// any ajax load
$(document).on('click', '.open-alert', function () {
if ( clickIsSet ) {
alert('hello world!');
clickIsSet = true;
}
});
Global scope
I don't recommend to make clickIsSet global, but in case you are importing/exporting modules you can do that:
// main.js
window.clickIsSet = false;
// any-other-module.js
$(document).on('click', '.open-alert', function () {
if ( window.clickIsSet ) {
alert('hello world!');
window.clickIsSet = true;
}
});
jQuery check if event exists on element : $._data( $(yourSelector)[0], 'events' )
this return all of element events such : click , blur ,
focus,....
Important Note: $._data when worked that at least an event bind to element.
so now:
1.in your main script or first ajax script bind click event on element
<script>
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
</script>
2. in secound ajax:
var _data = $._data( $('.open-alert')[0], 'events' );
if(typeof _data != "undefined"){
var eventClick = $._data( $('.open-alert')[0], 'events' ).click
var hasEventClick = eventClick != null && typeof eventClick != "undefined";
if(!hasEventClick){
$(document).on('click', '.open-alert', function () {
alert('hello world!');
});
}
}
I get Confuse about your question but as far as understand your question I have three suggestions:
Use Id element (as #Mokun write the answer)
Use Common Function for call functionality instead use through the click event.(Make Sure of function does not overwrite your content by calling).
Use of flag variable (or global variable for your tracking event) in jquery and identify your function call for particular execution.
I'm using introjs to build a tour of my application. I've searched in quite a few places online and through the documentation but can't seem to find anywhere a method of how to run a function upon skipping or clicking done on the tour. I'm trying to make it so a cookie is stored and the tour isn't run again until a user requests it or a new user comes to the site. Any help would be great, thanks!
$(function(){
var introguide = introJs();
introguide.setOptions({
showProgress: true,
steps: [
{ hidden }
]
});
introguide.start();
});
This code allows to store the tour info
var introguide = introJs();
window.addEventListener('load', function () {
var doneTour = localStorage.getItem('MyTour') === 'Completed';
if (doneTour) {
return;
}
else {
introguide.start()
introguide.oncomplete(function () {
localStorage.setItem('MyTour', 'Completed');
});
introguide.onexit(function () {
localStorage.setItem('MyTour', 'Completed');
});
}
});
Yes, there is a way but with some caveats.
First, after intro.js is loaded you will have a global called introJs with a property fn (standard jquery plug-in approach).
By setting a function using the oncomplete() function under introJS.fn, you can perform some actions when the user hits the 'Done' button.
Here's an example that just displays a console message:
introJs.fn.oncomplete(function() { console.log("Finished"); });
This works as expected. You can put this in a script anytime after the intro.js library is included.
The 'skip' button functionality, however, will only call the 'oncomplete' handler if you are on the last step. The author of the code views that as not complete and so doesn't run that code as you can see by this extract from the code:
skipTooltipButton.onclick = function() {
if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
self._introCompleteCallback.call(self);
}
_exitIntro.call(self, self._targetElement);
};
This basically says it must be at the last step for this to consider calling the complete callback.
Of course, you could fork the code and remove the restriction. I would suggest if you are going to do that, create a _introSkipCallback in a fashion similar to _introlCompleteCallback and invoke that unless on last step where you might invoke both functions if present.
Hope this helps.
Use oncomplete for functions after 'Done' is clicked
Use onexit for functions after 'Skip' is clicked
Bonus function: onchange will log each step, this can be used to call functions on a particular step
document.getElementById('startButton').onclick = function() {
// log each step
introJs().onchange(function(targetElement) {
console.log(this._currentStep)
if (this._currentStep === 3){
stepThreeFunc()
}
}).start()
// clicking 'Done'
.oncomplete(function(){
someFunc()
})
// clicking 'Skip'
.onexit(function(){
someOtherFunc()
});
};
I've noticed that onexit will be called when you click the done button (which is skip until the last step). onexit does not appear to bind this to the introjs object, so I was able to solve the issue of having onexit called when the walkthrough was completed like this:
// during setup
introJs.oncomplete(handleOnComplete);
introJs.onexit(() => handleOnExit(introJs));
function handleOnComplete() {
console.log(this._currentStep); // this is bound to the introJs object
}
function handleOnExit(introJs) {
const currentStep = introJs._currentStep;
if (currentStep < introJs._options.steps.length) {
doSomethingOnSkip();
}
};
I was going to add a comment, but my rep is too low. I didn't want to answer because I haven't actually tested this, but in version 2.5.0 (maybe previous versions too), there is the onexit function, which I believe is supposed to handle interrupts as well as clicking done at the end. Did you try that?
if ($(".introjs-skipbutton").is(":visible")) {
$( document ).on('click', '.introjs-skipbutton', function(event) {
event.stopPropagation();
event.stopImmediatePropagation();
self.exitTourguide();
});
}
I am using introJS tool in my application to give tour guide information of my application.
I used some functions for handling it dynamically. Here stepsData sending in an array format.
var intro = introJs();
intro.setOptions( {
'nextLabel': 'Next >',
'prevLabel': '< Back',
'tooltipPosition': 'right',
steps: this.stepsData,
showBullets: false,
showButtons: true,
exitOnOverlayClick: false,
keyboardNavigation: true,
} );
hope it will help for handling skip button action.
var self = this; intro.start().onbeforechange( function() { /* skip action*/
if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) {
$( document ).on( 'click', '.introjs-skipbutton', function( event ) {
self.exitTourguide();
});
}
});
skip and done action handling.
/Done click action/
intro.oncomplete( function(){ if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) { $( document ).on( 'click', '.introjs-skipbutton', function( event ) { event.stopPropagation(); event.stopImmediatePropagation(); self.exitTourguide(); }); } });
/* clicking 'Skip' action */ intro.onexit(function(){ if ( $( ".introjs-skipbutton" ).is( ":visible" ) ) { $( document ).on( 'click', '.introjs-skipbutton', function( event ) { event.stopPropagation(); event.stopImmediatePropagation(); self.exitTourguide(); }); } });
I am using the bPopup jquery library.. the syntax to add to the onclose event is pretty straightforward:
$('element_to_pop_up').bPopup({
onOpen: function() { alert('onOpen fired'); },
onClose: function() { alert('onClose fired'); }
})
What I want to do is add something to the onClose event after the object is created.. is it possible?
In general you can do this by creating a function that you fiddle with later:
var myOnClose = function() { alert('onClosed fired'); }
function doOnClose() { myOnClose(); }
$('element_to_pop_up').bPopup({
onOpen: function() { alert('onOpen fired'); },
onClose: doOnClose
})
// later...
myOnClose = function() { console.log("Doing something different!"); }
You can access the bPopup object which will be present inside the data of the element.
$('element_to_pop_up').bPopup({
onOpen: function() { alert('onOpen fired'); },
onClose: function() { alert('onClose fired'); }
});
$('element_to_pop_up').data('bPopup');
NOTE: There is no guarantee that the created object will be always present in element's data. But this is widely used approach. It is better to rely on the callback provided.
var realOnclose = f1;
function myOnClose(){realOnclose();}
$('element_to_pop_up').bPopup({
onOpen: function() { alert('onOpen fired'); },
onClose: myOnClose
})
function f1() { alert('onClose fired'); }
function f2() { alert('hey I am another function'); }
//when you want to change the action when onclose...
realOnclose = f2;
If you want to add to, rather than replace, the code you originally supply to the onClose option, you could trigger a custom event:
$('element_to_pop_up').bPopup({
onClose: function() {
// Do the original stuff here.
this.trigger('popup:close');
}
});
Then, at any time later, you can register a handler for the custom event:
$('element_to_pop_up').on('popup:close', function() {
// Do the additional stuff here.
});
Note: Looking at the code for the bPopup library, it looks like the context for the onClose function is the original jQuery object, but if it isn't, replace with: $('element_to_pop_up').trigger('popup:close');.
I'm trying to bind a click event to the function below, however the entire function is currently being run when being binded in the document ready.
Is it possible to run it solely on the click event? Possibly it has something to do with the way my method is composed?
Thanks in advance
$(function() {
$("#expand-search").on("click", search.resize());
});
var search = {
element: $('#search_advanced'),
resize: function() {
search.element.slideToggle(400, 'swing', search.buttonState());
},
buttonState: function() {
if(search.element.is(':hidden')) {
console.log('hidden');
} else {
console.log('visible');
}
}
};
You are calling the function (handler) instead of passing the reference (name) of function (handler) to on().
Change
$("#expand-search").on("click", search.resize());
To
$("#expand-search").on("click", search.resize);
No parenthesis to event handlers! You want to pass the function-to-be-executed, not the result from executing it. Also, you will need to move your search object inside the ready handler since you use selectors for its initialisation.
$(function() {
var search = {
element: $('#search_advanced'),
resize: function() {
search.element.slideToggle(400, 'swing', search.buttonState);
},
buttonState: function() {
if(search.element.is(':hidden')) {
console.log('hidden');
} else {
console.log('visible');
}
}
};
$("#expand-search").on("click", search.resize);
});
Following on from my earlier question, I have decided to have a go at writing a series of jQuery plugins that mimic mobile events (tap, taphold, etc.).
I have the concept working, but am having problems executing the handler functions. Here's how I am defining the plugin methods:
(function($) {
var touch_capable = isTouchCapable();
var settings = {
swipe_h_threshold : 30,
swipe_v_threshold : 50,
taphold_threshold : 750,
startevent : (touch_capable) ? 'touchstart' : 'mousedown',
endevent : (touch_capable) ? 'touchend' : 'mouseup'
};
// tap Event:
$.fn.tap = function(handler) {
return this.each(function() {
var $this = $(this);
var started = false;
$this.bind(settings.startevent, function() {
started = true;
});
$this.bind(settings.endevent, function() {
if(started)
{
handler();
}
});
});
};
}) (jQuery);
And then I can bind these 'events' using $('#a_div').tap();. The problem that I have is this one:
If I pass in a function to the tap() method which works upon the element, there's an error. For example:
$('#my_div').tap(function() { alert($(this).get()) });
Actually alerts [object DOMWindow]. Can anyone point me in the direction of correctly executing the handler function? Could it be somehow related to event propagation?
You can use the Function.prototype.call and Function.prototype.apply methods to set the execution context of a function;
$this.bind(settings.endevent, function() {
if(started)
{
handler.call(this);
}
});
This allows you to minic the interface bind provides to the provided handler;
$this.bind(settings.endevent, function() {
if(started)
{
handler.apply(this, arguments);
}
});
Now the handler will receive the event object as it's first parameter, and this will be set the the element the event fired on.