Using javascript to reference current node? - javascript

So I'm doing a redesign for a site with an unhelpful CMS where I don't have full access to the markup (or ftp access). There's a node in particular which is hindering my progress significantly - its written with inline style & no class or ID and I have to change it (not remove it). Its looking like:
<div style="background-color: blue">
<div class="editablecontent">
(stuff I can edit in the CMS goes here)
</div>
</div>
I don't think getElementsByClassName is going to work here? but what sort of works is the very ugly defining an empty div with an ID then and document.getElementById.parentNode.parentNode.style.backgroundColor="white" which is obviously filthy and doesn't work in IE anyways and I need IE8 support (at least). I am not using a framework but do have access to the header.
Thanks in advance.

Assuming this content is unique in your document (e.g. there's only one of them), you can use this to find it:
function changeColor() {
var divs = document.getElementsByTagName("div");
for (var i = 0, len = divs.length; i < len; i++) {
if (divs[i].style.backgroundColor === "blue" &&
divs[i + 1] &&
divs[i + 1].parentNode === divs[i] &&
divs[i + 1].className === "editablecontent") {
divs[i].style.backgroundColor = "white";
return;
}
}
}
This does the following steps:
Get all divs in the document
Look for one with backgroundColor set to blue with an inline style setting
When you find one check to see if there is another div after it
If that next div is a child
If that next div has a className of "editablecontent"
Then, change its background color.
If you wanted any further checks based on other criteria to make sure you were finding the right object, you could add those to the logic, though these checks are all you have disclosed to us.
Working example: http://jsfiddle.net/jfriend00/NmxUn/
Another approach is to use getElementsByClassName and install a polyfill like this one to make it work in older browsers:
// Add a getElementsByClassName function if the browser doesn't have one
// Limitation: only works with one class name
// Copyright: Eike Send http://eike.se/nd
// License: MIT License
if (!document.getElementsByClassName) {
document.getElementsByClassName = function(search) {
var d = document, elements, pattern, i, results = [];
if (d.querySelectorAll) { // IE8
return d.querySelectorAll("." + search);
}
if (d.evaluate) { // IE6, IE7
pattern = ".//*[contains(concat(' ', #class, ' '), ' " + search + " ')]";
elements = d.evaluate(pattern, d, null, 0, null);
while ((i = elements.iterateNext())) {
results.push(i);
}
} else {
elements = d.getElementsByTagName("*");
pattern = new RegExp("(^|\\s)" + search + "(\\s|$)");
for (i = 0; i < elements.length; i++) {
if ( pattern.test(elements[i].className) ) {
results.push(elements[i]);
}
}
}
return results;
}
}
And, then you could simply do this, even on older versions of IE:
var items = document.getElementsByClassName("editablecontent");
if (items.length) {
items[0].parentNode.style.backgroundColor = "white";
}

Related

How to remove :hover css rule from stylesheet

I am working on a project based on jQuery and javascript. I am trying to add the CSS rules using javascript insert/addRule properties. What I want to do is :
I have to create hover effect and insert them into the stylesheet using javascript css properties. For this I am creating two classes for example:
.dummy {.... }
and
.dummy:hover{.....}
I want that If I change the hover effect then it must delete the previous one (or duplicates ) from the stylesheet and insert the new rules to the stylesheet.
But the problem is that If I try to remove or delete the CSS rules using deleteRule or removeRule it removes only
.dummy{...} rule but not the .dummy:hover{...}
Here is my code that can help you to understand what I am doing:
function createNewStyle() {
var style = document.createElement("style");
style.setAttribute('id', 'd-set-stylesheet');
style.appendChild(document.createTextNode(""));
document.head.appendChild(style);
} // this function might be not required but included here so that you may understand the program flow.
//the main issue is with this function.
function removeIfExists(class_name) {
var styleTag = document.getElementById("d-set-stylesheet");
var styleRef = styleTag.sheet ? styleTag.sheet : styleTag.styleSheet;
if (styleRef.rules) { //all browsers except IE 9-
console.log(styleRef);
for (var i in styleRef.cssRules) {
if (styleRef.cssRules[i].selectorText === "." + class_name+":hover" ||styleRef.cssRules[i].selectorText === "." + class_name )
styleRef.deleteRule(i);
}
} else {
var i;
for (i = 0; i < styleRef.rules.length; i++) {
//if(styleRef.rules[i].selectorText === "."+class_name || styleRef.rules[i].selectorText === "."+class_name+":hover")
styleRef.removeRule(i);
}
}
return styleRef;
}
//this function maybe not related with the issue but if it is you can check it.
function setNewClass(element, class_name, classSelector, styleObject) {
var stylesheet = removeIfExists(class_name);
if (element.data('hover-class') == null)
element.data('hover-class', class_name);
let count = 0;
var style = [];
var property = [{
"append": ":hover"
}, {
"append": ""
}];
for (j = 0; j < 2; j++) {
style.push({});
style[j].sheet = stylesheet;
style[j].selector = class_name;
style[j].type = classSelector;
style[j].property = property[j]["append"];
style[j].style = "";
}
for (i in styleObject) {
if (styleObject.hasOwnProperty(i))
if (count % 2 == 0)
style[0].style += styleObject[i] + ":";
else
style[0].style += styleObject[i] + ";";
count++;
}
style[1].style = "transition:" + styleObject.t_property_value;
addCSSRule(style);
}
function addCSSRule(styleSheet) {
console.log(styleSheet);
for (i = 0; i < styleSheet.length; i++)
if (styleSheet[i].sheet.insertRule)
styleSheet[i].sheet.insertRule(styleSheet[i].type + styleSheet[i].selector + styleSheet[i].property + "{" + styleSheet[i].style + "}", styleSheet[i].sheet.cssRules.length);
else
styleSheet[i].sheet.addRule(styleSheet[i].type + styleSheet[i].selector + styleSheet[i].property, styleSheet[i].sheet.style, -1);
}
Here are the images that of console.log
In the first image the hover and without hover class is added to CSS rules.
In the second image the previous hover class is not deleted while without hover class is deleted. I want hover transition must be deleted. Before adding new Rules to prevent duplication of classes and more than one transition on hovering the element should be prevented.
Thank you.
I found the answer today. I will explain it so that it can help others in future to understand the problem and solution.
As through my question, you can understand the issue. The problem was with this function only
function removeIfExists(class_name) {
......
if (styleRef.rules) { //all browsers except IE 9-
for (var i in styleRef.cssRules) {
.....
styleRef.deleteRule(i); //problem is with this.
.....
}
}
.......
return styleRef;
}
When I check the deleteRule on the basis of some test I found that when the deleteRule is called following things happens:
The css rule is deleted on the basis of index.
(The most important, as there is no documentation or clarification or explanation about this) The content get updated with their indexing. So that my function will no longer be able to delete the correct rules on the basis of old indexing.
To understand it better I will give you an example:
Suppose, I have added two rules in css such as: .dummy:hover{...} and .dummy{...}
Now the indexing of these rules will be as follows:
[0]: .dummy:hover rule
[1]: .dummy rule
When i call the delete rule what happen is :
// at i =0;
deleteRule(0);/* will be called and it will remove [0]: .dummy:hover rule
and then it will update the rules indexing again, which means
[1]: .dummy rule => [0]: .dummy rule */
// now when i = 1;
deleteRule(1); /* will be called and it will try to remove [1]: .dummy rule which is not here anymore and it will just skip the removal of [1]: .dummy rule.
So what I do to remove it is starting to remove the rule from highest indexing or in reverse order which means the i = 1 rule will be deleted first then i=0; rule so that there would not be any consequences.
and the function would changed as this:
function removeIfExists(class_name)
{
...
var styleRef = styleTag.sheet ? styleTag.sheet : styleTag.styleSheet;
var len = styleRef.cssRules ? styleRef.cssRules.length : styleRef.rules.length;
if(styleRef.rules){ //all browsers except IE 9-
for(i=len;i>0;i--)
{
if (styleRef.cssRules[i-1].selectorText === "." + class_name+":hover" ||styleRef.cssRules[i-1].selectorText === "." + class_name )
styleRef.deleteRule(i-1);
}
}
......
.....
}
return styleRef;
}
Note : The similar procedure would be followed for IE but I have not included it here.

getElementsByClassName() doesn't work in old Internet Explorers like IE6, IE7, IE8

The following code:
var borderTds = document.getElementsByClassName('leftborder');
gives me an error message in Internet Explorer 6, 7 and 8:
Object does not support this method
How can I select elements by their class in these browsers?
I prefer not to use JQuery.
IE6, Netscape 6+, Firefox, and Opera 7+ copy the following script in your page:
document.getElementsByClassName = function(cl) {
var retnode = [];
var elem = this.getElementsByTagName('*');
for (var i = 0; i < elem.length; i++) {
if((' ' + elem[i].className + ' ').indexOf(' ' + cl + ' ') > -1) retnode.push(elem[i]);
}
return retnode;
};
This solution may help. This is a custom getElementsByClassName function implemented in pure javascript, that works in IE.
Essentially what this script is doing is probing, one by one, all possible options and picks the best one available. These options are:
Native document.getElementsByClassName function.
document.evaluate function, which allows evaluation of XPath queries.
Traversing the DOM tree.
Of course the first one is the best performance-wise, however the latter should be available everywhere including IE 6.
Usage example, which is also available on the page, looks like this:
getElementsByClassName("col", "div", document.getElementById("container"));
So the function allows 3 parameters: class (required), tag name (optional, searches for all tags if not specified), root element (optional, document if not specified).
Update. The solution linked in the blog post is hosted on the Google Code which is shutting down in Jan 2016. However the author has made it available on GitHub. Kudos to flodin pointing this out in the comments.
Internet Explorer 8 and older does not support getElementsByClassName(). If you only need a solution for IE8, it supports querySelectorAll(), you can use one of these instead. For older IEs you have to provide your own implementation, and for some other ancient browsers that support it you can also use evaluate() which runs XPath expressions.
This code provides a document.getElementsByClassName method if it does not exist yet using the methods I've described:
if (!document.getElementsByClassName) {
document.getElementsByClassName = function(search) {
var d = document, elements, pattern, i, results = [];
if (d.querySelectorAll) { // IE8
return d.querySelectorAll("." + search);
}
if (d.evaluate) { // IE6, IE7
pattern = ".//*[contains(concat(' ', #class, ' '), ' " + search + " ')]";
elements = d.evaluate(pattern, d, null, 0, null);
while ((i = elements.iterateNext())) {
results.push(i);
}
} else {
elements = d.getElementsByTagName("*");
pattern = new RegExp("(^|\\s)" + search + "(\\s|$)");
for (i = 0; i < elements.length; i++) {
if ( pattern.test(elements[i].className) ) {
results.push(elements[i]);
}
}
}
return results;
}
}
If you don't like something about it, you can use your favorite search engine to find a different one.
The method doesn't exist in IE6. If you want to select elements by class and don't want to use a library, you simply have to loop through all elements in the page and check for the class in their className property.
function getElementsByClassName(className) {
var found = [];
var elements = document.getElementsByTagName("*");
for (var i = 0; i < elements.length; i++) {
var names = elements[i].className.split(' ');
for (var j = 0; j < names.length; j++) {
if (names[j] == className) found.push(elements[i]);
}
}
return found;
}
Demo: http://jsfiddle.net/kYdex/1/
If getElementsByClassname does not support is error in some old browsers
Simply try
var modal = document.getElementById('myModal');
modal.onclick= function(){
Then do what ever onclick function or another function by using getElementById
modal.style.display = "none";
}

javascript modify css class property while knowing only the class' name

Here's my css class:
.my-css-class-name
{
display: block;
}
And I have one element at my webpage that uses this class. I want to modify this element's "display" property.
I would happily do this by getting a handle to that element and then modifying what I need, BUT, I don't know the element's name - it's being randomly generated (it's a third-party extension).
So I figured I'm gonna have to get a handle to ".my-css-class-name" and modify that property directly.
How do I get there, cross-browser (major ones) solution?
Edit #1:
I'm looking for compatibility with newer browsers.
Well, theoretically, it's easy.
document.getElementsByClassName("my-css-class-name")[0].style.display = "something";
In case you need IE compatibility:
/*
Developed by Robert Nyman, http://www.robertnyman.com
Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/
var getElementsByClassName = function (className, tag/* "a","div",... */, elm/*parent*/){
if (document.getElementsByClassName) {
getElementsByClassName = function (className, tag, elm) {
elm = elm || document;
var elements = elm.getElementsByClassName(className),
nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
returnElements = [],
current;
for(var i=0, il=elements.length; i<il; i+=1){
current = elements[i];
if(!nodeName || nodeName.test(current.nodeName)) {
returnElements.push(current);
}
}
return returnElements;
};
}
else if (document.evaluate) {
getElementsByClassName = function (className, tag, elm) {
tag = tag || "*";
elm = elm || document;
var classes = className.split(" "),
classesToCheck = "",
xhtmlNamespace = "http://www.w3.org/1999/xhtml",
namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
returnElements = [],
elements,
node;
for(var j=0, jl=classes.length; j<jl; j+=1){
classesToCheck += "[contains(concat(' ', #class, ' '), ' " + classes[j] + " ')]";
}
try {
elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
}
catch (e) {
elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
}
while ((node = elements.iterateNext())) {
returnElements.push(node);
}
return returnElements;
};
}
else {
getElementsByClassName = function (className, tag, elm) {
tag = tag || "*";
elm = elm || document;
var classes = className.split(" "),
classesToCheck = [],
elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
current,
returnElements = [],
match;
for(var k=0, kl=classes.length; k<kl; k+=1){
classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
}
for(var l=0, ll=elements.length; l<ll; l+=1){
current = elements[l];
match = false;
for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
match = classesToCheck[m].test(current.className);
if (!match) {
break;
}
}
if (match) {
returnElements.push(current);
}
}
return returnElements;
};
}
return getElementsByClassName(className, tag, elm);
};
getElementsByClassName("my-css-class-name")[0].style.display = "something";
Following your response in the comment, if the element is being generated by Jquery, then the library is most likely installed. Here is something you can try to select it via Jquery and change the require property.
$(document).ready( function(){
$('.my-class-name').css('display', 'block');
});
Substituting 'block' for whatever setting you require.
If Jquery is included it should do what your require on page load. You can also attach it to other events as well.
$(document).ready(function(){
$('.my-class-name').click(classClicked);
})
function classClicked(){
$(this).css('display','block')
}
getElementByClassName is not possible (in older browsers) but there are work arounds including iterating through every element. See here for discussion Do we have getElementsByClassName in javascript?
Some newer browsers support document.getElementsByClassName right out of the box. Older browsers do not and you have to use a function that loops through the elements of the page.
A flexible getElementsByClassName function with support for browser versions that do not support the native function as thejh suggested may be what you are looking for. It would work, at least. However, for what you are doing, it may be useful to look at the document.styleSheets property. With this route, you can change the CSS rule directly, which, if it worked consistently across browsers, would be the better route here. Unfortunately, browser compatibility in this area is far from consistent, as shown here: http://www.quirksmode.org/dom/w3c_css.html
If you are still interested, have a look at this question: Changing a CSS rule-set from Javascript

How to Get Element By Class in JavaScript?

I want to replace the contents within a html element so I'm using the following function for that:
function ReplaceContentInContainer(id,content) {
var container = document.getElementById(id);
container.innerHTML = content;
}
ReplaceContentInContainer('box','This is the replacement text');
<div id='box'></div>
The above works great but the problem is I have more than one html element on a page that I want to replace the contents of. So I can't use ids but classes instead. I have been told that javascript does not support any type of inbuilt get element by class function. So how can the above code be revised to make it work with classes instead of ids?
P.S. I don't want to use jQuery for this.
This code should work in all browsers.
function replaceContentInContainer(matchClass, content) {
var elems = document.getElementsByTagName('*'), i;
for (i in elems) {
if((' ' + elems[i].className + ' ').indexOf(' ' + matchClass + ' ')
> -1) {
elems[i].innerHTML = content;
}
}
}
The way it works is by looping through all of the elements in the document, and searching their class list for matchClass. If a match is found, the contents is replaced.
jsFiddle Example, using Vanilla JS (i.e. no framework)
Of course, all modern browsers now support the following simpler way:
var elements = document.getElementsByClassName('someClass');
but be warned it doesn't work with IE8 or before. See http://caniuse.com/getelementsbyclassname
Also, not all browsers will return a pure NodeList like they're supposed to.
You're probably still better off using your favorite cross-browser library.
document.querySelectorAll(".your_class_name_here");
That will work in "modern" browsers that implement that method (IE8+).
function ReplaceContentInContainer(selector, content) {
var nodeList = document.querySelectorAll(selector);
for (var i = 0, length = nodeList.length; i < length; i++) {
nodeList[i].innerHTML = content;
}
}
ReplaceContentInContainer(".theclass", "HELLO WORLD");
If you want to provide support for older browsers, you could load a stand-alone selector engine like Sizzle (4KB mini+gzip) or Peppy (10K mini) and fall back to it if the native querySelector method is not found.
Is it overkill to load a selector engine just so you can get elements with a certain class? Probably. However, the scripts aren't all that big and you will may find the selector engine useful in many other places in your script.
A Simple and an easy way
var cusid_ele = document.getElementsByClassName('custid');
for (var i = 0; i < cusid_ele.length; ++i) {
var item = cusid_ele[i];
item.innerHTML = 'this is value';
}
I'm surprised there are no answers using Regular Expressions. This is pretty much Andrew's answer, using RegExp.test instead of String.indexOf, since it seems to perform better for multiple operations, according to jsPerf tests.
It also seems to be supported on IE6.
function replaceContentInContainer(matchClass, content) {
var re = new RegExp("(?:^|\\s)" + matchClass + "(?!\\S)"),
elems = document.getElementsByTagName('*'), i;
for (i in elems) {
if (re.test(elems[i].className)) {
elems[i].innerHTML = content;
}
}
}
replaceContentInContainer("box", "This is the replacement text.");
If you look for the same class(es) frequently, you can further improve it by storing the (precompiled) regular expressions elsewhere, and passing them directly to the function, instead of a string.
function replaceContentInContainer(reClass, content) {
var elems = document.getElementsByTagName('*'), i;
for (i in elems) {
if (reClass.test(elems[i].className)) {
elems[i].innerHTML = content;
}
}
}
var reBox = /(?:^|\s)box(?!\S)/;
replaceContentInContainer(reBox, "This is the replacement text.");
This should work in pretty much any browser...
function getByClass (className, parent) {
parent || (parent=document);
var descendants=parent.getElementsByTagName('*'), i=-1, e, result=[];
while (e=descendants[++i]) {
((' '+(e['class']||e.className)+' ').indexOf(' '+className+' ') > -1) && result.push(e);
}
return result;
}
You should be able to use it like this:
function replaceInClass (className, content) {
var nodes = getByClass(className), i=-1, node;
while (node=nodes[++i]) node.innerHTML = content;
}
var elems = document.querySelectorAll('.one');
for (var i = 0; i < elems.length; i++) {
elems[i].innerHTML = 'content';
};
I assume this was not a valid option when this was originally asked, but you can now use document.getElementsByClassName('');. For example:
var elements = document.getElementsByClassName(names); // or:
var elements = rootElement.getElementsByClassName(names);
See the MDN documentation for more.
There are 3 different ways to get elements by class in javascript. But here for your query as you have multiple elements with the same class names you can use 2 methods:
getElementsByClassName Method - It returns all the elements with the specified class present in the document or within the parent element which called it.
function ReplaceContentInContainer(className, content) {
var containers = document.getElementsByClassName(className);
for (let i = 0; i < containers.length; i++) {
containers[i].innerHTML = content;
}
}
ReplaceContentInContainer('box', 'This is the replacement text');
<div class='box'></div>
querySelectorAll Method - It select element on the basic of CSS selectors. Pass your CSS class to it with a dot and it will return all the element having specified class as an array-like object.
function ReplaceContentInContainer(className, content) {
var containers = document.querySelectorAll(`.${className}`);
for (let i = 0; i < containers.length; i++) {
containers[i].innerHTML = content;
}
}
ReplaceContentInContainer('box', 'This is the replacement text');
<div class='box'></div>
I think something like:
function ReplaceContentInContainer(klass,content) {
var elems = document.getElementsByTagName('*');
for (i in elems){
if(elems[i].getAttribute('class') == klass || elems[i].getAttribute('className') == klass){
elems[i].innerHTML = content;
}
}
}
would work
jQuery handles this easy.
let element = $(.myclass);
element.html("Some string");
It changes all the .myclass elements to that text.
When some elements lack ID, I use jQuery like this:
$(document).ready(function()
{
$('.myclass').attr('id', 'myid');
});
This might be a strange solution, but maybe someone find it useful.

How to get text from all descendents of an element, disregarding scripts?

My current project involves gathering text content from an element and all of its descendants, based on a provided selector.
For example, when supplied the selector #content and run against this HTML:
<div id="content">
<p>This is some text.</p>
<script type="text/javascript">
var test = true;
</script>
<p>This is some more text.</p>
</div>
my script would return (after a little whitespace cleanup):
This is some text. var test = true; This is some more text.
However, I need to disregard text nodes that occur within <script> elements.
This is an excerpt of my current code (technically, it matches based on one or more provided selectors):
// get text content of all matching elements
for (x = 0; x < selectors.length; x++) { // 'selectors' is an array of CSS selectors from which to gather text content
matches = Sizzle(selectors[x], document);
for (y = 0; y < matches.length; y++) {
match = matches[y];
if (match.innerText) { // IE
content += match.innerText + ' ';
} else if (match.textContent) { // other browsers
content += match.textContent + ' ';
}
}
}
It's a bit simplistic in that it just returns all text nodes within the element (and its descendants) that matches the provided selector. The solution I'm looking for would return all text nodes except for those that fall within <script> elements. It doesn't need to be especially high-performance, but I do need it to ultimately be cross-browser compatible.
I'm assuming that I'll need to somehow loop through all children of the element that matches the selector and accumulate all text nodes other than ones within <script> elements; it doesn't look like there's any way to identify JavaScript once it's already rolled into the string accumulated from all of the text nodes.
I can't use jQuery (for performance/bandwidth reasons), although you may have noticed that I do use its Sizzle selector engine, so jQuery's selector logic is available.
function getTextContentExceptScript(element) {
var text= [];
for (var i= 0, n= element.childNodes.length; i<n; i++) {
var child= element.childNodes[i];
if (child.nodeType===1 && child.tagName.toLowerCase()!=='script')
text.push(getTextContentExceptScript(child));
else if (child.nodeType===3)
text.push(child.data);
}
return text.join('');
}
Or, if you are allowed to change the DOM to remove the <script> elements (which wouldn't usually have noticeable side effects), quicker:
var scripts= element.getElementsByTagName('script');
while (scripts.length!==0)
scripts[0].parentNode.removeChild(scripts[0]);
return 'textContent' in element? element.textContent : element.innerText;
EDIT:
Well first let me say im not too familar with Sizzle on its lonesome, jsut within libraries that use it... That said..
if i had to do this i would do something like:
var selectors = new Array('#main-content', '#side-bar');
function findText(selectors) {
var rText = '';
sNodes = typeof selectors = 'array' ? $(selectors.join(',')) : $(selectors);
for(var i = 0; i < sNodes.length; i++) {
var nodes = $(':not(script)', sNodes[i]);
for(var j=0; j < nodes.length; j++) {
if(nodes[j].nodeType != 1 && node[j].childNodes.length) {
/* recursion - this would work in jQ not sure if
* Sizzle takes a node as a selector you may need
* to tweak.
*/
rText += findText(node[j]);
}
}
}
return rText;
}
I didnt test any of that but it should give you an idea. Hopefully someone else will pipe up with more direction :-)
Cant you just grab the parent node and check the nodeName in your loop... like:
if(match.parentNode.nodeName.toLowerCase() != 'script' && match.nodeName.toLowerCase() != 'script' ) {
match = matches[y];
if (match.innerText) { // IE
content += match.innerText + ' ';
} else if (match.textContent) { // other browsers
content += match.textContent + ' ';
}
}
ofcourse jquery supports the not() syntax in selectors so could you just do $(':not(script)')?

Categories

Resources