I am trying to convert this code below to vanilla javascript because we cannot use jQuery
if (isSafari) {
$('.buttonClassName').click(function() {
// do something
});
}
I was trying something like this, but it doesn't work:
if (isSafari) {
document.getElementByClassName("buttonClassName").onclick = function () {
// do something
};
}
What javascript can I use without jQuery
var elements = document.getElementsByClassName('buttonClassName');
for(var i = 0; i < elements.length; i++) {
var element = elements[i];
element.onclick = function() {}
}
I find it useful to build an array of my query selection so I can use Array prototype functions on the element collection. This selector function will be useful if you can't use jQuery:
function elem (selector) {
return Array.apply(this, document.querySelectorAll(selector));
}
elem('.buttonClassName').forEach(function (el) {
el.onclick = fn;
});
use getElementsByClassName
document.getElementsByClassName('buttonClassName')[0].onclick = myFunc;
See demo: http://jsbin.com/nihovadoto/edit?html,js,output
Everything looks close . I suggest caching your button(s) and trying an event listener like below
Example
var buttons = document.getElementsByClassName('buttonClassName');
buttons.addEventListener('click' , doSomething);
Hope this helps
Related
I am looking for a way to get all the attributes of an element that begins with "on" using jQuery or Vanilla JS. I am currently getting all attributes and then looping through them to get the ones I want using the method proposed by #primvdb on this post: Get all attributes of an element using jQuery.
My code looks like this:
/* Expanding .attr as proposed by #primvdb */
(function(old) {
$.fn.attr = function() {
if(arguments.length === 0) {
if(this.length === 0) {
return null;
}
var obj = {};
$.each(this[0].attributes, function() {
if(this.specified) {
obj[this.name] = this.value;
}
});
return obj;
}
return old.apply(this, arguments);
};
})($.fn.attr);
/* And then my function */
$.fn.attrThatBeginWith = function(begins){
var attributes = this.attr();
var attrThatBegin = {};
for(var attr in attributes){
if(attr.indexOf(begins)==0){
attrThatBegin[attr] = attributes[attr];
}
}
return attrThatBegin;
};
/* Usage */
var onAttributes = $("#MyElement").attrThatBeginWith("on");
And this works but is very "dirty". It's seems like with all the vast features of jQuery there should be a better "cleaner" way to do this. Does anybody have any suggestions?
You can get all attributes attached to an element with element.attributes.
The native attributes object can be converted to an array and then filtered based on the given string.
A plugin that does the above would look like
$.fn.attrThatBeginWith = function(begins){
return [].slice.call(this.get(0).attributes).filter(function(attr) {
return attr && attr.name && attr.name.indexOf(begins) === 0
});
};
FIDDLE
CI have this code:
for(var i = 0; i < toObserve.length; i++) {
var elems = toObserve[i].split('###');
var elementToObserve = elems[0];
var imageToUse = elems[1];
$(elementToObserve).observe('click', respondToClick);
}
function respondToClick(event) {
var element = event.element();
}
In the respondToClick function I need a different image (imageToUse) for each elementToObserve. How can I do that? Can I pass a param or something?
Thanks!
Addition: I tried what Diodeus suggested, but it seems that only the last passed parameter is used, when any of the elements I observe is clicked. Whats wrong or is the way I want to do it not the right one?
Use an anonymous function:
$(elementToObserve).observe('click', function(event) {
var yourVar = "moo";
respondToClick(event,yourVar)
});
Use the specialised bindAsEventListener.
$(elementToObserve).observe('click',
respondToClick.bindAsEventListener(imageToUse)
);
The bound arguments will then be passed after the event parameter.
function respondToClick(event, imageToUse)
{
var element = event.element();
element.src = imageToUse;
}
I got this to work by simply assigning my variable to the element object:
elementToObserve.imageToUse = imageToUse;
Then in the observer function:
function respondToClick(event) {
var imageToUse = event.target.imageToUse;
}
I haven't realized any drawbacks to this.
I am working with a decent sized set of data relating to objects on the page and some objects need links applied to them onclick. The link to connect to is part of the dataset and I build a string for the link with the variable linkTarget and apply it like so.
if (dataTag[i][3]==true){
if(prepend==undefined || prepend=="undefined"){
var linkTarget=ResultsJSON["targetUrl"];
ele.onclick = function(){
window.open(linkTarget);
};
} else {
var linkTarget=prepend+ResultsJSON["targetUrl"];
ele.onclick = function(){
window.open(linkTarget);
};
}
ele refers to an element picked up with getElementByID. Now I am going through quite a few objects and the problem I have is the onclick for every object is the last value of linkTarget. This is all contained in a function and link target is a local variable so I have no idea why. I have tried using an array with something like
ele.onclick=function(){window.open(linkTarget[linkTarget.length-1]);};
and even
ele.onclick=function(){window.open(linkTarget.valueOf());};
with the same results. I am at a loss now and would appreciate any help.
Use Array.forEach() to iterate your data and watch your troubles melt away.
dataTag.forEach(function (item) {
if (item[3]==true) {
var linkTarget = "";
if (prepend==undefined || prepend=="undefined") {
linkTarget = prepend;
}
linkTarget += ResultsJSON.targetUrl;
ele.onclick = function () {
window.open(linkTarget);
};
}
});
See this compatibility note for using Array.forEach() in older browsers.
You're in a loop — therefore, you need to put your things-to-be-executed in another function, like so:
if(dataTag[i][3]) {
if(prepend) {
(function(linkTarget) {
ele.onclick = function() {
window.open(linkTarget);
};
})(ResultsJSON.targetUrl);
} else {
(function(linkTarget) {
ele.onclick = function() {
window.open(linkTarget);
};
})(ResultsJSON.targetUrl);
}
I also made some general corrections.
for example:
<li class="list" id="first-list"></li>
var li = document.getElementById("first-list");
matchSelector(li, "li.list"); // this should return true
now my solution could be described as:
function matchSelector(element, selector){
var all_matched_elements = $(selector);
return element in all_matched_elements
}
but obviously this includes some unnecessary work.
is there a better solution?
You can use jQuery is method.
Something like:
var li = document.getElementById("first-list");
matchSelector(li, "li.list");
function matchSelector(element, selector){
return $(element).is(selector);
}
Working example: http://jsfiddle.net/NqwxQ/
Use jQuery's .is() to determine if an element can be matched by the specified selector. This function returns a boolean value.
For instance:
var $li = $("li");
$li.each(function() {
if ($(this).is(".list")) {
// Take action
}
});
In trying to make my Javascript unobtrusive, I'm using onLoads to add functionality to <input>s and such. With Dojo, this looks something like:
var coolInput = dojo.byId('cool_input');
if(coolInput) {
dojo.addOnLoad(function() {
coolInput.onkeyup = function() { ... };
});
}
Or, approximately equivalently:
dojo.addOnLoad(function() {
dojo.forEach(dojo.query('#cool_input'), function(elt) {
elt.onkeyup = function() { ... };
});
});
Has anyone written an implementation of Ruby's andand so that I could do the following?
dojo.addOnLoad(function() {
// the input's onkeyup is set iff the input exists
dojo.byId('cool_input').andand().onkeyup = function() { ... };
});
or
dojo.byId('cool_input').andand(function(elt) {
// this function gets called with elt = the input iff it exists
dojo.addOnLoad(function() {
elt.onkeyup = function() { ... };
});
});
I don't know Dojo, but shouldn't your first example read
dojo.addOnLoad(function() {
var coolInput = dojo.byId('cool_input');
if(coolInput)
coolInput.onkeyup = function() { ... };
});
Otherwise, you might end up trying to access the element before the DOM has been built.
Back to your question: In JavaScript, I'd implement andand() as
function andand(obj, func, args) {
return obj && func.apply(obj, args || []);
}
Your example could then be written as
dojo.addOnLoad(function() {
andand(dojo.byId('cool_input'), function() {
this.onkeyup = function() { ... };
});
});
which isn't really that much shorter than using the explicit if statement - so why bother?
The exact syntax you want is not possible in JavaScript. The way JavaScript executes would need to change in a pretty fundamental fashion. For example:
var name = getUserById(id).andand().name;
// ^
// |-------------------------------
// if getUserById returns null, execution MUST stop here |
// otherwise, you'll get a "null is not an object" exception
However, JavaScript doesn't work that way. It simply doesn't.
The following line performs almost exactly what you want.
var name = (var user = getUserById(id)) ? user.name : null;
But readability won't scale to larger examples. For example:
// this is what you want to see
var initial = getUserById(id).andand().name.andand()[0];
// this is the best that JavaScript can do
var initial = (var name = (var user = getUserById(id)) ? user.name : null) ? name[0] : null;
And there is the side-effect of those unnecessary variables. I use those variables to avoid the double lookup. The variables are mucking up the context, and if that's a huge deal, you can use anonymous functions:
var name = (function() {return (var user = getUserById(id)) ? user.name : null;})();
Now, the user variable is cleaned-up properly, and everybody's happy. But wow! what a lot of typing! :)
You want dojo.behavior.
dojo.behavior.add({
'#cool_input': {
onKeyUp: function(evt) { ... }
}
});
How about something like this:
function andand(elt, f) {
if (elt)
return f(elt);
return null;
}
Call like this:
andand(dojo.byId('cool_input'), function(elt) {
// this function gets called with elt = the input iff it exists
dojo.addOnLoad(function() {
elt.onkeyup = function() { ... };
});
});
As far as I know there isn't a built-in JavaScript function that has that same functionality. I think the best solution though is to query by class instead of id and use dojo.forEach(...) as you will be guaranteed a non-null element in the forEach closure.
You could always use the JavaScript equivalent:
dojo.byId('cool_input') && dojo.byId('cool_input').whateverYouWantToDo(...);
I've never used dojo, but most javascript frameworks (when dealing with the DOM) return the calling element when a method is called from the element object (poor wording, sorry). So andand() would be implicit.
dojo.addOnLoad(function() {
dojo.byId('cool_input').onkeyup(function(evt) { /*event handler code*/
});
});
For a list:
Array.prototype.andand = function(property, fn) {
if (this.filter(property).length > 0) this.map(fn);
}