I want my element.all() to return children with prototyped methods.
here's my code so far:
el.tags = function() {
var el = element.all(by.css('.tags > span'));
// return el;
return el.map(function(tag) {
tag.name = function() {
return tag.element(by.binding('tag.name')).getText()
}
tag.count = function() {
var text = tag.element(by.binding('tag.count')).getText()
return _.parseInt(text);
}
return tag;
});
}
the idea being once I call tags() the children have my prototyped methods immediately.
var tags = tags().filter(function(tag) {
return tag.name() === "foobar";
});
thanks!
Related
I'm trying to create something similar to d3(ex: d3.select()) but much more simple and I need to have a new instance each time I call the namespace function. Is this possible and/or am I approaching this wrong?
var dom = new function () {
var Element = null;
this.select = function (query) {
Element = document.querySelector(query);
return this;
};
this.append = function (elem) {
Element.append(elem);
return this;
};
};
Desired use
var bodyelement = dom.select("body");
var p = dom.select("p");
You need to run some code each time you use the dom object. So if the dom object was a function, you could call it to get a new instance.
var dom = function () {
var Element = null;
var newdom = {};
newdom.select = function (query) {
Element = document.querySelector(query);
return this;
};
newdom.append = function (elem) {
Element.append(elem);
return this;
};
return newdom;
};
console.log(dom() === dom(), "(false means the instances are different)");
var dom = new function () {
var Element = null;
this.select = function (query) {
Element = document.querySelector(query);
return this;
};
this.append = function (elem) {
Element.append(elem);
return this;
};
// add a way of accessing the resulting Element
this.element = function() { return Element; }
};
console.log(dom.select("body").element());
console.log(dom.select("p").element());
<p>blah</p>
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;
}
I'm trying to understand Javascript chaining with a return DOM element.
I'm not sure how to do this.
This is my code:
(function () {
function MyQuery(selector) {
if (!(this instanceof MyQuery)) {
return new MyQuery(selector);
}
this.nodes = document.querySelectorAll(selector);
for (var i = 0; i < this.nodes.length; i++) {
this.nodes[i] = this.nodes[i];
}
}
MyQuery.fn = MyQuery.prototype = {
parent: function () {
return this.nodes[0].parentNode;
},
color: function(setColor) {
this.nodes[0].style.color = setColor;
return this;
}
};
window.myQuery = window.$ = MyQuery;
})();
Call Methods:
myQuery(".mySpan").parent();
// Returns .. <div>
myQuery(".mySpan").parent().color("red");
// TypeError: myQuery(...).parent(...).color is not a function
HTML:
<div>
This DIV has some content.
<span class="mySpan">This is a span</span>
more content here.
</div>
I'm not sure why it would give me a TypeError, I have the parentNode which is the div all I want to do is set the color text of that div.
In order to make chainable methods available, you must not return a DOM element but rather an instance of your MyQuery class that has this method.
function MyQuery(selector) {
if (!(this instanceof MyQuery)) {
return new MyQuery(selector);
}
if (Array.isArray(selector)) {
this.nodes = selector;
} else {
this.nodes = [];
if (typeof selector == "string") {
var nodes = document.querySelectorAll(selector);
for (var i = 0; i < nodes.length; i++) {
this.nodes[i] = nodes[i];
}
}
}
}
MyQuery.prototype.parent = function () {
return new MyQuery([this.nodes[0].parentNode]);
};
MyQuery.prototype.color = function(setColor) {
this.nodes[0].style.color = setColor;
return this;
};
I would like to create a jQuery type chaining on an element created using javascript's document.createElement(). The following code is generating an error "Cannot call method 'appendChild' of undefined" whenever I try to run my "append" method on a parent object that was defined by my function. Any help or suggestions are appreciated.
this.el = (function () {
function _el() {
var self = this,
ele;
this.add = function (tag) {
ele = document.createElement(tag);
return this;
},
this.byId = function (id) {
ele = document.getElementById(id);
return this;
},
this.byClass = function (cl) {
ele = document.getElementsByClassName(cl);
return this;
},
this.id = function (name) {
ele.id = name;
return this;
},
this.cl = function (name) {
ele.className = name;
return this;
},
this.css = function (style) {
_this.setCSS(ele, style);
return this;
},
this.html = function (str) {
ele.innerHTML = str;
return this;
},
this.append = function (parent) {
if (parent.nodeType === 1) {
parent.appendChild(ele);
}
console.log(ele);
console.log(ele.nodeType);
return this;
};
return this;
}
return new _el();
}());
This is how I use the function in my code. The first use works while the second one does not. It has something to do with the type of object being returned by my function but I am not sure how to correct.
var dialog = hlp.el.add("div").cl("alphaDialog").append(document.body);
var top = hlp.el.add("div").append(dialog);
this.append function returns this object which holds _ele js object. We have to return our HTML element ele. In this.append we return ele;
this.el = (function () {
function _el() {
var self = this,
ele;
this.add = function (tag) {
ele = document.createElement(tag);
return this;
},
this.byId = function (id) {
ele = document.getElementById(id);
return this;
},
this.byClass = function (cl) {
ele = document.getElementsByClassName(cl);
return this;
},
this.id = function (name) {
ele.id = name;
return this;
},
this.cl = function (name) {
ele.className = name;
return this;
},
this.css = function (style) {
_this.setCSS(ele, style);
return this;
},
this.html = function (str) {
ele.innerHTML = str;
return this;
},
this.append = function (parent) {
if (parent.nodeType === 1) {
parent.appendChild(ele);
}
console.log(ele);
console.log(ele.nodeType);
//return this; // this holds javascript object, not element
return ele; // return our ele variable which holds the element
// this.append() is the end of the chain
};
return this;
}
return new _el();
}());
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