How to manually execute a function inside a jQuery plugin? - javascript

I have a plugin that looks like this:
(function($) {
$.fn.plugin_name = function(options) {
var $this = $(this);
var defaults = {
// some defaults
};
options = $.extend({}, defaults, options);
var work = {
action_1: function() {
// do something
},
action_2: function(output) {
alert('hello world');
}
}
that.submit(function(e) {
e.preventDefault();
work.action_1();
});
return $this;
}
})(jQuery);
It's being used like any traditional jquery plugin, by being attached to a page element like so:
$('#search-form').plugin_name({
// overide options
});
My question is, how can I execute the work.action_2() function that's deeply nested inside the plugin? I would like to call it manually from the javascript console in firebug.

My question is, how can I execute the work.action_2() function that's deeply nested inside the plugin?
You can't, it's outside of your scope!
you can't reach private function variables, just like you can't reach my functions...

Create it in a separate utility function if its needed independently, better if its in your own global object.
So you can call it like so: $.myGlobalObj.action_2()

Related

create jquery extension. problems with scope

I create a simple jQuery extension(it's my first).
(function($){
var MyClass = function(opt){
//..
};
//one of the methods of my extension
$.fn.myExtension = function(opt){
this._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function(){
if(this._ext){
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function(){
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
when i invoke method $('#selector').myExtensionOtherMethod();, this does not contains this._ext variable. I know that this is other scope, but i know that there is some way to access that variable in both methods.how can i do it?
This isn't really a scope issue. This is because the jQuery prototype $.fn gives you a jquery object as this. Even though you are selecting the same element each time its a new jQuery object set as the context so that property is gone. You can put that property on the DOM element and achieve the outcome you want.
(function($) {
var MyClass = function(opt) {};
//one of the methods of my extension
$.fn.myExtension = function(opt) {
this[0]._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function() {
if (this[0]._ext) {
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function() {
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
This is just a quick example above. If your selector finds more than one element you should loop though them. But I only grabbed the first index since you were selecting by ID.
Fiddle: https://jsfiddle.net/AtheistP3ace/gd1ehk0d/
As mentioned above by #charlietfl, I agree with that comment. Happy to explain why what you did didn't work but there may be better ways to achieve what you are looking for.

JS Revealing Pattern event undefined issue

I am using the modular design pattern for JS and I keep running into issues when using arguments bound functions. I have a particular function that I would like to bind to different events to keep from having to write the function for each bound event. The only difference in the function, or the argument, is the table that will be updated. The problem is that when I build a function with the arguments I need and pass those arguments to bound events, I get an undefined error, in the console, on load. Keep in mind, I want to stick with this design pattern for the security it offers.
Here is my JS:
var Users = (function(){
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
$addRoleForm.submit(ajaxUpdate(event, $rolesTableBody));
function ajaxUpdate(event, tableName) {
event.preventDefault();
event.stopPropagation();
var url = this.action;
var data = $(this).serialize();
var $this = $(this);
$.ajax({
type: 'POST',
url: url,
dataType: 'json',
data: data,
success: function(data) {
if(data.st === 0){
$messageContainer.html('<p class="alert alert-danger">' + data.msg + '</p>');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
} else {
$messageContainer.html('<p class="alert alert-success">' + data.msg + '</p>');
tableName.fadeOut().html('').html(data.build).fadeIn();
$this.find('input').val('');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
}
},
error: function(xhr, status, error){
console.log(xhr.responseText);
}
});
}
})();
Here is the error I get in the console, on load:
Uncaught TypeError: Cannot read property 'preventDefault' of undefined
I have tried to bind the event like this: $addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody)); and receive the same results.
Any ideas how to fix this?
You're seeing that issue, because the way you have it written now, ajaxUpdateexecutes, returns undefined and THEN passes undefined to the event listener, so you're basically doing this: $addRoleForm.submit(undefined).
2 Choices here:
1) You can wrap it in an anonymous function:
$addRoleForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someValue);
});
$someOtherForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someOtherValue);
});
2) You can set the first argument in-advance using bind:
$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));
Using this way, you're binding the value of this to be $addRoleForm, setting the first argument to always be someValue, so it's the same as:
ajaxUpdate(someValue, event) {
//value of "this" will be $addRoleForm;
}
To pass the event, and the custom argument, you should be using an anonymous function call
$addRoleForm.submit(function(event) {
ajaxUpdate(event, $rolesTableBody));
});
This is by far the easiest and most readable way to do this.
What you're doing right now equates to this
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
var resultFromCallingFunction = ajaxUpdate(event, $rolesTableBody); // undefined
$addRoleForm.submit(resultFromCallingFunction);
Where you're calling the ajaxUpdate function, as that's what the parentheses do, and pass the returned result back to the submit callback, which in your case is undefined, the default value a function returns when nothing else is specified.
You could reference the function, like this
$addRoleForm.submit(ajaxUpdate);
but then you can't pass the second argument
The question refers to the Revealing Module pattern. Benefit of using this design is readability. Going with the anon function may work, but defeats the overall purpose of the module pattern itself.
A good way to structure your module to help maintain your scope is to setup helper functions first, then call a return at the end.
Example use case with events:
var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init
}
}().runMe();
Helps to "modularize" your workflow. You are also writing your revealing pattern as an IIFE. This can cause debugging headaches in the future. Editing the IIFE to instead invoke via the return is easier to maintain and for other devs to work with and learn initially. Also, it allows you to extend outside of your IFFE into another Module, example:
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.runMe();
anotherFunc();
}
return {
addClothes: init
}
}().addClothes();
I hope this helps to give you a better understanding of how/when/why to use the JS revealing pattern. Quick note: You can make your modules into IIFE, that's not a problem. You just limit the context of the scope you can work with. Another way of doing things would be to wrap the var User and var Clothes into a main module, and then make that an IIFE. This helps in preventing polluting your global namespace.
Example with what I wrote above:
// MAIN APPLICATION
var GettinDressed = (function() {
// MODULE ONE
///////////////////////////
Var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init,
style: someFunc
}
}();
// MODULE TWO
//////////////////////////
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.style();
anotherFunc();
}
return {
dressUp: init
}
}();
// Define order of instantiation
User.runMe();
Clothes.dressUp();
}());

How to detect when an .html() function is called in jQuery?

The problem is simple. I have a massive javascript application. And there are lot of times in the app where I use code which looks something like this -
$('#treat').html(new_data);
....
....
$('#cool').html(some_html_data);
....
....
$('#not_cool').html(ajax_data);
So what I want to do is, everytime this html() function is called I want to execute a set of functions.
function do_these_things_every_time_data_is_loaded_into_a_div()
{
$('select').customSelect();
$('input').changeStyle();
etc.
}
How do I do this? Thank you.
You can use the custom event handlers for that:
$('#treat').html(new_data);
// Trigger the custom event after html change
$('#treat').trigger('custom');
// Custom event handler
$('#treat').on('custom', function( event) {
// do_these_things_every_time_data_is_loaded_into_a_div
alert('Html had changed!');
});
UPDATE
Based on answer over here and with some modifications you can do this:
// create a reference to the old `.html()` function
$.fn.htmlOriginal = $.fn.html;
// redefine the `.html()` function to accept a callback
$.fn.html = function (html, callback) {
// run the old `.html()` function with the first parameter
this.htmlOriginal(html);
// run the callback (if it is defined)
if (typeof callback == "function") {
callback();
}
}
$("#treat").html(new_data, function () {
do_these_things_every_time_data_is_loaded_into_a_div();
});
$("#cool").html(new_data, function () {
do_these_things_every_time_data_is_loaded_into_a_div();
});
Easily maintainable and less code as per your requirements.
You can overwrite the jQuery.fn.html() method, as described in Override jQuery functions
For example, use this:
var oHtml = jQuery.fn.html;
jQuery.fn.html = function(value) {
if(typeof value !== "undefined")
{
jQuery('select').customSelect();
jQuery('input').changeStyle();
}
// Now go back to jQuery's original html()
return oHtml.apply(this, value);
};
When html() is called it usually make the DOM object changes, so you can look for DOM change event handler, it is called whenever your HTML of main page change. I found
Is there a JavaScript/jQuery DOM change listener?
if this help your cause.
You can replace the html function with your own function and then call the function html:
$.fn.html = (function(oldHtml) {
var _oldHtml = oldHtml;
return function(param) {
// your code
alert(param);
return _oldHtml.apply(this, [param]);
};
})($.fn.html);
I have a little script for you. Insert that into your javascript:
//#Author Karl-André Gagnon
$.hook = function(){
$.each(arguments, function(){
var fn = this
if(!$.fn['hooked'+fn]){
$.fn['hooked'+fn] = $.fn[fn];
$.fn[fn] = function(){
var r = $.fn['hooked'+fn].apply(this, arguments);
$(this).trigger(fn, arguments);
return r
}
}
})
}
This allow you to "hook" jQuery function and trigger an event when you call it.
Here how you use it, you first bind the function you want to trigger. In your case, it will be .html():
$.hook('html');
Then you add an event listener with .on. It there is no dynamicly added element, you can use direct binding, else, delegated evets work :
$(document).on('html', '#threat, #cool, #not_cool',function(){
alert('B');
})
The function will launch everytime #threat, #cool or #not_cool are calling .html.
The $.hook plugin is not fully texted, some bug may be here but for your HTML, it work.
Example : http://jsfiddle.net/5svVQ/

How to call function in another js file or trigger a custom event that passes a parameter as data object in Jquery?

I'm using reuqireJS and am struggling to call a function, which is in a js file I'm requiring. My main app.js "controller" requires (plugin)app.js, which handles all plugin configuration and plugin related functions.
This is from app.js
define([], function(){
var start = function() {
require(['jquery', 'overrides', 'jqm', 'multiview', 'respond'],function() {
// globals
var
// PROBLEM attempt at an external plugin function object
dataTablesExt = {},
...;
// call for (plugin)app.js
enhanceDataTables =
function( page, from ) {
var datatable = page.find('.table-wrapper table');
if ( datatable.length > 0 && datatable.jqmData('bound') != true ) {
datatable.not(':jqmData(bound="true")')
.each( function() {
var that = $(this),
tblstyle = that.jqmData("table-style");
that.jqmData('bound', true);
require(['services/datatables/app'], function (App) {
// this calls (plugin)app.js
App.render({style: tblstyle, table: that });
});
});
}
};
// PROBLEM - try to call function "Hello" inside datatables.app
anotherFunc=
function( page, from ) {
dataTablesExt.sayHello("john");
};
I guess my problem is how to set up the global variable dataTablesExt, so I can "fill" it with functions to be called globally. Here is what I'm trying inside (plugin)app.js:
define(['services/datatables/app', 'services/datatables/datatables.min'], function( app, datatables ) {
function render(parameters) {
...
// the function I want to call
function helloName( name ){
alert( name );
};
// I'm trying to add this function to the global "dataTablesExt"
dataTablesExt.sayHello = helloName;
}
But... doesn't work. I'm always getting:
dataTablesExt.sayHello is not a function
Question:
Can someone point me to what I'm doing wrong? If this is not possible, what would be an alternative.
I was thinking to trigger a custom event, but I would have to set up an object to pass along with the event, which I have no clue how to do.
Thanks for help!
Got it. I need to attach dataTablesExt to a global variable I'm using and not declare it as a variable. So like this:
// globals
var
...;
$.dataTablesExt = {};
Then I can assign functions to it and call them.

How can I provide a public function in a jQuery plugin?

I have written a small jQuery plugin with the following structure:
(function($) {
// Private vars
// Default settings
$.PLUGINNAME = {
id: 'PLUGINNAME',
version: '1.0',
defaults: {
min: 0,
max: 10
}
};
// extend jQuery
$.fn.extend({
PLUGINNAME: function(_settings) {
init = function() {
}
prepare = function() {
}
...
return this.each(function() {
_this = this;
init();
});
}
});
})(jQuery);
I am calling this as follows:
$("#update-panel").PLUGINNAME({
min: 1,
max: 20
});
I am trying to provide an additional public method where some data inside the function can be updated after the above function call to the plugin and am not sure how to go about doing this. What I am looking for is something like this:
_PluginInstance = $("#update-panel").PLUGINNAME({
min: 1,
max: 20
});
...
...
_PluginInstance.setMin(2); //should change the minimum to 2
setMin will probably use some of the plugin's internal variables so I am not understanding how to do this. I know I am not returning an instance of the plugin to do the above but can someone please tell me how to go about doing this by keeping the same plugin structure?
Just make the function a property of this within the PLUGINNAME object:
(function($) {
$.PLUGINNAME = {
// Default settings
};
// extend jQuery
$.fn.extend({
PLUGINNAME: function(_settings) {
// private methods
var init = function() {};
var prepare = function() {};
// public methods
this.setMin = function ( val ) {
_settings.min = val;
};
this.getMin = function () {
return _settings.min;
};
return this.each(function() {
_this = this;
init();
});
}
});
})(jQuery);
Then you could do:
_PluginInstance = $("#update-panel").PLUGINNAME({
min: 1,
max: 20
});
_PluginInstance.getMin(); // 1
_PluginInstance.setMin(2);
_PluginInstance.getMin(); // 2
EDIT: Oh god I can't believe I forgot the var keywords, why didn't y'all tell me my fly was down?
You could use the jQuery-UI method calling style:
$("#update-panel").PLUGINNAME('method', arguments...);
And then in your plugin:
PLUGINNAME: function(_settings, args) {
if(!$.isPlainObject(_settings)) {
// _settings (should) be the method name so
// do what needs to be done to execute the method.
return;
}
// Proceed as before.
You might want to use the arguments pseudo-array instead of the extra args parameter. You can store extra things in $(this).data('PLUGINNAME') inside your PLUGINNAME function if you need to attach internal data to your individual objects.
Not a jQuery guy myself, and mu is too short's answer seems to be the right one. But I'd imagine you could also do like it says on jQuery's own docs and use the .data() method to store the min/max values.
In other words, make your code look for existing data on the elements, so the first time you call $('#update_panel').plugin({min:1, max:20}), it won't find any existing data, so it'll place those min/max values in an object and "save" them using .data().
Then, when you later call $('#update_panel').plugin({min:2}) your code finds the existing data, and updates the values.
Just an idea
You can define a variable just like you've defined a plugin.
$.fn.PLUGINNAME.someVariable = 4;
or if you prefer, just declare an empty variable outside the plugin and then add to it from inside the plugin
var someVariable;
(function($) {
... the plugin code
})(jQuery);

Categories

Resources