I have this code:
(function() {
var base = function (elem) {
var elements = document.querySelectorAll(elem);
return {
elems: elements[0],
on: function (evt, func) {
if(this.elems) this.elems.addEventListener(evt, func, false);
return this;
}
};
};
window.base = window._ = base;
})();
And I can do this:
_('form').on('submit', uploadImage);
But if i do:
_('form').appendChild(input);
i get an error: Object #<Object> has no method 'appendChild'
So how can i use _('element') with native functions and still make it work with the methods in my object?
Give your object an .appendChild function that calls the .appendChild on the element.
(function() {
var base = function (elem) {
var elements = document.querySelectorAll(elem);
return {
elems: elements[0],
on: function (evt, func) {
if(this.elems) this.elems.addEventListener(evt, func, false);
return this;
},
appendChild: function(el) {
this.elems.appendChild(el);
return this;
};
};
window.base = window._ = base;
})();
Side note. If you're only interested in the first element returned from querySelectorAll, you can use querySelector instead.
return {
elems: document.querySelector(elem),
on: function (evt, func) {
// ...
I think you can do this with prototype (not recommended):
// Prototype.js style
var Base = function (selector) {
return document.querySelector(selector);
};
Element.prototype.on = function (e, f) {
this.addEventListener(e, f, false);
return this;
};
elp = Base('#result');
elp.on('click', function () {
console.log(this);
});
elp instanceof Element; // true
elp.innerHTML; // text
Or with an object wrapper:
// jQuery style
var Base = function (selector) {
this[0] = document.querySelector(selector);
return this;
};
Base.prototype.on = function (e, f) {
this[0].addEventListener(e, f, false);
return this;
};
elj = new Base('#result'); // internal new called in jQuery
elj.on('click', function () {
console.log(this);
});
elj instanceof Base; // true
elj[0] instanceof Element; //true
elj[0].innerHTML; // text
Related
I am trying to understand how to work jQuery and other libraries. I would like to know how to create a selector with this format:
$("#selector").get();
By the moment, I am trying the next, but i don't know how to run internal functions (get(), set()):
var $ = (function() {
var jQuery = {
get: function() {
console.log("get() function!!");
return this;
},
set: function() {
console.log("set() function!!");
return this;
}
};
return function(el) {
return document.querySelector(el);
}
})();
I have read something about modular pattern design in JavaScript, but I don't understand all.
The way to make chainable functions, is to first and foremost create instances with the new keyword.
This can be done "automatically" by making sure the this value of the called function is an instance of itself, if not explicitly call it with new.
Then it's just a matter of returning the instance and using prototyped methods.
var $ = function(selector) {
if (! (this instanceof $) ) {
return new $(selector);
}
this.el = document.querySelectorAll(selector);
return this;
}
$.prototype.css = function(prop, val) {
this.el.forEach(function(element) {
element.style[prop] = val;
});
return this;
}
$('#test').css('color', 'red').css('font-size', '30px')
<div id="test">test</div>
const $ = function(selector) {
if (!(this instanceof $)) {
return new $(selector);
};
this.el = document.querySelectorAll(selector);
};
$.prototype.css = function(obj) {
this.el.forEach(function(element) {
element.style[Object.keys(obj)[0]] = Object.values(obj);
});
};
$.prototype.click = function(callback) {
this.el.forEach(function(element) {
element.addEventListener('click', callback, false);
});
};
jQuery this or $(selector) is array like [div, div] not object {el: [div, div]}, so its not using this.el to modified the elements, here simplified version
if (window.$ === undefined) window.$ = (function () {
var $, fun = {}, emptyArray = [];
function Z(dom, selector) {
var i, len = dom ? dom.length : 0;
for (i = 0; i < len; i++) this[i] = dom[i];
this.length = len;
this.selector = selector || '';
}
fun.Z = function (dom, selector) {return new Z(dom, selector);};
fun.init = function (selector, context) {
if (!selector) return fun.Z();
var dom = document.querySelectorAll(selector);
return fun.Z(dom, selector);
};
Z.prototype = {
splice: emptyArray.splice,
forEach: emptyArray.forEach,
html: function (str) {
return this.forEach(function (el) {
el.innerHTML = str;
});
},
css: function(obj, value){
if(typeof obj == 'object'){ // like: .css({background: 'red'})
for(var k in obj){
return this.forEach(function (el) {
el.style[k] = obj[k];
});
}
}
else{ // called: .css('background', 'red')
return this.forEach(function (el) {
el.style[obj] = value;
});
}
}
};
$ = function (sel, ctx) {return fun.init(sel, ctx); };
return $;
})();
<div class="test"> AAAA </div>
<div class="test"> BBBB </div>
<button onclick="$('.test').css({background: 'red'})">red</button>
<button onclick="$('.test').css('background', 'blue')">blue</button>
<br />
<button onclick="console.log($('.test'))">log to console</button>
I am trying to write own custom plain Javascript plugin.
Here is my sample plugin code:
(function() {
var pMethods = {
append: function(text) {
var node = this.node;
node.innerHTML += text;
},
click: function(fn) {
if (this.node instanceof Array) {
this.node.forEach(function(e) {
e.addEventListener('click', function() {
fn();
});
}, this);
} else {
this.node.addEventListener('click', function(e) {
fn(e);
});
}
}
};
myPlugin = function(selector) {
this.node = document.querySelectorAll(selector);
if (this.node.length === 1) {
this.node = this.node[0];
}
return this.node;
};
myPlugin.prototype = pMethods;
this.r = function(selector) {
return new myPlugin(selector);
};
}());
which has just two function append and click.
Here is my HTML:
<div class="close"></div>
Now I am trying to add click event on close div as follow:
r('.close').click(function() {
alert('Hi')
});
but it is not working as expected and I don't know what I'm missing here.
Your code did not work because you were explicitly checking if your element collection is an Array. Any element collection returned will be a NodeList which is an array like object, but not an array.
if (this.node instanceof Array)
should be
if (this.node instanceof NodeList)
Or you could use Array.prototype.slice to convert the NodeList to an Array
this.node = Array.prototype.slice.call(
document.querySelectorAll(selector)
)
Here are a couple of optimisations.
(function() {
var pMethods = {
append: function(text) {
// iterate over the collection
this.nodes.forEach(function(node) {
node.innerHTML += text;
})
// return this for chaining
return this
},
click: function(fn) {
// iterate over the collection
this.nodes.forEach(function(e) {
e.addEventListener('click', fn);
});
// return this for chaining
return this
},
find: function(selector) {
return new myPlugin(
// flat map over each of the nodes in the collection
this.nodes.reduce(function(nodes, node) {
return [].concat.apply(nodes, node.querySelectorAll(selector))
}, [])
)
}
};
myPlugin = function(nodes) {
// changed constructor to recievea array of elemnets only
// it's private so won't affect anything else
this.nodes = nodes
};
myPlugin.prototype = pMethods;
this.r = function(selector) {
var nodes = null
// handle creating the object with normal elements
if (selector instanceof HTMLElement) {
nodes = [selector]
}
else {
nodes = [].slice.call(
document.querySelectorAll(selector)
);
}
return new myPlugin(nodes);
};
}());
r('.close')
.click(function(e) {
console.log('alerts suck! ' + e.target.textContent)
r(e.target).find('.child').append(' appended child!')
})
.append(' append works!')
<div class="close">
close
<div class="child">this is the child</div>
</div>
your constructor function (e.g. myPlugin = function(selector) {) should return this instead of this.node
This doesn't work.
var genericClickHandler = function () {
this.handlers = [];
if (console && console.log) {
console.log("this:", this);
console.log("event:", event);
}
};
genericClickHandler.addHandler = function (handlerSpec) {
this.handlers.push(handlerSpec);
return this;
};
genericClickHandler.executeHandler = function (handlerName) {
for (var i = 0; i < this.handlers.length; i++) {
if (handlerName === this.handlers[i][0]) {
this.handlers[i][1]();
}
}
return this;
};
It doesn't work because the addHandler can't see the this.handlers in genericClickHandler.
Anyway what I'm after is function that gets defined once, but has methods and properties. I want to be able to use the function with Google Maps like this:
heatmap.addListener("click", genericClickHandler)
circle.addListener("click", genericClickHandler)
polygons.addListener("click", genericClickHandler)
So in the first instance, it only reports the this and event object. However, I then want to write code which extends the genericClickHandler dynamically so that it can implement map-object-specific behaviour.
Here's an example of what I meant using an object rather than a function.
var genericClickHandler = {
handlers: []
};
genericClickHandler.addHandler = function (name, fn) {
this.handlers.push([name, fn]);
return this;
};
genericClickHandler.executeHandler = function (name) {
for (var i = 0, l = this.handlers.length; i < l; i++) {
if (this.handlers[i][0] === name) this.handlers[i][1]();
}
};
genericClickHandler.addHandler('click', function () {
console.log('hi');
});
genericClickHandler.addHandler('click', function () {
console.log('hallo again');
});
genericClickHandler.executeHandler('click'); // hi... hallo again
DEMO
if you want to create an object, here you can see 2 ways to do the same thing, javascript got multiple way to write the same things.
var genericClickHandler = function()
{
this.handlers = [];
this.addHandler = function (handlerSpec)
{
this.handlers.push(handlerSpec);
return this;
},
this.executeHandler = function (handlerName)
{
this.handlers[handlerName]();
return this;
}
};
//sample:
var tmp = new genericClickHandler();
console.log(tmp.handlers);
console.log(tmp.addHandler("TEST"));
Another way to write the same object, but more optimised : prototype will be stored once for each object
var genericClickHandler = function(){}
genericClickHandler.prototype =
{
handlers:[],
addHandler : function (handlerSpec)
{
this.handlers.push(handlerSpec);
return this;
},
executeHandler : function (handlerName)
{
this.handlers[handlerName]();
return this;
}
}
//sample:
var tmp = new genericClickHandler();
console.log(tmp.handlers);
console.log(tmp.addHandler("TEST"));
I have following javascript code
function MyFunc () {
var add = function () {
return "Hello from add";
};
var div = function () {
return "Hello from div";
};
var funcCall = function (obj) {
if (!obj) {
throw new Error("no Objects are passed");
}
return obj.fName();
};
return {
func: function (obj) {
funcCall(obj);
}
};
}
var lol = new MyFunc();
When lol.func({fName: add}); is passed it should invoke the function private function add or when lol.func({fName: div}); is passed it should invoke the private div function. What i have tried does not work. How can i achieve this.
DEMO
In this case it's better to store your inner function in the object so you can easily access this with variable name. So if you define a function "map"
var methods = {
add: add,
div: div
};
you will be able to call it with methods[obj.fName]();.
Full code:
function MyFunc() {
var add = function () {
return "Hello from add";
};
var div = function () {
return "Hello from div";
};
var methods = {
add: add,
div: div
};
var funcCall = function (obj) {
if (!obj) {
throw new Error("no Objects are passed");
}
return methods[obj.fName]();
};
return {
func: function (obj) {
return funcCall(obj);
}
};
}
var lol = new MyFunc();
console.log( lol.func({fName: 'add'}) );
When you pass lol.func({fName: add}) add is resolved in the scope of evaluating this code, not in the scope of MyFunc. You have to either define it in that scope like:
function MyFunc () {
var add = function () {
return "Hello from add";
};
var div = function () {
return "Hello from div";
};
var funcCall = function (obj) {
if (!obj) {
throw new Error("no Objects are passed");
}
return obj.fName();
};
return {
add: add,
div: div,
func: function (obj) {
funcCall(obj);
}
};
}
var lol = new MyFunc();
lol.func({fName: lol.add});
Or use eval.
I'm developing a small framework (in JS) and for esthetic reasons and simplicity I was wondering if there could be a way to implement something like PHP "__invoke".
For example:
var myClass = function(config) {
this.config = config;
this.method = function(){};
this.execute = function() {
return this.method.apply(this, arguments);
}
}
var execCustom = new myClass({ wait: 100 });
execCustom.method = function() {
console.log("called method with "+arguments.length+" argument(s):");
for(var a in arguments) console.log(arguments[a]);
return true;
};
execCustom.execute("someval","other");
Desired way to execute:
execCustom("someval","other");
Any ideas? Thanks.
if you are ready to use JS pattern, you can do this in following way:
var myClass = function(opts) {
return function(){
this.config = opts.config;
this.method = opts.method;
return this.method.apply(this, arguments);
};
};
var execCustom = new myClass({
config:{ wait: 100 },
method:function() {
console.log("called method with "+arguments.length+" argument(s):");
for(var a in arguments) console.log(arguments[a]);
return true;
}});
execCustom("someval","other");
jsbin
this is the best way I can think of
UPDATED VERSION (by op)
var myClass = function(opts) {
var x = function(){
return x.method.apply(x, arguments);
};
x.config = opts.config;
x.method = opts.method;
return x;
};
var execCustom = new myClass({
config:{ wait: 100 },
method:function() {
console.log("called method with "+arguments.length+" argument(s):");
for(var a in arguments) console.log(arguments[a]);
return true;
}});
execCustom("someval","other");
jsbin
Just return a function that will form the public interface:
function myClass(config)
{
var pubif = function() {
return pubif.method.apply(pubif, arguments);
};
pubif.config = config;
pubif.method = function() { };
return pubif;
}
The rest of the code remains the same.