Any Way of Knowing What This Javascript Code Does? - javascript

I've been noticing some strange things going on with my websites so I have been inspecting my files when I came across this:
if( typeof document.getElementsByClassName != 'function' ) {
document.getElementsByClassName = function(classname) {
var node = document.body;
var a = [];
var re = new RegExp('(^| )'+classname+'( |$)');
var els = node.getElementsByTagName("*");
for(var i=0,j=els.length; i<j; i++)
if(re.test(els[i].className))a.push(els[i]);
return a;
}
}
Does anyone know what this might be doing?

Your code is a simple implementation of document.getElementsByClassName method of document object. It's a standart method but is not defined in older browsers(like older IE versions).
if( typeof document.getElementsByClassName != 'function' ) {
This part checks if type of the method is not a function(so not defined) and later on defines it if so.
With this method you can select DOM elements from your document using class name, like this
<div class="box"></div>
document.getElementsByClassName('box')

Related

JavaScript: Implement 'element.hasAttribute' if undefined [for IE7]

I need to make an exisitng web application compatible with IE7.
The code uses element.hasAttribute extensively and IE7 has issues with this method.
Object doesn't support property or method 'hasattribute'
I am trying to check in the code if an input element has the hasAttribute method defined and if not, I am trying to add it to all input elements.
//create an input element variable << works fine
var myInput = document.createElement("input");
//see if it has the 'hasAttribute' method << condition works fine
if (('hasAttribute' in myInput)==false)
{
//get all input elements into objInputElements <<works fine
var objInputElements=document.getElementsByTagName("input");
// MORE CODE NEEDED - To implement a hasAttribute function for all
// elements in the array probably using something
// like: !!element[attributeName] which works in IE7. See link and notes below.
}
This article describes how to define a seperate function to do this. However, I would like to add the hasattribute method to the elements if it is not defined. (this way I don't need to change all the code that is currently written)
IMPORTANT NOTE: There are > 1000 hidden input fields in the form therefore, the 'hasattribute' method needs to be added to the elements in a very efficient way.
Please let me know the how I could achieve this. Thank you!
Since Element.prototype is not supported IE < 8, there is no efficient way to polyfill hasAttribute. The inefficient way (if you wanted to avoid shimming) would be something like this (placed after all inputs had loaded):
<input data-abc="" />
<script>
if (!window.Element || !window.Element.prototype || !window.Element.prototype.hasAttribute) {
(function () {
function hasAttribute (attrName) {
return typeof this[attrName] !== 'undefined'; // You may also be able to check getAttribute() against null, though it is possible this could cause problems for any older browsers (if any) which followed the old DOM3 way of returning the empty string for an empty string (yet did not possess hasAttribute as per our checks above). See https://developer.mozilla.org/en-US/docs/Web/API/Element.getAttribute
}
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
inputs[i].hasAttribute = hasAttribute;
}
}());
}
var inputs = document.getElementsByTagName('input');
document.write(
'has?' + inputs[0].hasAttribute('abc') // false
);
document.write(
'has?' + inputs[0].hasAttribute('data-abc') // true
);
</script>
I known this is an old post and maybe nobody else use IE7 but if like me you need it (and need to use ajax or something like that) this is my propose.
Maybe we can improve the performance creating a proxy of getElementsByTagName or getElementById to do the trick, and this add support to dynamic elements that are created in runtime.
Maybe something like this:
if (!window.Element || !window.Element.prototype || !window.Element.prototype.hasAttribute) {
(function (document) {
var originalGetElementById = document.getElementById;
var originalGetElementsByTagName = document.getElementsByTagName;
// The HasAttribute function.
function hasAttribute (attrName) {
return typeof this[attrName] !== 'undefined';
}
// Add the HasAttribute to the element if is not present yet.
function attachFunction (element) {
if (element && !element.hasAttribute) {
element.hasAttribute = hasAttribute;
}
return element;
}
// Proxy of the document.getElementById
document.getElementById = function (elementId) {
var element = originalGetElementById(elementId);
return attachFunction(element);
}
// Proxy of the document.getElementsByTagName
document.originalGetElementsByTagName = function (tagName) {
var elements = originalGetElementsByTagName(tagName);
for(var i = 0, len = elements.length; i < len; i++) {
attachFunction(element[i]);
}
return elements;
}
}(document));
}
And this functionality can be in a separated javascript file included with conditional tags in IE:
<!--[if lt IE 8]>
<script src="ie7fixs.js" type="text/javascript" ></script>
<![endif]-->
And then just use the document.getElementsByTagName or document.getElementById.
var inputs = document.getElementsByTagName('input');
document.write(
'has?' + inputs[0].hasAttribute('abc') // false
);
document.write(
'has?' + inputs[0].hasAttribute('data-abc') // true
);
Try it:
//if po is object then for IE:
if (!po.hasAttribute) po.hasAttribute=function(attr) {
return this.getAttribute(attr)!=null
};

ID Ends With in pure Javascript

I am working in a Javascript library that brings in jQuery for one thing: an "ends with" selector. It looks like this:
$('[id$=foo]')
It will find the elements in which the id ends with "foo".
I am looking to do this without jQuery (straight JavaScript). How might you go about this? I'd also like it to be as efficient as reasonably possible.
Use querySelectorAll, not available in all browsers (like IE 5/6/7/8) though. It basically works like jQuery:
http://jsfiddle.net/BBaFa/2/
console.log(document.querySelectorAll("[id$=foo]"));
You will need to iterate over all elements on the page and then use string functions to test it. The only optimizations I can think of is changing the starting point - i.e. not document.body but some other element where you know your element will be a child of - or you could use document.getElementsByTagName() to get an element list if you know the tag name of the elements.
However, your task would be much easier if you could use some 3rd-party-javascript, e.g. Sizzle (4k minified, the same selector engine jQuery uses).
So, using everything that was said, I put together this code. Assuming my elements are all inputs, then the following code is probably the best I am going to get?
String.prototype.endsWith = function(suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1;
};
function getInputsThatEndWith(text) {
var result = new Array();
var inputs = document.getElementsByTagName("input");
for(var i=0; i < inputs.length; i++) {
if(inputs[i].id.endsWith(text))
result.push(inputs[i]);
}
return result;
}
I put it on JsFiddle: http://jsfiddle.net/MF29n/1/
#ThiefMaster touched on how you can do the check, but here's the actual code:
function idEndsWith(str)
{
if (document.querySelectorAll)
{
return document.querySelectorAll('[id$="'+str+'"]');
}
else
{
var all,
elements = [],
i,
len,
regex;
all = document.getElementsByTagName('*');
len = all.length;
regex = new RegExp(str+'$');
for (i = 0; i < len; i++)
{
if (regex.test(all[i].id))
{
elements.push(all[i]);
}
}
return elements;
}
}
This can be enhanced in a number of ways. It currently iterates through the entire dom, but would be more efficient if it had a context:
function idEndsWith(str, context)
{
if (!context)
{
context = document;
}
...CODE... //replace all occurrences of "document" with "context"
}
There is no validation/escaping on the str variable in this function, the assumption is that it'll only receive a string of chars.
Suggested changes to your answer:
RegExp.quote = function(str) {
return str.replace(/([.?*+^$[\]\\(){}-])/g, "\\$1");
}; // from https://stackoverflow.com/questions/494035/#494122
String.prototype.endsWith = function(suffix) {
return !!this.match(new RegExp(RegExp.quote(suffix) + '$'));
};
function getInputsThatEndWith(text) {
var results = [],
inputs = document.getElementsByTagName("input"),
numInputs = inputs.length,
input;
for(var i=0; i < numInputs; i++) {
var input = inputs[i];
if(input.id.endsWith(text)) results.push(input);
}
return results;
}
http://jsfiddle.net/mattball/yJjDV/
Implementing String.endsWith using a regex instead of indexOf() is mostly a matter of preference, but I figured it was worth including for variety. If you aren't concerned about escaping special characters in the suffix, you can remove the RegExp.quote() bit, and just use
new RegExp(suffix + '$').
If you know the type of DOM elements you are targeting,
then get a list of references to them using getElementsByTagName , and then iterate over them.
You can use this optimization to fasten the iterations:
ignore the elements not having id.
target the nearest known parent of elements you want to seek, lets say your element is inside a div with id='myContainer', then you can get a restricted subset using
document.getElementById('myContainer').getElementsByTagName('*') , and then iterate over them.

JS: How do we work with classes in CSS?

How do we manipulate the class of DOM elements with javascript? Is there a getElementsByClassName function?
Standard way is
error_message.className = 'error-message';
But you'll find these functions can simplify things a lot:
function hasClass(ele,cls) {
return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
//chekcs if selected element has class "cls", works for elements with multiple classes
function addClass(ele,cls) {
if (!this.hasClass(ele,cls)) ele.className += " "+cls;
}
//adds new class to element
function removeClass(ele,cls) {
if (hasClass(ele,cls)) {
var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
ele.className=ele.className.replace(reg,' ');
}
}
//removes class from element
Usage in a stackoverflow greasemonkey script to show all questions on page, regardless if they're ignored or not:
var childNodes=document.getElementById("questions").childNodes; //array of all questions
for (var i=1; i<childNodes.length; i+=2) //iterates through all questions on page.
{
removeClass(childNodes[i],"tagged-ignored-hidden");
addClass(childNodes[i],"user_defined_class");
}
(Don't worry if the for loop looks weird in that it skips every other element; the specifics of Stackoverflow's DOM layout with extra nodes between questions aren't important here.)
As to document.getElementsByClassName, it returns an array of DOM elements with the specific class (as you would suspect). BUT:
Safari 3.1 has native
getElmentsByClassName support, and
upcoming Firefox 3 and Opera 9.5 will
have it too. It only leaves out,
you’ve guessed it, Internet Explorer.
source
You can change a class in plain-old JavaScript using something like:
document.getElementById('myElement').className = 'myClass';
Or, if you're using JQuery, you can just use the "Class" functions.
Addressing the added details to the question about 'getElementsByClassName' and your comment:
It would probably be safest (and easiest) to use your favourite JavaScript library for this.
JQuery example:
$(".myClassName").each(function() {
//do what you want with the current element $(this)
});
Hope that helps.
Many JavaScript implementations do have a getElementsByClassName method built in. But if they don’t, you can implement it for yourself:
if (typeof Element.prototype.getElementsByClassName == "undefined") {
Element.prototype.getElementsByClassName = function(className) {
var elems = document.getElementsByTagName("*"),
matches = [];
for (var i=0, n=elems.length; i<n; ++i) {
if (elems[i].hasAttribute("class")) {
var classNames = elems[i].getAttribute("class").split(/\s+/);
for (var j=0,m=classNames.length; j<m; ++j) {
if (classNames[j] == className) {
matches.push(elems[i]);
break;
}
}
}
}
return new NodeList(matches);
};
}

Search attribute in JavaScript

Without using any open source framework (jQuery, etc.) :), in JavaScript, what's the most efficient way to search for attributes in any controls. (Any old/new browsers)
This is the pattern I'm kind of following. Is there any better way or any better getElementByAttribute() method? Thanks!
e.g
<input type="button" id="b1" value="Continue" a1="something" />
<input type="text" id="t1" a1="something1" />
<script>
var attrToFind = "something;something1";
var elems = document.all ? document.all : document.getElementByTagName("*");
//assume elems work always
for(var i = 0 ; i < elems.length; i++)
{
var att = elems[i].getAttribute("a1");
if (typeof att == "string")
{
if (att.indexOf(attrToFind) > -1)
... //search which attr you find, create array, save value, etc.
}
}
</script>
There is. Given that browser supports other means to collect elements, such as document.querySelectorAll (css expression) or document.evaluate (xpath expression), these "specialized" methods are usually more efficient.
document.querySelectorAll('*[foo="bar"]');
document.evaluate("//*[#foo='bar']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
Accessing an HTMLCollection (returned by getElement[s]By* functions) is slow in comparison to accessing arrays because an HTMLCollection must match the document at all times (it is live).
For this reason, it's best to create an array from the HTMLCollection and iterate over that.
This is a bit more optimized for speed:
var attrToFind = "something;something1",
elems = document.all ? document.all : document.getElementByTagName('*'),
i, attr;
// Works in Firefox; not sure about other browsers and engines.
elems = Array.prototype.slice.call(elems);
i = elems.length;
while(i --> 0) {
attr = elems[i].getAttribute('a1');
// Are you sure you want indexOf?
// att === attrToFind may be faster, as it requires one less comparison.
if(typeof att !== 'string' || att.indexOf(attrToFind) < 0) {
continue;
}
// Do stuff.
}
This is maybe the solution you'll need:
function $$$$(obj) {
var attrToFind = obj;
var elements = document.getElementsByTagName('*');
var attrResults = [];
var x = 0;
while (x < elements.length) {
var attr = elements[x].getAttribute('a1');
if (attr !== null) {
if (attr.indexOf(attrToFind) > -1) {
attrResults.push(elements[x]);
}
}
x++
}
return attrResults;
}
Run function:
$$$$('something');
Result is an Array with all elements with the class 'something'.
Maybe somebody can refactor my code, so that is working also with more than 1 parameter.
I hope I could help you.

Finding DOM node index

I want find the index of a given DOM node. It's like the inverse of doing
document.getElementById('id_of_element').childNodes[K]
I want to instead extract the value of K given that I already have the reference to the child node and the parent node. How do I do this?
The shortest possible way, without any frameworks, in all versions of Safari, FireFox, Chrome and IE >= 9:
var i = Array.prototype.indexOf.call(e.childNodes, someChildEl);
A little shorter, expects the element to be in elem, returns k.
for (var k=0,e=elem; e = e.previousSibling; ++k);
After a comment from Justin Dearing I reviewed my answer and added the following:
Or if you prefer "while":
var k=0, e=elem;
while (e = e.previousSibling) { ++k;}
The original question was how to find the index of an existing DOM element. Both of my examples above in this answer expects elem to be an DOM element and that the element still exists in the DOM. They will fail if you give them an null object or an object that don't have previousSibling. A more fool-proof way would be something like this:
var k=-1, e=elem;
while (e) {
if ( "previousSibling" in e ) {
e = e.previousSibling;
k = k + 1;
} else {
k= -1;
break;
}
}
If e is null or if previousSibling is missing in one of the objects, k is -1.
RoBorg's answer works... or you could try...
var k = 0;
while(elem.previousSibling){
k++;
elem = elem.previousSibling;
}
alert('I am at index: ' + k);
A modern native approach might include Array.from(e.children).indexOf(theChild)
No IE support, but Edge works: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
As with the original poster, I was trying to
find the index of a given DOM node
but one that I had just use a click handler on, and only in relation to its siblings. I couldn't end up getting the above to work (because of noobness undoubtably, i tried subbing in 'this' for elem but it didn't work).
My solution was to use jquery and use:
var index = $(this).parent().children().index(this);
It works without having to specify the type of the element ie:'h1' or an id etc.
I think the only way to do this is to loop through the parent's children until you find yourself.
var K = -1;
for (var i = myNode.parent.childNodes.length; i >= 0; i--)
{
if (myNode.parent.childNodes[i] === myNode)
{
K = i;
break;
}
}
if (K == -1)
alert('Not found?!');
using a framework like prototype you could use this :
$(el).up().childElements().indexOf($(el))

Categories