How to define shorthand function in javascript - javascript

I'm wondering how to setup a shorthand function for a shorthand selector in javascript. I apologise if that isn't the correct termonolgy.
Example:
var abc = function (selector) {
return document.querySelector(selector);
};
Allows you to:
var temp = abc('#someID').value;
What I'm wondering is how do you go about creating a custom .something (in a similar fashion to how jQuery have .val)?
For example calling:
abc('#someID').doSomething;
The the doSomething command allowing you to update the value (or pull it back) in a similar fashion to .val etc.
Thank you in advance.

Well, this is a very nice JS code-design question.
Let's try to create a simple jQuery implementation. For this, we should first scope things up.
jQuery function is a wrapper. It will hold a reference to the node in the DOM, and it will provide an array functions that will "operate" on the node.
Most of the functions (setters, for being more specific) should return a pointer to the wrapper itself, so you can chain operations.
You can define the wapper first, by defining your function.
// Here we define the constructor.
var q = function(selector){
this._node = document.querySelector(selector);
// Add more private stuff here if you like
};
//Now we can add functionality to the function prototype.
q.prototype.hide = function(){
this._node.style.visibility = 'hidden';
return this;
};
q.prototype.show = function(){
this._node.style.visibility = 'visible';
return this;
};
q.prototype.val = function(){
return this._node.value;
};
q.prototype.click = function(callback){
this._node.onclick = callback;
return this;
};
// This is just for not having to call new every-time we want a new instance
var $q = function(selector){
return new q(selector);
};
Now let's play a bit
<label for="name"> Hey I'm a text box</label>
<input id="name" value="" />
<button id="clickMe"> Click me </button>
We will attach a click handler to the button, when the user clicks, we display the value that the textbox contains, then we hide the text box. All in a single line (chained commands).
$q('#clickMe').click(function(){
alert($q('#name').hide().val());
});
See JsFiddle https://jsfiddle.net/4Lfangj4/

To make that, you must return an object (easiest solution) or extend the prototype (advanced solution).
Returning an object
You can return the doSomething() method:
var abc = function (selector) {
return {
doSomething: function() {caller()},
dom: document.querySelector(selector);
}
};
And it works:
var temp = abc("#someID").dom.value;
var doSome = abc("#someID").doSomething();
Extending prototype
You can add a function to the object prototype:
var abc = function(sel){
return document.querySelector(sel);
}
abc.prototype.doSomething = function() {
caller();
};
And it works
var temp = new abc("#someID");
temp.doSomething(); //doSomething() method
temp.value; // value attribute of element

Jquery keeps your selection in an internal property and decorates that property with methods that can help with is DOM presence.
Almost every time it returns the same object so you can chain method calls.
The point is that you cannot avoid keeping a reference to the selected DOM element and the decoration part
A simple example about selection and manipulating the DOM element
note here i store a reference to document.querySelector and document.querySelectorAll which are pretty much as good as jquery selection mechanism (Sizzle)
var MyWrapper = (function(){
var $ = document.querySelector.bind(document);
var $$ = document.querySelectorAll.bind(document);
var slice = Array.prototype.slice;
var selection;
var that = {
select: select,
remove: remove,
html: html
};
function select(selector){
selection = $(selector);
return that;
}
function remove(){
selection.parentNode.removeChild(selection);
return undefined;
}
function html(htmlstring){
if(typeof htmlstring == 'undefined'){
return selection.innerHTML;
} else {
selection.innerHTML = htmlstring;
return that;
}
}
return that;
}())
of course jQuery is a much complex and sophisticated library for all kind of use cases but the above code can get you started

Related

Override JavaScript (window) Function

I'd like to override a function which is being made by a javascript plugin. I know how to override regular window functions, but this is different. I'm not sure how to name it, but the structure of that function is like:
window.something.function
I have no idea how to override that. I have tried the following:
var originalFunction = window.something.function;
window.something.function = function(parameter) {
alert('called');
return originalFunction(parameter);
}
But it's not working.
Does someone know a solution?
Edit:
As I have been told my question is unclear, I have edited it again using the actual names of the plug-in.
The plugin is meant to be used as:
var myColor = new jscolor(target, options)
When this is being used, there is a function "inside" the object "jscolor" which is being called when setting the value of target element. I want to override that function to add an extra functionality without changing the original js file.
Code:
if (!window.jscolor) { window.jscolor = (function () {
var jsc = {
.....
jscolor : function (targetElement, options) {
....
//Function I want to change:
this.exportColor = function (flags) {
if (!(flags & jsc.leaveValue) && this.valueElement) {
var value = this.toString();
if (this.uppercase) { value = value.toUpperCase(); }
if (this.hash) { value = '#' + value; }
if (jsc.isElementType(this.valueElement, 'input')) {
this.valueElement.value = value;
} else {
this.valueElement.innerHTML = value;
}
}
}
}
};
My attempts so far:
var origJsColor = jscolor.exportColor;
jscolor.exportColor = function(flags) {
console.log('called');
return origJsColor(flags);
}
and the window attempt above.
The jscolor code you've shown creates an object with its own copy of exportColor (one is created for each object). So to replace it, you have to replace it on each instance as the instance is created.
You can do that as a one-off in much the way you showed, just working with the instance rather than the plugin function, and using Function#call to call it with the right this:
// Get the instance
var c = new jscolor(target, options)
// Update it
var origExportColor = c.exportColor;
c.exportColor = function(flags) {
console.log('called');
return origExportColor.call(c, flags); // Note the changes on this line
};
Or instead of
return origExportColor.call(c, flags);
you might use
return origExportColor.apply(c, arguments);
...if there's any chance of the function being called with anything other than exactly one argument. (arguments is a magic pseudo-array containing the arguments used to call the function.)
If you want to do that for all instance you might create, you can put a facade in front of jscolor to do that to each instance:
var realJscolor = jscolor;
jscolor = function() {
// Call the real function, passing along all the arguments we
// get automatically (`arguments` is a magic pseudo-array)
var retVal = realJscolor.apply(this, arguments);
// If it returned a non-`null` object, we want to use that instead
// of `this`; if not, we keep using `this`
if (!retVal || typeof retVal !== "object") {
retVal = this;
}
// Slip in our version of exportColor
var origExportColor = retVal.exportColor;
retVal.exportColor = function(flags) {
console.log('called');
// (Maybe use `apply` here instead)
return origExportColor.call(retVal, flags);
};
// Return the result, in case the real function overrode `this`
return retVal;
};
jscolor.prototype = realJscolor.prototype;
Then just use jscolor normally:
var c = new jscolor(target, options);
The reason for the retVal thing is that although normally a new expression's result is a refernece to the new object created by new, a constructor function can return a non-null object reference and, if it does, the new expression's result is that object reference instead. That's why we check the return value of realJscolor.
Of course, that means that all uses of jscolor on the page that use the global will now use your updated function instead. If you don't want that, just use your own name and don't override jscolor:
var myColor = function() {
var retVal = jscolor.apply(this, arguments);
// ...and so on...
return retVal;
};
myColor.prototype = jscolor.prototype;
Usage:
var c = new myColor(target, options);
Function
function a() {alert(this)} // will print `window` obejct
is defined in the window scope. That is, it is a method of the window. Your more difficult situation comes from the fact that this is different from window if you define function as a method in another object.
var a = {method: function() {alert(this)}}
you call a.method() but see that the same window again. You need to bind your function to the parent object to make it compete method.

Properly get "this" in jQuery plugin member

I have a JavaScript module that I would like to create a jQuery plugin interface to.
The module itself is like this:
var Foo = (function () {
"use strict";
var self = {};
self.add = function (selector, otherParam)
{
// Does things unto selector.
// Precisely what it does doesn't matter.
};
return self;
}());
and is used, with success, like this:
Foo.add(a);
Now, I would like to create a plugin that interfaces to this module,
so I can use it somewhat like this:
$.fn.foo = Foo;
$.fn.foo.add = function (param) {
var selector = this;
Foo.add(selector, param);
}
$(elem).foo.add(a);
The problem I'm facing is that I can't get "this" working in .add().
The best way I managed to do it was to not have Foo be self-initializing,
and use a syntax like:
$.fn.foo = Foo;
$(elem).foo().add(a);
It works, but I find it less aesthatically pleasing and less "clean".
Is there a way to do this? Am I on the wrong approach altogether?
Thankful for any input, and I apologize in advance if this has already been answered or is unfit in any other way.
I did search for answers, but I'm not well-versed in plugin authoring nor an expert on jQuery itself.
TL;DR: I have a module like Foo above, and would like to access it's members like a jQuery plugin.
Here is a simplified version of the pattern I normally use (error checking and extra features removed).
It uses a single class function and a plugin bridge extension method to allow attachment to multiple elements. Methods are called by using a string option value:
var Foo = (function () {
"use strict";
// Constructor
function Foo($element, options){
this.$element = $element;
this.options = options
this.fooVal = 0;
}
// Create method (called from bridge)
Foo.prototype.onCreate = function(){
this.fooVal = ~~this.$element.text()
};
// Add the specified val to the elements current value
Foo.prototype.add = function (val) {
this.fooVal += val;
// Update the element text with the new value
this.$element.text(this.fooVal);
};
return Foo;
})();
// Create a bridge to each element that needs a Foo
$.fn.foo = function (options, args) {
this.each(function () {
var $element = $(this);
// Try to get existing foo instance
var foo = $element.data("Foo");
// If the argument is a string, assume we call that function by name
if (typeof options == "string") {
foo[options](args);
}
else if (!foo) {
// No instance. Create a new Foo and store the instance on the element
foo = new Foo($element, options);
$element.data("Foo", foo);
// Record the connected element on the Foo instance
foo.$element = $element;
// Call the initial create method
foo.onCreate();
}
});
}
// testing
console.clear();
$('#test').foo();
$('#button2').click(function () {
$('#test').foo("add", 2);
});
$('#button10').click(function () {
$('#test').foo("add", 10);
});
For your example Foo takes the initial value from the element text and subsequent "add" calls modify that value.
JSFiddle: http://jsfiddle.net/TrueBlueAussie/o2u7egfy/3/
Notes:
~~ is just a fast short-cut for parseInt()
You could supply an initial value via the options parameter (ignored in first example). See following:
e.g.
// Create method (called from bridge)
Foo.prototype.onCreate = function(){
this.fooVal = this.options.value || ~~this.$element.text()
// Set initial value
this.$element.text(this.fooVal);
};
and start with
$('#test').foo({value: 999});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/o2u7egfy/4/

JavaScript return a default value

I want to make a library of functions like this (similar to what jquery is doing)
var myLib = function (idOfAnElement){
var myElement = document.getElementById(idOfAnElement);
return{
getHeight: function (){
return myElement.style.height;
},
getWidth: function (){
return myElement.style.width;
}
}
}
My problem is, that I don't know how to return
myElement
by default, if there is no other function called like
myLib('myId').getHeight; // Returns Height
myLib('myId') // Is supposed to return the HTML-Element with id = 'myId'
Create a privileged method returning the value of private myElement property itself
var myLib = function (idOfAnElement){
var myElement = document.getElementById(idOfAnElement);
return{
getHeight: function (){
return myElement.style.height;
},
getWidth: function (){
return myElement.style.width;
},
getElement: function() {
return myElement;
}
}
}
myLib('myId').getElement();
What you want can be achieved simply by adding the methods you want to the element object,
Javascript allows easy adding methods to existing objects, even the this pointer will point to the bound object.
var myLib = function (idOfAnElement){
var myElement = document.getElementById(idOfAnElement);
myElement.getHeight: function (){
return this.style.height;
}
myElement.getWidth: function (){
return this.style.width;
}
return myElement;
}
Note: While it works, I wouldn't recommend it.
You need to take care not to overwrite existing methods/fields and if multiple libraries will take the same approach a collision is likely.
And this is NOT what jQuery is doing: they create a wrapper object. To get the element from jQuery you need to use [0] for example $('#myEl')[0].

How to create a single jquery plugin function that is callable both on jQuery instances and the jQuery object itself?

I have created JQuery plugin like this:
$.fn.myPlugin = function(className)
{
if(className) {
// create new instance of className and return it
} else {
$(this).css("background-color","red");
}
};
When I use it like this: $("div").myPlugin(); it works fine.
Buy when I use $.myPlugin("TestClass") it does not work.
note: TestClass exist.
If you want $.customFunction to be set, you need to assign a value to it:
$.customFunction = function () {
alert('this works!');
};
given your update, you should use two separate functions:
$.myPlugin = function (classname) {
//create new instance of className and return it
}
$.fn.myPlugin = function () {
this.css('background-color', 'red');
};
Use the jQuery extend function:
jQuery.fn.extend({
myFunction: function(){
alert("Hi!");
}
});
Good luck!
For a simple plugin like that (that has no notion of this), you might want something like
$.myPlugin = $.fn.myPlugin = function()
{
alert("test");
};
$.fn is the prototype, so you're giving every jQuery instance that function (method, really).
Direct assignment to $ will make it a static function on the global jQuery object.
To update my answer to your updated question, here's one way to do what I think you want to do (keep it all in one function? -- please note: I am trying to read your mind here ;):
$.myPlugin = $.fn.myPlugin = function(className) {
var isInstance = (this instanceof $); // true if invoked on an instance
if (className) { // whenever a className is given
className = '.' + className; // turn it into a selector (assumes only one class)
return isInstance ? $(className, this) : $(className); // returns jQuery object
} else if (isInstance) { // when invoked on an instance
return this.css("background-color","red"); // sets color and returns this
}
};
If you want two completely different behaviors based on how the plugin is called, however, it is clearer and more maintainable to write them as two separate functions:
// returns jQuery object containing element(s) that have a given className
$.myPlugin = function(className) {
// turn className into selector (assumes only one class name is given)
var selector = className ? ('.' + className) : '';
return $(selector);
};
// sets color and returns this
$.fn.myOtherPlugin = function() {
return this.css('background-color', 'red');
};
You may additionally want to give the plugins different names that are indicative of their respective functions (hence myOtherPlugin).

Javascript: object return itself a.k.a. chaining

I am trying to build my own little jquery-like library but I'm having a really rough time with creating this chaining pattern. Basically I have one class with a bunch of methods that make it easier to manipulate the document. Here's an example
function MF(selector){
var DO; // Stands for DocumentObject
this.select = function(selector){
return document.getElementById(selector);
}
if(typeof selector === 'string'){
DO = this.select(selector);
}else if(selector instanceof HTMLElement){
DO = selector;
}
this.children = function children(selector){
return DO.getElementsByClassName(selector);
}
return {
MF: ???
}
}(null);
I might be wrong in my reflections but what I've come to figure out is that in order to have additional methods for a document object ( html element ) I either need to extend the HTMLElement prototype or pass the element along with my class. I've chosen the second option. I just can't figure out what to return in my class so that I can have chaining going on. What I simply aim at, for the sake of this example, is to be able to write the following line of code:
MF('someDiv').children('someClass');
In a desperate attempt I tried returning a new instance of MF which should not have instances by default and led myself to an infinite loop. I really cannot figure what I'm supposed to return there. Any help is greatly appreciated!
It looks like you also tried to use the module pattern, and haven't taken advantage of prototypes..
When you want to chain something, you either need to return itself (this) within the chainable methods, or return a new instance of your Object. In the example below I achieve chaining by using new instances (well the children looked like it needed to be a list so I did an Array of them).
var MF = (function () { // module pattern start
function MF(selector) {
if (!(this instanceof MF)) return new MF(selector); // always construct
this.node = null; // expose your DO
if (typeof selector === 'string') {
this.node = document.getElementById(selector);
} else if (selector instanceof HTMLElement) {
this.node = selector;
} else {
throw new TypeError('Illegal invocation');
}
}
MF.prototype = {}; // set up any inheritance
MF.prototype.select = function (selector) {
return new MF(document.getElementById(selector)); // returns new instance
};
MF.prototype.children = function (selector) {
var MFs = [],
nodes = this.node.getElementsByClassName(selector),
i;
for (i = 0; i < nodes.length; ++i) {
MFs[i] = new MF(nodes[i]);
}
return MFs; // array of items of new instances
};
return MF; // pass refence out
}()); // module pattern end
Then, for example, you can chain like..
MF(document.body).children('answer')[0].children('post-text')[0].node;
return this; will allow access to methods of the Constructor. Do it at the very bottom of the Constructor and the very bottom inside every method that belongs to it, if the method doesn't need to return another value.
function MF(selector){
var doc = document;
this.select = function(selector){
return doc.getElementById(selector);
}
// there are problems with some of your code
this.someMethod = function(){
/* do stuff - If you want to access an Element then
var thisIsNowGlobal = this.select('someId');
thisIsNowGlobal.innerHTML = 'someText';
Note, the keyword this is global not var
If you wrote this.select('someId').innerHTML the property would not exist
When a property of an Object is assigned to a variable or argument
the this value changes to the global context.
*/
return this;
}
return this;
}
You can do this pretty easily. Bear in mind that when you return this then if this has methods defined on it, then you can call them sequentially.
var MyUtilThing = function(){};
MyUtilThing.prototype.doStuff = function doStuff (){ // give it a name, helps in debugging
// do your thing
console.log('doing stuff');
return this; // this is your instance of MyUtilThing
}
var thing = new MyUtilThing();
thing.doStuff().doStuff().doStuff(); // etc
One way around having to explicitly create instances is do this in your constructor.
var MyUtilThing = function(selector){
var F = function(){};
F.prototype = MyUtilThing.prototype;
var toReturn = new F();
toReturn.initialize(selector);
return toReturn;
};
MyUtilThing.prototype.initialize = function initialize(selector){
this.selector = selector;
};
MyUtilThing.prototype.doStuff = function doStuff (){ // give it a name, helps in debugging
// do your thing
console.log('doing stuff to', this.selector);
return this; // this is your instance created in the constructor (the blank function with the same prototype as MyUtilThing)
}
var thing = MyUtilThing('div'); // no use of new here!
thing.doStuff().doStuff().doStuff(); // etc
But, getting into some slightly heavy territory there. Best bet is just to try and understand exactly how this is used in JS, and you'll get a long way.
Traditionally the way jQuery enables chaining is by creating a wrapper object for every type of return value. For example in your case it pays to create your own wrapper for HTMLElement:
function HTMLElementWrapper(element) {
if (element instanceof HTMLElementWrapper) return element;
this.element = element;
}
Now that you have an HTMLElementWrapper you can refactor your MF function as follows:
function MF(selector) {
return new HTMLElementWrapper(typeof selector === "string" ?
document.getElementById(selector) : selector);
}
The MF function now returns an HTMLElementWrapper object which has two methods select and children:
HTMLElementWrapper.prototype.select = function (selector) {
return new HTMLElementWrapper(this.element.getElementById(selector));
};
HTMLElementWrapper.prototype.children = function (selector) {
return new NodeListWrapper(this.element.getElementsByClassName(selector));
};
Ofcourse for the children function to work you'll need to create a NodeListWrapper constructor:
function NodeListWrapper(list) {
if (list instanceof NodeListWrapper) return list;
this.list = list;
}
Now you can chain methods as follows:
MF("someDiv").select("something").children("someClass");
To chain methods after .children("someClass") you need to add those methods to NodeListWrapper.prototype.

Categories

Resources