Equivalent of jQuery code in Javascript - javascript

This is the jQuery code I have
$('p').click(function(){
alert("click successful!");
});
This is the JS code I could come up with
window.onload = function() {
var para = document.getElementsByTagName('p');
for(var i = 0; i < para.length; i++) {
para[i].addEventListener('click',function(){
alert("click successful!");
});
}
}
The Javascript code is too bulky, is there a way where I can select a tag by its name and write the code as -
"If any 'p' tag is clicked, alert('click successful')"
instead of looping through all the <p></p> tags?
Any alternative way using tag name?

You can use event delegation - add a click handler to a higher level element and check event.target
document.body.addEventListener("click", function(e) {
if (e.target.tagName.toLowerCase() == "p") alert("click succeeded");
});
Demo: http://jsfiddle.net/jdkr3sch/

jQuery is "less code" because you're calling a pre-written function. Don't want to use jQuery? Then write your own functions.
function addEventToElements(tagname,handler) {
var elems = document.getElementsByTagName(tagname);
for(var i = 0, l = elems.length; i<l; i++) {
elems[i].addEventListener('click',handler);
}
}
Now in your actual code, you can just write:
addEventToElements('p',function() {alert("click succeeded");});
Congratulations, you have re-invented jQuery.
... Or not, because the reason jQuery is so popular is that it does a lot more. Things like normalising browser support (for those that use attachEvent or the old onEventName handlers) are half the reason jQuery exists, and you'd have to account for all of them in your own re-invention ;)

Here's a shorter way.
document.addEventListener('DOMContentLoaded', function() {
document.body.addEventListener('click', function(evt) {
if (evt.target.matches('p, p *')) alert('Paragraph clicked!');
}, false);
}, false);
Notes:
1) This has the advantage of event delegation, which is something I'd suggest looking into. In a nutshell, means you bind the event once, not N times, and then interrogate which element fired it when it fires, i.e. in the callback, not at the point of declaring the event as you are currently.
2) For waiting to use elements, use the DOMContentLoaded event rather than window.onload - the former is (loosely) analogous to jQuery's DOM ready handler.
3) matches() is a relatively modern method and won't work in ancient browsers, or may need a vendor-prefixed version - http://caniuse.com/#feat=matchesselector

for selecting:
document.querySelectorAll('p')
(also for more than one element p ).
AFAIK this is the closest thing to $('p')
addEventListener('click',function(){alert("click successful!")}
to add click handler to single element.
to simulate an array you can use the [].slice.call on the dom element collection (in this way you use .forEach() method) .
all together:
[].slice.call(document.querySelectorAll('p')).
forEach(function(x){x.addEventListener('click',
function(){alert("click successful!")
})})
https://jsfiddle.net/maio/m861hbmh/

Related

jQuery force dynamic event to bind first

I'm binding events on the document element to make them dynamic.
The problem is that these events execute AFTER events binded directly on the elements.
I have code that binds an element to execute first, but it doesn't work with dynamic content. Here is the code I would use for non dynamic content :
bindFirst: function (name, fn) {
var elem, handlers, i, _len;
this.bind(name, fn);
for (i = 0, _len = this.length; i < _len; i++) {
elem = this[i];
handlers = jQuery._data(elem).events[name.split('.')[0]];
handlers.unshift(handlers.pop());
}
}
This function is under .fn.extend.
As I said earlier, I want to be able to do the same, for for dynamic content bound on the document variable. Eg...
$(document).on("click", "div", function(){});
I want this to execute AFTER the code directly above :
$("div").on("click", function(){});
Like #epascarello wrote in the comment:
this is not possible, you need to figure out something different.
A workaround must be thought around the logic of your code. In any case I propose you a "let me say" bad code just to create the evidence of what should happen in order to revert event bubling.
Use the .trigger() method in order to add parameters and use event.stopPropagation() in order to avoid an infinite loop.
Test in the delegated event if the current element has the click event handler and so trigger again the event.
$('#btn').on('click', function(e) {
$(document.body).append($('<div/>', {text: 'CLICK ME: NEW DIV'}));
});
$("div").on("click", function(e, fromInner){
// revert event bubling....
if (fromInner == undefined) {
return;
}
e.stopPropagation();
//normal code.........
console.log('Normal click event');
});
$(document).on("click", "div", function(e) {
// revert event bubling....
if ($._data(this).events != undefined && $._data(this).events[e.type] != undefined) {
$(this).trigger('click', ['fromInner']);
}
console.log('Delegated Click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" id="btn">Add new DIV</button>
<div>CLICK ME</div>
You can't do this with jQuery event delegation, it just uses event bubbling, which works from the target element out to the containers. But the plain Javascript addEventListener() method also supports event capture, which goes the other direction. The optional third argument can be used to enable capture mode for an event listener. See Event Bubbling and Capturing.
So I think you should be able to write:
document.addEventListener("click", function() {...}, true);

Optimize live elements' selectors in jQuery

I read a lot about optimization in jQuery in some links below:
jQuery Website , Performance
jQuery Best Practices - Greg Franko
jQuery Coding Standards and Best Practices
14 Helpful jQuery Tricks, Notes, and Best Practices
and more ...
But none of them mentioned for .on() caching selectors. I don't know if there is any way to use cached elements in these kind of selectors.
for example I have a lot of these selectors in my script.js file.
$(document).on('click', '.menu li.remove', function(e){ ... });
$(document).on('click', '.menu li.edit', function(e){ ... });
$(document).on('click', '.menu li.action', function(e){ ... });
$(document).on('click', '.menu li.anotherAction', function(e){ ... });
and much more. .menu is a menu and can be anywhere in document, so I can't use specific id container to select it. like this:
$('#sidebar').on('click', '.menu li.action', function(e){ ... });
is there any way to optimize these selectors. Checking for existence maybe, caching .menu if it is possible.
When you need to eek out every last bit of performance, you probably need to ditch abstractions.
If you do your own delegation, you'll certainly see a performance improvement.
Because in the example you gave, all the delegation is identical except for the class name, I'd bind a single handler, put the code in separate functions, and then examine the e.target and its ancestors manually looking for the .menu li. If that's found, then check the class of the li, and invoke the correct handler.
var handlers = {
remove: function() {/*your code*/},
edit: function() {/*your code*/},
action: function() {/*your code*/},
anotherAction: function() {/*your code*/}
};
var targets = Object.keys(handlers);
document.onclick = function(e) {
e = e || window.event;
var li;
var node = e.target || e.srcElement;
var targetClass;
do {
if (!li) {
if (node.nodeName === "LI") {
li = node;
}
} else if (node.className.indexOf("menu") > -1) {
targetClass = li.className
break;
}
} while(node = node.parentNode);
if (!targetClass)
return;
for (var i = 0; i < targets.length; i++) {
if (targetClass.indexOf(targets[i]) > -1) {
handlers[targets[i]].call(li, e);
}
}
}
In the code above, as we traverse up from the e.target, we first check to see if we're on an li. If so, grab it and continue one.
As we continue, we no longer need to check for li elements, but we now need to check for an element with the menu class. If we find one, we grab the class name of the li we previously found and then halt the loop.
We now know we have our menu li.someClass element. So then we can use the class that we found on the li to look up the proper function to invoke from our list of functions we made above.
You should note that my .indexOf() class testing is ad hoc, and could result in false positives. You should improve it. Also, the code needs more tweaking since we're caching the li without knowing if it actually has a class that we're interested in. That should be fixed as well.
I'll leave it to you to add the necessary tweaks if you desire. :-)
I personally think you are worrying about speed where speed is not an issue.
If the menus are not loaded dynamically, there is nothing stopping you from combining delegated event handlers with normal jQuery selectors to target more of the closer elements (e.g. your .menu class):
e.g.
$('.menu').on('click', 'li.remove', function(e){ ... })
.on('click', 'li.edit', function(e){ ... })
.on('click', 'li.action', function(e){ ... })
.on('click', 'li.anotherAction', function(e){ ... });
This will create a handler on each menu (so closer to the elements).
If your menus are dynamically loaded, then your existing code is perfectly fine, as my understanding is that delegated event handlers only apply the selector argument to the elements in the bubble chain. If that is the case, delegated events will be pretty darn fast anyway. Certainly faster than you can click your mouse! I have never had speed issue with delegated event handlers and I probably overuse them in my plugins (I always assume dynamic content in those).

Plain JavaScript version of e.preventDefault();

With the following jQuery based script you can stop the default action of dummy links, ie: <a href="#">
$('a').click(function(e) {
e.preventDefault();
});
What would be the plain vanilla version of this script?
I'm not a JavaScript programmer but I'm thinking it may be something that uses return false;. Again, I may be totally wrong.
Thanks in advance for your help.
You have event.preventDefault() available in vanilla javascript as well. In a generic way you can always use return false. If you attach an event handler you are guaranteed to get event agument passed in the handler as opposed to using the onclick or any other attributes of the element (In which case you should rely on the specific event object available in side the handler which you may not get in all browsers, like in IE you would use window.event).
Ex: -
document.getElementById('someId').addEventListener('click', function(e){ //say this is an anchor
//do something
e.preventDefault();
});
So for all the anchors:
var anchors = document.getElementsByTagName('a');
for(i=0, len=anchors.length; i<len; i++){
anchors[i].addEventListener('click', function(e){e.preventDefault();});
}
// Like $('a'), gets all the <a> elements in the document.
var aElements = document.getElementsByTagName('a');
// Create one function object instead of one per <a> element.
// The calling convention is the same for jQuery as for regular JS.
function preventDefaultListener(e) { e.preventDefault(); }
// For each a element,
for (var i = 0, n = aElements.length; i < n; ++i) {
// register the listener to be fired on click.
aElements[i].addEventListener('click', preventDefaultListener);
}
Return false is the way to go. E.g. if your anchor already has an href:
Click me
calling onclick like this will only perform the function, but not the navigation.
yes you can use return false;
test
#Ricardo you are correct sir.
Not only does return false disable the default action for a said element, it prevents bubbling also.
e.preventDefault();
e.stopPropagation();
is equal to
return false;
you decide :)

Way to remove javascript events from the html tags?

Is there any way to remove all the javascript events from the html tags?
For example
<img src=" " onclick="" />
<img src=" " onerror="" />
I want to strip onclick and onerror from these tags. As in the example, I want to strip all the javascript events from the html tags.
You can do this to clear the events :
// get all img elements
var imgElements = document.getElementsByTagName('img');
// loop elements
for (var i = 0; i < imgelements.length; i++) {
// clear handler
imgElements[i].onclick = null;
// add other handlers here
// imgElements[i].<here> = null;
}
Docs for getElementsByTagName() here
Update 1 - All elements on document
If you want to get all of the elements on the document then use document.getElementsByTagName('*')
Update 2 - All events
To clear all events have a look at this answer
You can check out jquery .unbind().
Reference: http://api.jquery.com/unbind/
It's also easy to put the event handler back when you need it using .bind()
by using jquery:
$('*').each(function(){
if ($(this).is('img')) {
$(this).unbind('click');
}
});
if you want to only remove the specific function onclick event, you can do like this..
$(this).unbind('click', FunctionName);
As of jQuery 1.7, you are also able use $.on() and $.off() for event binding, so to unbind the click event, you would use the simpler and tidier:
$(this).off('click');
If you mean "remove" as in physically remove them from the page source, you could probably use a regular expression to get most of them, replacing pattern on(\w+?)="(.*?)" with a blank string. (I've not tested, it may need tweaking)
If you mean "remove" as in logically remove them by unbinding them, you could use the unbind() method in jQuery, or set each event to null looping through the DOM.
Try this javascript in the document.onload event (untested):
var all = document.getElementsByTagName('*');
for (var i = 0; i < all.length; i++) {
all[i].onclick = undefined;
all[i].onerror = undefined;
}

How can I check on which element the event was triggered?

addLBEvent : function()
{
var amount = this.imageList.length;
for(var i = 0;i < amount;i++)
{
if(this.imageList[i].addEventListener)
{
this.imageList[i].addEventListener("click",this.startLB,false);
}
/*
IE<9-part
*/
}
},
startLB : function(src)
{
}
I'd like to know which element triggered the event.
If I'd do this in the HTML-Code I'd write something like onlick="startLB(this.src)" for example. How can I do such a thing with addEventListener?
I've already tried `addEventListener("click","myobjectname.startLB(this.src)" but it didn't work.
And sorry for my bad English
An event object is passed in as the first argument to any event handler.
The event object as a target property identifying the element to which the event applies.
addLBEvent : function(event) {
console.log(event.target);
}
https://developer.mozilla.org/en/DOM/event.target
You can access a reference to the element that triggered the event...
var elementThatTriggeredEvent = e.target || e.srcElement;
...assuming that e is the reference to the event.
If you want to know which element was clicked, use event.target.
If you want to know which element you had the handler on, use event.currentTarget or, in most cases, this. addEventListener will call your handler with this set to the element on which you called addEventListener.
Note the distinction. For instance, if you had this markup:
<div id="foo"><span id="bar">Hi there</span></div>
...and this code:
document.getElementById("foo").addEventListener('click', function(event) {
alert(this.id);
alert(event.target.id)
}, false);
...then if the user clicks the text "Hi there", this will be the div but event.target will be the span.
Live example | source
See how this is the element you hooked the event on, and event.target is the element on which it fired (and then it bubbled up to the div).
Note that addEventListener isn't available on older versions of IE; you have to use attachEvent instead. attachEvent doesn't ensure that this is set to the element on which you hooked it, so beware that difference between APIs. To smooth things like that out, you might look to any decent library, like jQuery, YUI, Closure, or any of several others.
Use this:
startLB : function(src)
{
var element = src.target.tagName;
alert("element >> "+element);
}
I think it's best for you to bind it like this:
var that = this;
this.imageList[i].addEventListener("click",function() {
that.startLB(this.src);
},false);
this will become the event target, so we have to access the object somehow, I named that that
try this...
addLBEvent : function(e)
{
if (!e) e = event;
e = e.srcElement || e.target;
.......
}

Categories

Resources