OOP - Accessing public "this" inside each - javascript

I'm trying to create a function to loop through some table rows but I need to use this.isempty instead of isempty. How do I access this inside of an each loop.
Code:
function checkValues(look) {
this.isempty = 1;
this.search = $(look).find("input").each(function(){
if ($.trim($(this).val())!="") {
this.isempty = 0;
}
});
return this.isempty;
}
Obviously the this.isempty = 0; will not work. How can I do this?

You can use a closure variable in this case to refer isempty
function checkValues(look) {
this.isempty = 1;
var self = this;
this.search = $(look).find("input").each(function () {
if ($.trim($(this).val()) != "") {
self.isempty = 0;
}
});
return this.isempty;
}
But a more appropriate way here is to use .filter() like
function checkValues(look) {
this.isempty = 1;
this.search = $(look).find("input").;
this.isempty = this.search.filter(function () {
return $.trim(this.value) != '';
}).length > 0;
return this.isempty;
}

Do you need to reference this because of a constraint in your code? Would the following work?
function checkValues(look) {
var isEmpty = 1;
$(look).find("input").each(function(){
if ($.trim($(this).val())!="") {
isEmpty = 0;
return false; // Breaks out of the loop for a little performance boost
}
});
return isEmpty;
}

Related

Can't call function on HTML element

I'm starting to write jQuery in Vanilla JS and my selectors work but when I call my append function on the HTML element I get an "is not a function" error.
var $ = function(){
this.select = function(input) {
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
return document.getElementById(input)
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
return document.getElementsByClassName(input)
}
else {
return document.getElementsByTagName(input)
}
},
this.append = function(text) {
return this.innerhtml = this.innerhtml + text
}
};
my console attempts:
var myQuery = new $();
returns undefined
myQuery.select("#testspan")
returns my span tag here
myQuery.select("#testspan").append("hellohello")
returns error
VM2207:1 Uncaught TypeError: myQuery.select(...).append is not a function(…)
From your snippet the return of each of the select method return a DOM element (or collection). Really what you would like to do is called Chaining where the result of the method returns the original object. Therefore you can keep calling additional methods on the same object.
Now in your example you are going to need a collection of elements (nodes) somewhere the object can then access again. Here is a simple example.
var $ = function () {
this.nodes = [];
this.select = function (input) {
var self = this;
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
var node = document.getElementById(input);
if (node)
this.nodes.push(node);
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
Array.prototype.slice.call(document.getElementsByClassName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
else {
Array.prototype.slice.call(document.getElementsByTagName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
return this;
},
this.append = function (text) {
this.nodes.forEach(function (i) {
i.innerHTML += text;
});
return this;
}
};
Sample Html:
<p id="test">This is test </p>
<p>This is number to</p>
Console (Chrome):
$ = new $()
$ {nodes: Array[0]}
$.select('p').append('hi')
Now a little issue here is you are (in the console) setting $ = new $() which effectivly overwrites the ability to call new $() again in the same script. I have provided a fiddle below that renames this to myQuery. Also changed that every time you call select will clear the node array.
Revised:
var myQuery = function () {
this.nodes = [];
this.select = function (input) {
this.nodes = [];
var self = this;
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
var node = document.getElementById(input);
if (node)
this.nodes.push(node);
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
Array.prototype.slice.call(document.getElementsByClassName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
else {
Array.prototype.slice.call(document.getElementsByTagName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
return this;
},
this.append = function (text) {
this.nodes.forEach(function (i) {
i.innerHTML += text;
});
return this;
}
};
$ = new myQuery();
$.select('p').append(' test selection by tag name ');
$ = new myQuery();
$.select('.p1').append(' test selection by class ');
$ = new myQuery();
$.select('#p1').append(' test selection by id ');
$ = new myQuery();
$.select('#p2').append(' test selection by id ').append('and then chanined').select('.p2').append(' still chaining');
Fiddle: https://jsfiddle.net/kxwt9gmg/
You need to change up your approach a bit. You are wanting to store a result and call a method on it. You can ONLY call a method that that particular object has. That object you are returning, the raw html element, doesn't have that method. What you want to do is store the html element and then return an OBJECT that performs operations on what was stored. You can accomplish this using closure. For example:
function miniQuery(input){
function elementIterate(collection, action){
for (var i = elements.length -1; i >= 0; i-- ){
collection[i].style.display = action;
}
}
var isCollection = function(element){
if(element instanceof HTMLCollection){
return true
} else{
return false
}
}
function findElement(element){
if (element.startsWith("#")) {
// id element selector
return document.getElementById(element.substring(1));
} else if (element.startsWith(".")) {
// class element selector
return document.getElementsByClassName(element.substring(1));
} else {
// tag element selector
return document.getElementsByTagName(element);
};
}
if (input != undefined) {
var _this = this;
this.element = findElement(input);
var elements = findElement(input);
}
return {
append: function(content, position = 'beforeend'){
var elements = _this.element;
if (isCollection(elements)) {
for(var i = elements.length -1; i >= 0; i--){
elements[i].insertAdjacentHTML(position, content)
}
}else{
elements.insertAdjacentHTML(position, content);
}
}
}
}
function $(input){
return selector(input);
}
function selector(input){
var query = new miniQuery(input);
return query;
}

What is wrong with my observable pattern?

I'm testing the observable pattern in javascript. My callbacks in the array never seem to execute. What is wrong with my syntax?
<script type="text/javascript">
var Book = function (value) {
var onChanging = [];
this.name = function () {
for (var i = 0; i < onChanging.length; i++) {
onChanging[i]();
}
return value;
}
this.addTest = function (fn) {
onChanging.push(fn);
}
}
var b = new Book(13);
b.addTest(function () { console.log("executing"); return true; });
b.name = 15;
</script>
From your code above it looks like you need to call your function name instead of assigning a value something like:
var b = new Book(13);
b.addTest(function () { console.log("executing"); return true; });
b.name(); //<-- Before b.name = 15
Setting b.name = 15 doesn't execute the function, it just overwrites the value of b.name.
You could use getters and setters to react to a changing value. See John Resig's blog post or the MDN reference
I edited your code to use them:
var Book = function (value) {
this.onChanging = [];
this._name = "";
}
Book.prototype = {
addTest: function (fn) {
this.onChanging.push(fn);
},
get name() {
return this._name;
},
set name(val) {
for (var i = 0; i < this.onChanging.length; i++) {
this.onChanging[i](val);
}
this._name = val;
}
};
var b = new Book(13);
b.addTest(function (val) {
console.log("executing", val);
return true;
});
b.name = 15;
b.name = 17;
working demo.
You can also make a more generic solution that can work for all your properties without having to define the getters and setters, a lot of frameworks use this approach.
Book = function () {
this._events = [];
this._rawdata = {};
}
Book.prototype = {
bind: function (fn) {
this._events.push(fn);
},
// pass the property, and it returns its value, pass the value and it sets it!
attr: function (property, val) {
if (typeof val === "undefined") return this._rawdata[property];
this._rawdata[property] = val;
for (var i = 0; i < this._events.length; i++)
// we pass out the val and the property
this._events[i](val, property);
}
};
b = new Book();
b.bind(function (val) {
console.log("executing", val);
return true;
});
b.attr("name","The Hobbit");
b.attr("SKU" ,1700109393901);
console.log(b.attr("name")); // --> The Hobbit
http://jsfiddle.net/wv4ch6as/
Of course you would want to change the binder so that you can bind onto properties not one bind for all properties, but I think this gets the idea.

Javascript select element in library

I am working on a javascript library that will work like this: tex("element").print("hi"). Here is the code:
(function (window) {
var regex = {
Id : /^[#]\w+$/,
Class : /^[.]\w+$/,
Tag : /^\w+$/,
validSelector : /^([#]\w+|[.]\w+|\w+)$/
},
tex = function(selector){
//only some of the functions need to select an element
//EX:
// style: tex(selector).style(style);
//one that would not need a selector is the random number function:
// tex().random(from,to);
if (selector){
if (typeof selector === 'string'){
var valid = regex.validSelector.test(selector);
if( valid ){
if(regex.Id.test(selector)){
this = document.getElementById(selector);
}
if(regex.Class.test(selector)){
this = document.getElementByClass(selector);
}
if(regex.Tag.test(selector)){
this = document.getElementByTagName(selector);
}
}
}else if(typeof selector === 'object'){
this = selector;
}
//this = document.querySelector(selector);
// I could make a selector engine byt I only need basic css selectors.
}
};
tex.prototype = {
dit : function(){
this.innerHTML = 'Hi?!?!?!'
}
};
window.tex = tex;
})(window);
When I try to run the code I get an error that says, "Left side of argument is not a reference" referring to this = document.getElementById(selector);
Does anyone know what is wrong with my code?
Because you can not set this.
To do something that you are after, you just return this.
without using a prototype
var foo = function( selector ) {
this.print = function () {
console.group("in print");
console.log(this.elements[0].innerHTML);
console.groupEnd("in print");
return this;
}
this.printAll = function () {
console.group("in printAll");
for (var i=0; i<this.elements.length; i++) {
console.log(this.elements[i].innerHTML);
}
console.groupEnd("in printAll");
return this;
}
this.elements = document.querySelectorAll( selector );
return this;
}
console.group("id");
foo("#foofoo").print();
console.groupEnd("id");
console.group("class");
foo(".bar").printAll().print();
console.groupEnd("class");
JSFiddle
Basic example with prototype
(function () {
var basic = function (selector) {
this.elements = document.querySelectorAll(selector);
return this;
}
basic.prototype.print = function () {
console.group("in print");
console.log(this.elements[0].innerHTML);
console.groupEnd("in print");
return this;
}
basic.prototype.printAll = function () {
console.group("in printAll");
for (var i = 0; i < this.elements.length; i++) {
console.log(this.elements[i].innerHTML);
}
console.groupEnd("in printAll");
return this;
}
var foo = function (selector) {
return new basic(selector);
}
window.foo = foo;
})();
console.group("id");
foo("#foofoo").print();
console.groupEnd("id");
console.group("class");
foo(".bar").printAll().print();
console.groupEnd("class");
JSFiddle

How to implement JavaScript Cascades..?

I'm reading "JavaScript the Good Parts" and it mentions cascades as a way to do method chaining in JavaScript but I can't find any code that explains how these methods should be implemented.
getElement('myBoxDiv').
move(350, 150).
width(100).
height(100).
color('red').
border('10px outset').
padding('4px').
appendText("Please stand by").
on('mousedown', function (m) {
this.startDrag(m, this.getNinth(m));
}).
on('mousemove', 'drag').
on('mouseup', 'stopDrag').
later(2000, function ( ) {
this.
color('yellow').
setHTML("What hath God wraught?").
slide(400, 40, 200, 200);
}).
tip('This box is resizeable');
The trick is that the method itself should only return this. That way, each time you chain these methods together, the object itself is the base of the call.
SomeObj.width(40) would then return just SomeObj, so adding the call .height(50) would function, and continue along.
In a cascade, we can call many methods on the same object in sequence in a single statement.
Lets try this example,
var Calc = function(){
this.result=0;
this.add = function(){
for(var x=0; x<arguments.length; x++){
this.result += arguments[x];
}
return this;
};
this.sub = function(){
for(var x=0; x<arguments.length; x++){
this.result -= arguments[x];
}
return this;
};
this.mult = function(){
if(this.result==0)this.result = 1;
for(var x=0; x<arguments.length; x++){
this.result *= arguments[x];
}
return this;
};
this.div = function(){
if(this.result==0) return this;
for(var x=0; x<arguments.length; x++){
this.result /= arguments[x];
}
return this;
};
this.eq = function(){
return this.result
};
}
var cal1 = new Calc();
cal1.add(3).sub(1).add(2) // Here result = 4;
These methods always return the object they belong to this, e.g.:
var txtProcesor = {
txt: '',
removeWhite: function () {
this.txt = this.txt.replace(/\s+/g, '');
return this;
},
evenToUp: function () {
var res = "";
for (var i = 0; i < this.txt.length; i++) {
if (i % 2 == 0) res += this.txt[i].toUpperCase();
else res += this.txt[i];
}
this.txt = res;
return this;
}
}
txtProcesor.txt = " abc def ";
alert(txtProcesor.removeWhite().evenToUp().txt);
This is basically the way JQuery works. The idea is to make each of those functions return objects which contain those functions again so to speak.
EDIT: You can download JQuery and look at the source code for it, because this is exactly what is going on in that library.
Here is a demo combine cascade with callback, and the usage, hope this will help.
let calNum = function(num) {
this.num = num;
this.add = function(dif, callback) {
this.num = this.num + dif;
callback(this.num);
return this;
}
this.sub = function(dif, callback) {
this.num = this.num - dif;
callback(this.num);
return this;
}
this.multi = function(m, callback) {
this.num = this.num * m;
callback(this.num);
return this;
}
return this;
};
calNum(3).add(3,function(result) {
console.log(result);
}).multi(2, function(result) {
console.log(result);
}).sub(1, function(result) {
console.log(result);
}); // The final result is 11
I was also going through the same book but didn't find any implementation of the concept. However I found this nice and short blog on this.
Here is how you can enable cascades:
function number(value) {
this.value = value;
this.plus = function (sum) {
this.value += sum;
return this;
};
this.return = function () {
return this.value;
};
return this;
}
console.log(new number(5).plus(1).return());

how to change javascript function to ActionScript 3?

anybody can help me to change that javascript function to AS3?
thanks :)
function parseURLtoVars(strLocation){
var rArray = new Array();
var key;
var urlString = new String(strLocation);
if (urlString.search(/\?/)>-1){
var qArray = urlString.split('?')[1].split('&');
if (qArray.length > 0){
for (key in qArray){
var arVal = qArray[key].split('=');
if (arVal.length ==2){
rArray[arVal[0]] = arVal[1];
} else {
continue;
}
}
return rArray;
} else {
return false;
}
}
return false;
}
How about
private function parseURLtoVars(strLocation:String):*
{
var rArray:Array = new Array();
var key:String;
var urlString:String = new String(strLocation);
if (urlString.search(/\?/)>-1){
var qArray:Array = urlString.split('?')[1].split('&');
if (qArray.length > 0){
for (key in qArray){
var arVal:Array = qArray[key].split('=');
if (arVal.length ==2){
rArray[arVal[0]] = arVal[1];
} else {
continue;
}
}
return rArray;
} else {
return false;
}
}
return false;
}
this returns params as an object, to return a boolean should be a simple edit.
function getParams(documentRoot):Object
{
try {
var params:Object = LoaderInfo(documentRoot.loaderInfo).parameters;
var pairs:Object = {};
var key:String;
for(key in params) {
pairs.key = String(params.key);
}
} catch(e:Error) {
return {};
}
return params;
}
I believe you just have to add type definitions to your function and variables.
So:
function parseURLtoVars(strLocation):Array
{
var rArray:Array = new Array();
var urlString:String = new String(strLocation);
...
for(var key:String in qArray)
...
return rArray;
} else {
return null;
}
}
return null;
}
I set the return of false to be nulls, but you can change your function return type to Object so you can return anything out of it, but I assumed you wanted an array to be returned.
It's not exactly what you asked, but in AS 3 I think there's an easier way:
import flash.net.URLVariables;
private function parseURLtoVars(strLocation:String):URLVariables {
strLocation = strLocation.indexOf("?") != -1 ? strLocation.split("?")[1] : strLocation;
return new URLVariables(strLocation);
}
And you could use it like this:
var testUrl:String = "test.php?key=value&key2=another_value";
var urlVars:URLVariables = parseURLtoVars(testUrl);
for(var k:String in urlVars) {
trace(k + " = " + urlVars[k]);
}

Categories

Resources