Javascript find child class - javascript

I have a table. When the item is dropped I need to apply padding to a single table cell. I have flagged that cell with a class. How do I select it?
droppedRow contains the table row that is has just been dropped.
If it was an id I would do droppedRow.getElementById('..'); Is there something similar for class names. Needs to support >= IE7
Thanks

Using vanilla JavaScript, you'll probably need to load up all of the element's by tag name and then locate it by evaluating each element's classname.
For example (the styles are just for example)...
var tableCells = document.getElementsByTagName('td');
for(var i = 0, l = tableCells.length; i < l; i++) {
if(tableCells[i].className === 'droppedRow') {
tableCells[i].style.padding = '1em';
}
}
If, on the other hand, you're using jQuery, then you should be able to use:
$('.droppedRow').css('padding', '1em');
Note however that in both of these examples, all cells that have the droppedRow class name will receive this styling (rather than just a single element).
If you're not using a library, I'd say stick with the vanilla variant of this functionality - libraries would be too much overhead just to condense this to a single line.
Maxym's answer also provides a solid implementation of getElementsByClassName for older browsers.

There exists getElementsByClassName but it is not supported in IE. Here is what you can do:
var element;
// for modern browsers
if(document.querySelector) {
element = droppedRow.querySelector('.yourClass');
}
else if(document.getElementsByClassName) { // for all others
element = droppedRow.getElementsByClassName('yourClass')[0];
}
else { // for IE7 and below
var tds = droppedRow.getElementsByTagName('td');
for(var i = tds.length; i--; ) {
if((" " + tds[i].className + " ").indexOf(" yourClass ") > -1) {
element = tds[i];
break;
}
}
}
Reference: querySelector, getElementsByClassName, getElementsByTagName

Clientside getElementsByClassName cross-browser implementation:
var getElementsByClassName = function(className, root, tagName) {
root = root || document.body;
if (Swell.Core.isString(root)) {
root = this.get(root);
}
// for native implementations
if (document.getElementsByClassName) {
return root.getElementsByClassName(className);
}
// at least try with querySelector (IE8 standards mode)
// about 5x quicker than below
if (root.querySelectorAll) {
tagName = tagName || '';
return root.querySelectorAll(tagName + '.' + className);
}
// and for others... IE7-, IE8 (quirks mode), Firefox 2-, Safari 3.1-, Opera 9-
var tagName = tagName || '*', _tags = root.getElementsByTagName(tagName), _nodeList = [];
for (var i = 0, _tag; _tag = _tags[i++];) {
if (hasClass(_tag, className)) {
_nodeList.push(_tag);
}
}
return _nodeList;
}
Some browsers support it natively (like FireFox), for other you need provide your own implementation to use; that function could help you; its performance should be good enough cause it relies on native functions, and only if there is no native implementation it will take all tags, iterate and select needed...
UPDATE: script relies on hasClass function, which can be implemented this way:
function hasClass(_tag,_clsName) {
return _tag.className.match(new RegExp('(\\s|^)'+ _clsName +'(\\s|$)'));
}

It sounds like your project is in need of some JQuery goodness or some Dojo if you need a more robust and full-fledged javascript framework. JQuery will easily allow you to run the scenario you have described using its selector engine.

If you are using a library, why not use:
JQuery - $("#droppedRow > .paddedCell")
Thats the dropped row by ID and the cell by class
Prototype - $$("#droppedRow > .paddedCell")

Related

Why can't jQuery directly manipulate the class of an SVG element?

I blame my own stupidity, but I can't for the life of me understand why jQuery (2.1.4) can't directly change the class of an SVG element (or child elements) using it's class functions? I was messing around with trying to make an SVG manipulation plugin for jQuery, and I was testing all kinds of things to make the class changeable (yes, I've tried the common SVG libraries; no, I don't care for them). I ended up settling on my current version which intelligently overrides the original jQuery.addClass() function. I check if the jQuery element's array contains an SVG type node and if it does I use my custom function, otherwise, I pass it back to the original jQuery function. This seems to work so far. That being said, my "custom" function is the exact same code as the jQuery function because I copied it from the source on GitHub. So if it works with my "custom" function, why doesn't it simply work with the default jQuery function?
Here's the code I have so far. Syntax wise my code is different to match my style, but it effectively does the exact same thing as the original jQuery code.
(function ($) {
var element,
jQueryFunctions = {
addClass: $.fn.addClass
};
var addClass = function (
elements,
value) {
var proceed = typeof (value) === "string" && value;
if (!proceed) {
return this;
}
for (var i = 0, j, l = elements.length, element, klasses = (value || "").match(/\S+/g) || [], klass, currentValue, current, finalValue; element = elements[i], i < l; i++) {
currentValue = element.getAttribute && element.getAttribute("class") || "",
current = element.nodeType === 1 && (" " + currentValue + " ").replace(/[\t\r\n\f]/g, " ");
if (!current) {
return;
}
j = 0;
while (klass = klasses[j++]) {
if (current.indexOf(" " + klass + " ") < 0) {
current += klass + " ";
}
finalValue = jQuery.trim(current);
if (currentValue !== finalValue) {
element.setAttribute("class", finalValue);
}
}
}
};
var hasSvgNodes = function (
elements) {
var returnValue = true;
for (var i = 0, l = elements.length, element; element = elements[i], i < l; i++) {
returnValue = returnValue && (element.nodeName === "svg");
}
return returnValue;
};
$.fn.svg = function () {
return element = this;
};
$.fn.addClass = function (
value) {
if (!hasSvgNodes(this)) {
jQueryFunctions.addClass.apply(this, arguments);
} else {
addClass(this, value);
}
return this;
};
$.fn.svg.test = function () {
element.addClass("Red");
};
}(jQuery));
#Alex is correct. It doesn't work (demo). It used to once upon a time (eg. in the 1.1 branches) but it no longer does.
The reason is basically because the type of the className property is different for SVG elements and HTML elements. In HTML elements, it's a string. In SVG elements it's an SVGAnimatedString.
The addClass() code expects it to be a string.
var htmlelem = document.getElementById("htmlelem"),
svgelem = document.getElementById("svgelem");
alert("HTML = "+(typeof htmlelem.className) + ". SVG = "+(typeof svgelem.className));
<svg>
<rect id="svgelem" width="300" height="150"/>
</svg>
<div id="htmlelem"></div>
The reason your own version is working is because it looks like you have taken code from an older branch of jQuery. It is definitely not the 2.1.4 version of addClass.
Note, workaround ; svg , jsfiddle forked from #PaulLeBeau 's Answer
Try utilizing HTMLElement.dataset to apply css
$().ready(function() {
$("#test")[0].dataset.class = "red";
});
[data-class="red"] {
fill: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<svg>
<rect id="test" width="300" height="150" />
</svg>
#Paul's answer made me wonder a bit because the code I took was from the current source on GitHub. I double checked just in case I did pull from an older version, and sure enough it was the latest. So, I decided to double check the release notes for 3.0.0 a1, and I found my answer. Apparently the jQuery team decided to ever so slightly change their stance on the Won't Fix policy and allow direct manipulation of SVG elements' class attributes. That is the code that's currently up there now and what I was copying.
Here's the discussion thread and commit for reference. So, jQuery does do class manipulation by default, it's just in the next version. For what I'm working on, I don't mind going to the alpha so it works out for me.
I'll still be making my own plug in for SVG specific tasks which are specific to my needs, but with the class issue resolved, it should be much easier now.

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.

JavaScript getAttribute not working

var objects = document.getElementsByTagName('object');
for (var i=0, n=objects.length;i<n;i++) {
objects[i].style.display='none';
var swfurl;
var j=0;
while (objects[i].childNodes[j]) {
if (objects[i].childNodes[j].getAttribute('name') == 'movie') {
/* DO SOMETHING */
}
j++;
}
var newelem = document.createElement('div');
newelem.id = '678297901246983476'+i;
objects[i].parentNode.insertBefore(newelem, objects[i]);
new Gordon.Movie(swfurl, {id: '678297901246983476'+i, width: 500, height: 400});
}
It says that getAttribute is not a function of childNodes[j]. What's wrong? I don't see the point.
Remember that childNodes includes text nodes (and comment nodes, if any, and processing instructions if any, etc.). Be sure to check the nodeType before trying to use methods that only Elements have.
Update: Here in 2018, you could use children instead, which only includes Element children. It's supported by all modern browsers, and by IE8-IE11. (There are some quirks in older IE, see the link for a polyfill to smooth them over.)
Check the nodeType property is 1 (meaning the node is an element) before calling element-specific methods such as getAttribute(). Also, forget getAttribute() and setAttribute(): you almost never need them, they're broken in IE and they don't do what you might think. Use equivalent DOM properties instead. In this case:
var child = objects[i].childNodes[j];
if (child.nodeType == 1 && child.name == 'movie') {
/* DO SOMETHING */
}
What browser are you using ? If it's IE then you need to use readAttribute instead.

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);
};
}

Categories