I'm facing the following issue.
Everytime I have to use querySelectorAll with Element.classList, I need to
Element.querySelectorAll
to an ArrayjQuery abstracts the above, so I would like to develop a helper method, similar to how jQuery works, that works as such:
myhelper('.someClass').classList.add('newClass'); // there are more than 1 .someClass items
myhelper('#id').classList.remove('existingClass');
Essentially myhelper(selector)
should under the hood abstract the points 1+2 above: get the NodeList from querySelectorAll, convert it to an Array, forEach the array and *executed the method that has been given by the user.
PS: to simplify things, it could work for a specific set of native methods: the classList methods and textContent for example.
My first impulse would be to suggest just using jQuery and extend where needed ;)
But to give it a go: if the my_Helper
function is used as an object, it can buffer the elements and contain functions that use those elements on itself. If my_Helper is called directly, it can be forced to return a new object.
Further the added functionality can return the object itself, so that chaining can be used just as in jquery. A simple example with addClass:
function my_Helper(query){
if(this.constructor !== my_Helper)
return new my_Helper(query); //if called directly (not as new()), return a new object
this.elements = document.querySelectorAll(query);
this.addClass = function(className) {
for(var el of this.elements)
el.classList.add(className);
return this; //to be able to use chaining
}
return this;
}
my_Helper('.someClass').addClass('newClass').addClass('newClass2'); //2 separate classes to test chaining
.newClass{
width:100px;
height:100px;
}
.newClass2{
border:1px solid black;
}
<div class= 'someClass'></div>
<div class= 'someClass'></div>
<div class= 'someClass'></div>
edit, based on the comment, it wouldn't be a problem to add the extra methods manually, but the wish is to make adding individual methods easier without copying the foreach? The below has a general invoke
function which can be called from outside the objects and helper functions that use this invoke to create the other methods (and also introduces a classList wrapper that simply calls addClass, for making it easier to migrate code)
function my_Helper(query){
if(this.constructor !== my_Helper)
return new my_Helper(query); //if called directly (not as new()), return a new object
this.elements = document.querySelectorAll(query);
let self = this;
this.invoke = function(property , func, ...pars){
for(let el of self.elements){
if(!func){ //no function given -> property setter
if(property)
el[property] = [pars]
}
else {
let p = property ? el[property] : el; //if no property is given, use element itself
if(!p) continue;
let fn = p[func];
if(!fn) continue; //function does not exist on the property or element
fn.apply(p,pars);
}
}
return self;
}
function fn(property, functionName, ...pars){
return (...pars) => self.invoke(property, functionName, pars);
}
this.addClass = fn('classList', 'add');
this.removeClass = fn('classList', 'add');
this.text = fn('textContent');
this.classList = {add:self.addClass, remove:self.removeClass}; //if classlist has to be used instead of addClass
return this;
}
my_Helper('.someClass').classList.add('newClass').addClass('newClass2').text('aaa');
.newClass{
width:100px;
height:100px;
}
.newClass2{
border:1px solid black;
}
<div class= 'someClass'></div>
<div class= 'someClass'></div>
<div class= 'someClass'></div>
In this basic implementation the invoke expects strings, but it could be easily extended to accept functions as well.