Pure JavaScript equivalent of jQuery click()? - javascript

I am building a small app which captures mouse clicks. I wrote the prototype in jQuery but, since it is a small app focusing on speed, embedding jQuery to use just one function would be an overkill.
I tried to adapt this example from JavaScriptKit:
document.getElementById("alphanumeric").onkeypress=function(e){
//blah..blah..blah..
}
but it didn't work when I tried this:
document.getElementsByTagName("x").onclick
What am I doing wrong?

Say you have a list of p tags you would like to capture the click for the <p> tag:
var p = document.getElementsByTagName("p");
for (var i = 0; i < p.length; i++) {
p[i].onclick = function() {
alert("p is clicked and the id is " + this.id);
}
}
Check out an example here for more clarity:
http://jsbin.com/onaci/

In your example you are using getElementsByTagName() method, which returns you an array of DOM elements. You could iterate that array and assign the onclick handler to each element, for example:
var clickHandler = function() {
alert('clicked!');
}
var elements = document.getElementsByTagName('div'); // All divs
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = clickHandler;
}

it looks a little bit like you miss more than just the click function of jQuery. You also miss jquery's selector engine, chaining, and automatic iteration across collections of objects. With a bit more effort you can minimally reproduce some of those things as well.
var myClickCapture = function (selector) {
var method, name,iterator;
if(selector.substr(0,1) === "#") {
method = "getElementById";
name = selector.substr(1);
iterator = function(fn) { fn(document[method](name)); };
} else {
method = "getElementsByTagName";
name = selector;
iterator = function(fn) {
var i,c = document[method](name);
for(i=0;i<c.length;i++){
fn(c[i]);
};
};
myClickCapture.click = function (fn){
iterator(function(e){
e.onclick=fn;
})
}
return myClickCapture;
}
I haven't tested the code, but in theory, it gets you something like this:
myClickCapture("x").click(function(e){ alert("element clicked") });
Hopefully this gives you a sense of the sorts of things jquery is doing under the covers.

document.getElementsByTagName("x")
returns an array of elements having the tagname 'x'.
You have to right event for each element in the returned array.

Related

DRY up htmlCollection to Array calls

I have a function that is currently using the .getElementBy... DOM calls in JavaScript.
var $ = function (selector) {
var elements = [];
var lastSelector = selector.substring(selector.search(/[^#.]+$/), selector.length);
if(selector.includes('#') !== true || selector.includes('.') !== true) {
elements.push(document.getElementsByTagName(lastSelector));
elements = Array.prototype.slice.call(elements[0]);
}
return elements;
};
There are a number of other if statements in the function using the code:
elements.push(document.getElementsByTagName(lastSelector));
elements = Array.prototype.slice.call(elements[0]);
or
elements.push(document.getElementsByClassName(lastSelector));
elements = Array.prototype.slice.call(elements[0]);
Ideally i'd like to DRY up the repeated:
elements = Array.prototype.slice.call(elements[0]);
but I cannot define it before the if statements because elements has not yet been populated. It therefore tries to run the code on an empty array and errors.
Any suggestions?
Instead of using a home-brew limited function for selecting elements by a selector, you could just use the standard querySelectorAll() available in all browsers including IE8+.
As for converting an array-like object (e. g. a DOM collection) to a real Array (what Array.prototype.slice.call() is used for in your code), I use the following function:
var arrayFrom = function(arrayLike) {
if (Array.from) {
return Array.from(arrayLike);
}
var items;
try {
items = Array.prototype.slice.call(arrayLike, 0);
}
catch(e) {
items = [];
var count = arrayLike.length;
for (var i = 0; i < count; i++) {
items.push(arrayLike[i]);
}
}
return items;
};
or its following simplified version if browsers not supporting passing a non-Array argument to Array.prototype.slice.call() (IE8- if I recall correctly) don’t matter:
var arrayFrom = function(arrayLike) {
return Array.from
? Array.from(arrayLike);
: Array.prototype.slice.call(arrayLike, 0);
};
Certainly consider #marat-tanalin answer. In the case where using querySelectorAll() is not an option, the following worked for me, thanks #master565 for the help:
To start, wrapping the lines:
elements.push(document.getElementsByTagName(lastSelector));
elements = Array.prototype.slice.call(elements[0]);
in a function:
function pushByTag(selector) {
elements.push(document.getElementsByTagName(selector));
elements = Array.prototype.slice.call(elements[0]);
}
Dried things up considerably. Then setting a variable for the if argument helped a lot:
if(selector.includes('#') !== true || selector.includes('.') !== true)
became:
var noClassOrId = selector.includes('#') !== true || selector.includes('.') !== true;
Both these refactors allowed me to single line my if statement in to something I'd argue was fairly readable:
if (noClassOrId) pushByTag(lastSelector);

How can I insert an argument like forEach, reduce and the like does?

I'm trying to reinvent the wheel, sort of.. Just messing around trying to remake some jquery functions.. I've come this far
var ye = function (ele) {
if (ele[0] == "#")
{
return document.getElementById(ele.slice(1));
}
else if (ele[0] == ".")
{
// returns an array, use index
return document.getElementsByClassName(ele.slice(1));
}
else
{
// also returns an array
return document.getElementsByTagName(ele);
}
}
but how can I use this element as a parameter in a function in the 'ye' prototype. For example, if I wanted to make fontsize how could I get the dom element like here:
ye.prototype.fontSize = function (ele)
{
ele.style.fontSize = "30px";
}
Just to add a bit to make the title relevant.. forEach inserts three arguments into the callback function, just like I want ye to insert ele into the fontSize function.
Just messing around trying to remake some jquery functions...
...but how can I use this element as a parameter in a function in the 'ye' prototype..
Here is a very crude and simple way to start...
Create a function with a property called elems which is an array and will store the selected DOM elements.
Like this:
var oye = function() { this.elems = []; };
On its prototype, you can create your custom functions which you want to expose. e.g. the function fontSize (as in your question), iterate over the elems array property that we created earlier changing the font size of each DOM element stored in. this points to the instance which is calling this function which we will ensure to be of type oye later on. To enable chaining, we simply return itself via this.
Like this:
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
Now create the selector function called ye. This serves the purpose of selecting the DOM elements, storing them in the elems array property of a new instance of oye class, and return the instance. We call the slice of the array prototype to convert the nodeList to an array.
Like this:
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
Now start using it in your code. Just like jQuery, you can use ye to select and then call your custom functions.
Like this:
ye("#elem1").fontSize('30px');
Just like jQuery, you can also chain multiple custom functions as shown in the complete working example below:
ye("P").fontSize('24px').dim(0.4);
Next step: Remember this is just a very crude example. You can now proceed to club the step 1 and 2 into a single call using the init pattern returning the new object from the selector function itseld. Learn more about Javascript and best practices.
Here is a sample working demo:
var oye = function() { this.elems = []; };
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
oye.prototype.dim = function(value) {
return this.elems.forEach(function(elem) {
elem.style.opacity = value;
});
return this;
};
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
ye("#elem1").fontSize('30px');
ye(".elem2").fontSize('20px');
ye("P").fontSize('24px').dim(0.4);
<div>This is normal text.</div>
<div id="elem1">size changed via id.</div>
<div class="elem2">size changed via class.</div>
<div class="elem2">size changed via class.</div>
<p>size changed and dimmed via tag name</p>
<p>size changed and dimmed via tag name</p>
Regarding your question, I may think you're new to JavaScript, or not familiar with its basic concepts. I'm not sure reinventing the wheel is a good thing in such conditions.
Since you've cited jQuery, you can have a look at its source code to understand how it works under the hood:
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L17-L23
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L38-L81
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core/init.js#L19-L114
Having that said, I would have done something like this:
var ye = function ( ele ) {
return new ye.prototype.init(ele);
};
ye.prototype.init = function( ele ) {
this._elements = [].slice.call(document.querySelectorAll(ele));
return this;
};
ye.prototype.forEach = function( fn ) {
this._elements.forEach(fn);
return this;
};
ye.prototype.fontSize = function( fontSizeValue ) {
this.forEach(function (ele) {
ele.style.fontSize = fontSizeValue;
});
return this;
};
The associated usage is as follow:
var myCollection = ye('.someClassName');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
myCollection.fontSize('45px');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
Use ye function calling before setting style, something like:
ye.prototype.fontSize = function(ele) {
ye(ele).style.fontSize = '30px';
}
returned object should be richer, like that:
var baseObject = {
// Will be used for the element:
element: null,
width: function(){ return this.element.getwidth(); /* or anything similar*/ }
// ... Further methods
}
and then in your ye function:
var ye = function (ele) {
var yeElem = clone(baseObject); // See comment below!!
if (ele[0] == "#") { yeElem.element = document.getElementById(ele.slice(1)); }
else if (ele[0] == "."){ /*...*/ }
else { /*...*/ }
return yeElem;
}
This way the new element has built in methods.
As for the clone() method used, it doesn't exist but you have to use some clone method.
I recommend Loadsh's _.cloneDeep() (here).

use querySelectorAll to create a loop for newly created elements

What I'm trying to do is use the querySelectorAll() function to create a loop where I can target each new <li/> element that I've created using the getInput() function that I created (which works fine). I wanted to use console.log() to test this, so that when I click on each <li/> item it confirms that I have done so in the console. This querySelectorAll() method is new to me and I'm also new at learning Javascript in general. So, any advice and explanation would be very helpful. thanks.
function getInput() {
var liValue = theForm.elements.input.value;
var liNew = document.createElement("li");
liNew.innerHTML += liValue;
var list = document.getElementById("ulList");
list.appendChild(liNew);
deleteLi;
}
function deleteLi() {
var handlers = document.querySelectorAll("li");
for (var i = 0; i < handlers.lenghth; i++) {
handlers[i].onclick = console.log("you got me");
}
}
Just add the click handler when you create the element. Running qSA as you have posted with select all li elements, even those you have already created.
function getInput() {
var liValue = theForm.elements.input.value;
var liNew = document.createElement("li");
liNew.onclick = function() { console.log("clicked!"); };
liNew.innerHTML += liValue;
var list = document.getElementById("ulList");
list.appendChild(liNew);
}

making getelementbyid plural

In the context of the code below (or anywhere), is it possible for a getelementbyid function to work plurally? Or do I need a different function, or possibly Jquery?
<script type="text/javascript">
window.onload = function()
{
var test = document.getElementById('test');
if (test)
{
test.className = 'unactive';
test.firstChild.onclick = function()
{
if(this.parentNode.className == 'unactive') {
this.parentNode.className = 'active';
}
else
{
this.parentNode.className = 'unactive';
}
}
}
};
</script>
You can use this;
document.getAllById = function(id){
if(document.all)
return document.all[id];
var elements = [],
all = document.getElementsByTagName('*');
for(var i=0;i<all.length;i++)
if(all[i].getAttribute('id')===id)
elements.push(all);
return elements;
}
Anyway, as #Pointy said, the id attribute is supposed to be unique, while class is used to define one or more elements that has some common properties
I assume you want to act on multiple elements using a list of IDs. (If I am wrong, and you actually want to select multiple elements with the same ID, you have done a Bad Thing, since IDs should be unique. In that case, you should use classes instead.)
In jQuery, you can accomplish this with a comma-separated list of id selectors (like $("#foo, #bar, #baz")) and implement your function like:
$("#foo, #bar, #baz").addClass("unactive")
.children(":first-child").click(function() {
var $this = $(this);
var $parent = $this.parent();
$parent.toggleClass("active unactive");
});
Without jQuery, this small function takes a list of IDs and results an array of nodes:
document.getElementsByIdList() {
var results = [];
for(var i=0; i<arguments.length; ++i) {
results.push(document.getElementById(arguments[i]));
}
return results;
}
Use it with you current code with:
var myNodeArray = document.getElementsByIdList("foo", "bar", "baz");
for(var i=0; i<myNodeArray.length; ++i) {
var test = myNodeArray[i];
if(test) {
// your code goes in here...
}
}

how to count total number of divs inside another div using javascript

How to count the total number of div elements that are contained in another div using javascript?
The getElementsByTagName() is not only a document method, but one that can run on any DOM element.
element.getElementsByTagName is
similar to
document.getElementsByTagName, except
that its search is restricted to those
elements which are descendants of the
specified element
see more at https://developer.mozilla.org/en/DOM/element.getElementsByTagName
So the actual code that does what you ask is
var container_div = document.getElementById('id_of_container_div');
var count = container_div.getElementsByTagName('div').length;
You can use #davidcmoulton's handy Gist:
https://gist.github.com/davidcmoulton/a76949a5f35375cfbc24
I find it quite useful that it doesn't only count DIVs but also lists the count of all element types of your page.
Here is a copy of the Gist for further reference:
(function (window, undefined) {
// Counts all DOM elements by name & logs resulting object to console.
var forEach = Array.prototype.forEach,
counter = {},
incrementElementCount = function (elementName) {
if (counter.hasOwnProperty(elementName)) {
counter[elementName] += 1;
} else {
counter[elementName] = 1;
}
},
processNode = function (node) {
var currentNode = node;
if (currentNode.nodeType === currentNode.ELEMENT_NODE) {
incrementElementCount(currentNode.nodeName);
if (currentNode.hasChildNodes) {
forEach.call(currentNode.childNodes, function (childNode) {
if (childNode.nodeType === currentNode.ELEMENT_NODE) {
processNode(childNode);
}
});
}
}
};
processNode(window.document.firstElementChild);
console.log(counter);
}(this));
There are many way to count divs element using jquery.
But most popular and simple way are:
$(document).ready(function(){
var divCount = $("div").size();
alert(divCount);
});
AND
$(document).ready(function(){
var divCount = $("div").length;
alert(divCount);
});
Its helpful for you

Categories

Resources