I have created a view in Drupal. I'm using JavaScript to modify the CSS in each row. The script runs on the first row, but does not make the changes on the rest of rows from the view.
This is the script:
<script language="javascript" type="text/javascript">
window.onload = floatbr;
function floatbr() {
var f = document.getElementById('firstright') // Get div element
var s = document.getElementById('secondright') // Get div element
var w = document.getElementById('catwrapper') // Get div element
var sh = s.offsetHeight // secondright div height
var wh = w.offsetHeight // catwrapper div height
f.style.height = wh - sh + 'px'; }
</script>
I'm using it from this page: http://agsone.100webcustomers.com/floatbottom.php
having the script in the page once does not do the trick.
having the script in the view footer and repeating the script does not work.
The link to the jSfiddle with HTML, CSS and JavaScript is the following one: http://jsfiddle.net/YTN3K/.
Drupal provides and already uses jQuery, so you should use it too. Drupal has its own way to manage JavaScript and comes with some additional JavaScript API, mainly to handle passing variables from PHP to JavaScript properly, register script to run on page load and content addition, etc.
jQuery is well documented and popular, so finding documentation, tutorial and howto is easy. Its own documentation page is a good start. But it requires basic understanding of what an XHTML document is and how it is structured.
It's hard to tell from your question and the markup you've linked to exactly what you're trying to do, so here's some general information to get you going:
The function you're currently using, getElementById, returns a single element: The one on the page with that id value. (id values must be unique on the page.)
To deal with multiple elements, you have several options. Two of the most popular:
You can start with a given element and then use its childNodes, firstChild, nextSibling, and similar properties to navigate from it to other elements nearby.
You can use getElementsByTagName (on the document or on an element) to find all elements within that container (including ones several levels down) that have a given tag. For instance, document.getElementsByTagName("p") gives you a NodeList of all paragraphs on the page.
These are properties and methods of the "DOM" (the Document Object Model), which is the tree of elements and associated information the browser creates when parsing and rendering your HTML.
References:
DOM2 Core
DOM2 HTML bindings
DOM3 Core
HTML5 Specification's DOM info
Here's an example showing some very basic operations (live copy):
HTML:
<div id="justOneDiv">I'm the <code>justOneDiv</code> element. I'm unique on the page. JavaScript code on the page turned me red.</div>
<div id="container">I'm a container called "container" with <span>various</span> <code>span</code> elements. <span>Code</span> on the <span>page</span> has made all of the <code>span</code> elements in this container <span>bold</span>.</div>
<div>I'm a container with <span>various</span> <code>span</code> elements. <span>Note</span> that the <code>span</code> elements are <span>not</span> bold, because I'm <span>not</span> in the container above.</div>
<div>I'm a <code>div</code> with no class.</div>
<div class="foo">I'm a <code>div</code> with class "foo". Code on the page turned me blue.</div>
<div class="bar">I'm a <code>div</code> with class "bar". Code on the page turned me green.</div>
<div>Another classless <code>div</code></div>
<div class="foo other">Another "foo", also with class "other"</div>
<div class="bar">Another "bar"</div>
<div>Another classless <code>div</code></div>
<div class="foo">Another "foo"</div>
<div class="bar test">Another "bar", also with class "test"</div>
<div>Another classless <code>div</code></div>
<div class="foo">Another "foo"</div>
<div class="bar">Another "bar"</div>
<div>Another classless <code>div</code></div>
<div class="foo">Another "foo"</div>
<div class="bar">Another "bar"</div>
JavaScript:
(function() {
hookEvent(window, "load", go);
function go() {
var list, index, div, container;
// Get just the one element, turn it red
document.getElementById("justOneDiv").style.color = "red";
// Get the spans within the specific container
container = document.getElementById("container");
list = container.getElementsByTagName("span");
// Loop through making those spans bold
for (index = 0; index < list.length; ++index) {
list.item(index).style.fontWeight = "bold";
}
// Get a NodeList of all divs on the page
list = document.getElementsByTagName("div");
// Loop it, turning "foo"s blue and "bar"s green
for (index = 0; index < list.length; ++index) {
div = list.item(index);
if (/\bfoo\b/.test(div.className)) {
div.style.color = "blue";
}
else if (/\bbar\b/.test(div.className)) {
div.style.color = "green";
}
}
}
function hookEvent(element, eventName, handler) {
// Very quick-and-dirty, recommend using a proper library,
// this is just for the purposes of the example.
if (typeof element.addEventListener !== "undefined") {
element.addEventListener(eventName, handler, false);
}
else if (typeof element.attachEvent !== "undefined") {
element.attachEvent("on" + eventName, handler);
}
else {
element["on" + eventName] = handler;
}
}
})();
Side note: The operations above can be made dramatically simpler by leveraging the utility functionality provided by any decent JavaScript library like jQuery, Prototype, YUI, Closure, or any of several others.
For example, using that same HTML, here's the JavaScript code using jQuery for the same result (live copy):
jQuery(function($) {
// Get just the one element, turn it red
$("#justOneDiv").css("color", "red");
// Get the spans within the specific container
// Loop through making those spans bold
$("#container span").css("font-weight", "bold");
// Turn all divs with the class "foo" blue
$("div.foo").css("color", "blue");
// Turn all divs with the class "bar" green
$("div.bar").css("color", "green");
});
The DOM is the official API; libraries like jQuery provide alternate or enhanced APIs. They're very useful and powerful, but I would recommend having some understanding of the DOM itself, even if you use a library and end up rarely writing code directly to the DOM API.
Related
This question already has answers here:
Advantages of createElement over innerHTML?
(5 answers)
Closed 6 years ago.
I'm making divs with several sub elements something like this:
<div class="item" data-id="28">
<div class="action-btns">
<button class="add"></button>
<button class="rmv"></button>
</div>
<div class="info">
<h3>Title here</h3>
<span>caption here</span>
</div>
</div>
And im giving functions to those two buttons on click. I'm wondering which method of creating these items is better - createElement or innerHTML?
As far as createElement goes i like it because i can bind the onclick while creating the button inside the element and then append it. Also im wondering if appending this "item" to the parent div is faster / better than updating innerHTML += "something";
As far as innerHTML goes there are fewer lines of code to write, but also i have to either write the onclick="myFunction()" inside the buttons instead of adding it dynamically.
Please no jQuery or anyting but pure Js. Thanks for your time :)
https://jsperf.com/innerhtml-vs-createelement-test (Note i did not write this)
Results in chrome is about 60% ish percent slower using createElement. As per (#Sagar V and #Hajji Tarik) answer consider more then just speed.
There are several advantages to using createElement instead of modifying innerHTML (as opposed to just throwing away what's already there and replacing it) besides safety, like Pekka already mentioned:
Preserves existing references to DOM elements when appending elements
When you append to (or otherwise modify) innerHTML, all the DOM nodes inside that element have to be re-parsed and recreated. If you saved any references to nodes, they will be essentially useless, because they aren't the ones that show up anymore.
Preserves event handlers attached to any DOM elements
This is really just a special case (although common) of the last one. Setting innerHTML will not automatically reattach event handlers to the new elements it creates, so you would have to keep track of them yourself and add them manually. Event delegation can eliminate this problem in some cases.
Could be simpler/faster in some cases
If you are doing lots of additions, you definitely don't want to keep resetting innerHTML because, although faster for simple changes, repeatedly re-parsing and creating elements would be slower. The way to get around that is to build up the HTML in a string and set innerHTML once when you are done. Depending on the situation, the string manipulation could be slower than just creating elements and appending them.
Additionally, the string manipulation code may be more complicated (especially if you want it to be safe).
Here's a function I use sometimes that make it more convenient to use createElement.
function isArray(a) {
return Object.prototype.toString.call(a) === "[object Array]";
}
function make(desc) {
if (!isArray(desc)) {
return make.call(this, Array.prototype.slice.call(arguments));
}
var name = desc[0];
var attributes = desc[1];
var el = document.createElement(name);
var start = 1;
if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) {
for (var attr in attributes) {
el[attr] = attributes[attr];
}
start = 2;
}
for (var i = start; i < desc.length; i++) {
if (isArray(desc[i])) {
el.appendChild(make(desc[i]));
}
else {
el.appendChild(document.createTextNode(desc[i]));
}
}
return el;
}
If you call it like this:
make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);
you get the equivalent of this HTML:
<p>Here is a link.</p>
The answer of #Matthew Crumley
innerHTML just put the plain text. Whereas createElement creates the element object and adds to Parent.
since browser convert tags in plain text to HTML element, it is converted to tags. It is not recommended.
createElement is the recommended method
In terms of performance, is it better to have 20 elements (with their children) in the DOM tree or everything stored in a Javascript object?
Case 1:
<html>
<body>
<div id='el1'>
<span>...</span>
...
</div>
<div id='el2'>
<p>...</p>
...
</div>
<div id='el3'>
<img src="" alt="" />
<br/>
</div>
<div id='el4'>
<section></section>
...
</div>
...
</body>
</html>
Case 2:
var OBJ = {};
OBJ.el1 = $("<div id='el1'><span>...</span>...</div>");
OBJ.el2 = $("<div id='el1'><p>...</p>...</div>");
OBJ.el3 = $("<div id='el1'><img src="" alt="" /><br/>...</div>");
OBJ.el4 = $("<div id='el1'><section></section>...</div>");
....
My application should only show one of these elements at once. Will it perform better if I just leave them in DOM tree and change their style, or is it preferably to store them in a Javascript object and remove and append them when needed?
How could I test it?
#edit
Extra info to consider:
1- all elements were written in the HTML document and then removed and stored in javascript objects when page was loaded.
2- Everytime the active element changes, I have change the DOM structure by removing the current active element and then appending the new one to it.
#EDIT 2: after some tests
The idea is: someone will write the HTML elements to DOM and then Javascript must show/hide only one of these elements at once. These elements have their own children. It may be a Full Page website or a Web App.
I created 2 test cases:
1- Elements will be removed and then re-appended several times by Javascript. So they will be stored in a Javascript object until it's summoned
2- Elements will be in DOM, I'll hide and show them (jQuery functions, but further tests will change the CSS directly) when needed.
<html>
<head>
<script src='https://code.jquery.com/jquery-1.11.3.min.js'></script>
</head>
<body>
<!-- I USED 15 ELEMENTS IN MY TEST, BUT THE CODE IS TOO LONG -->
<script>
var OBJ1 = {};
var OBJ2 = new Array();
var ACTIVE;
var BODY = $(document.body);
var T1, T2, DELTA;
$(function(){
T1 = new Date().getTime();
/*
//TEST 1: Javascript
var _this,id;
$('div').each(function(){
_this = $(this);
id = _this.attr('id');
OBJ1[id] = _this;
_this.remove();
});
var c;
for (var i = 0; i < 10000; i++) {
for(var k in OBJ1){
c = OBJ1[k];
if(ACTIVE) ACTIVE.remove();
BODY.append(c);
ACTIVE = c;
}
}*/
//TEST 2: DOM
var _this,id;
$('div').each(function(){
_this = $(this);
id = _this.attr('id');
OBJ2.push(id);
});
var c;
for (var i = 0; i < 10000; i++) {
for(var k=0,l=OBJ2.length;k<l;k++){
c = $('#'+OBJ2[k]);
if(ACTIVE) ACTIVE.hide();
c.show();
ACTIVE = c;
}
}
T2 = new Date().getTime();
DELTA = T2 - T1;
console.log('It took '+DELTA+' milliseconds');
});
</script>
</body>
I created 5 generic elements with some Lorem Ipsum and Lorem Pixel content and them copied/pasted them 3 times, making 15 1st-level elements.
For 1000 times I hide and revealed each one of the 15 elements. Ran the test 3 times each.
Case 1 (winner so far): 5.2s, 4.75s, 4.85s
Case 2: 21.5s, 21.5s, 20s
#edit 3: changing Case 2 method
Changing the second case from hide()/show() to css() makes a high increase in performance.
//TEST 2: DOM
var _this,id;
$('div').each(function(){
_this = $(this);
_this.css('display','none');
id = _this.attr('id');
OBJ2.push(id);
});
var c;
for (var i = 0; i < 15000; i++) {
for(var k=0,l=OBJ2.length;k<l;k++){
c = $('#'+OBJ2[k]);
if(ACTIVE) ACTIVE.css('display','none');
c.css('display','block');
ACTIVE = c;
}
}
Also increase the loop to 15k.
Case 1: Around 6.5s
Case 2: Around 2.5s
Definitely leave it in the DOM tree. I would just use show/hide methods to show which one you want.
I would only use the JS method if there was a dynamic changing value that should be reflected in the element, but if they are all static, changing the DOM tree is much more performance heavy rather than just a CSS style change.
You can test this by writing both, then using a jsperf to compare.
Javascript is a very fast language, however the DOM is a huge bottleneck. So having the data in a Javascript object will be much faster, but you will have to render the active element to the DOM on your own (Instead of just making it visible).
I wouldn't worry about the DOM being slow for a few dozen elements, but if you have lots of elements or are trying to perform computations/filters on them, definitely go for Javascript.
I think the only dom elements you might want to apply lazy rendering are those that require media content like image, audio, video,etc. For those, search for a lazy loading js library that load the content of that element when you show that element. Also you could leave the element loaded into the page and hidden, but maybe if there are too many media elements it might be a bad use of memory.
Lastly, you could use React.js for this since it's main job is to dinamically render the diff between two dom trees, this might give some great improvements in scenarios where you are rendering the same dom element but with slightly different content. You probably knew about this, is there any reason why you decided to go full vainilla.js on this ?
I am trying to add a second class to my element
Before
<div class="foo1">
<a class="class1" >Text</a>
</div>
After
<div class="foo1">
<a class="class1 class2" >Text</a>
</div>
here is my javascript code (tried but no success):
window.onload = function() {
document.getElementById('class1').className = 'class2';
};
UPDATE
actually, all of your answers work, but since this (class1) is a click event, by running the codes it temporarily adds the second class (class2) to the class1 but when page is reloaded , it has disappeared. I need to implement it via a click event by clicking class1. How do I do it?
2nd UPDATE
actually the site users have the ability to change this class, so I am trying to make this happen: that a user changes a class by clicking on class1 and making it "class1 class2"
Two problems with your code:
You're trying to look up class1 as an id, but you don't have any element with id="class1"; I assume you want to look up elements with that class, but that's not what getElementByid does
You're overwriting the class list by assigning to className; instead, you need to append to it (including a space, so you end up with the right string).
If you want to add the second class to all elements that have class1, you'll need a loop:
[].forEach.call(document.querySelectorAll(".class1"), function(element) {
element.className += " class2";
});
querySelectorAll works on all modern browsers, and also IE8. It accepts a CSS selector and returns a list of matching elements. Then, in the above, I loop over the list by borrowing Array#forEach, which is happy to loop through array-like things like the lists from querySelectorAll.
I should mention that if you want to use forEach on IE8, you'll need a polyfill for it. Or of course, use a boring old loop:
var list = document.querySelectorAll(".class1");
var n;
for (n = 0; n < list.length; ++n) {
list[n].className += " class2";
}
Or if you only want to do it if they're inside a .foo1:
[].forEach.call(document.querySelectorAll(".foo1 .class1"), function(element) {
element.className += " class2";
});
Or only direct children of a .foo1:
[].forEach.call(document.querySelectorAll(".foo1 > .class1"), function(element) {
element.className += " class2";
});
Suggested reading:
MDN
DOM
CSS Selectors
In case you're using jQuery and forgot to tag it (hey, it happens):
All:
$(".class1").addClass("class2");
Only inside a .foo1:
$(".foo1 .class1").addClass("class2");
Or only direct children of a .foo1:
$(".foo1 > .class1").addClass("class2");
To get all the elements with that class:
var elements = document.getElementsByClassName('class1');
Then loop over them:
for (var i = 0; i < elements.length; i++)
You need to concat the values:
element.className += ' class2';
So the full code looks like this:
var elements = document.getElementsByClassName('class1');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
element.className += ' class2';
}
Example (.class2 elements have red colour):
var elements = document.getElementsByClassName('class1');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
element.className += ' class2';
}
.class2 {
color: red
}
<div class="class1">1</div>
<div class="class1">2</div>
Or, if your browser supports classList (as most modern browsers do), then something like this will work as well:
element.classList.add('class2');
AFTER THE QUESTION UPDATE
but since this is a click event, it temporarily adds the second class but when page is reloaded , it has disappeared. I need to implement it via a click event. How do I do it?
Ideally, you'd use a server-side language to maintain changed classes after a page reloads. Normally, when you reload a page, your whole DOM reloads (including the classes).
There is a client-side solution, but not recommended: use a storage mechanism, like sessionStorage, localStorage, or plain old cookie, to keep track of click events. And then, when you render the page, simply check the storage for those click events you tracked, and adjust classes accordingly using the code above.
Why not recommended? Because this is a way of faking a server-side behaviour using a client-side language. Ideally, if you want to keep things on the client, you'd avoid the page reload completely and have everything happen on a single page (like SPAs do). But I'm afraid I can't tell you much more unless you provide more details about what are you trying to do.
Here's a super-simple example using localStorage, note how it keeps the class settings after a refresh:
http://jsfiddle.net/73efLsht/
Generating HTML source on backend, I am using separate independent widgets.
I am simply including pieces of markup like this to the resulting HTML output.
<div>
I want to work with this DOM element
<script>
new Obj(/*but I can't get this <div> as a parameter! */);
</script>
</div>
I'm looking for a way to find the DOM element in which the obj is created (Without any unique IDs). This would add flexibility to my app and speed up the development. But is that technicaly possible in JavaScript?
You could seed an element in there and then get it's parent, and then remove the element.
<div>
I want to work with this DOM element
<script>
document.write("<div id='UniqueGUID_3477zZ7786_' style='display:none;'></div>");
var thatDivYouWanted;
(function(){
var target = document.getElementById("UniqueGUID_3477zZ7786_");
thatDivYouWanted = target.parentNode;
target.parentNode.removeChild(target);
})();
new Obj(/*but I can't get this <div> as a parameter! */);
</script>
</div>
The following code works:
<script>
function Obj(color) {
var scriptTags = document.getElementsByTagName("script");
var scriptTag = scriptTags[scriptTags.length - 1];
// find parent or do whatsoever
var divTag = scriptTag.parentNode;
divTag.style.backgroundColor = color;
}
</script>
<div>
I want to work with this DOM element
<script>new Obj("green");</script>
</div>
<div>
I want to work with this DOM element
<script>new Obj("yellow");</script>
</div>
<div>
I want to work with this DOM element
<script>new Obj("lime");</script>
</div>
This method has very simple code and has almost zero impact on performance.
Note: I am pretty sure this won't work IE6 (as far as I remember it does not support manipulating open tags).
I believe your approach is not ideal. If you're trying to obtain the <div>, it should be done programmatically in a conventional way using JavaScript and the API's that empower you to query the target <div>
Instead of executing inline, you can execute in a separate scope in a controlled way (DOM Ready then Query then Your Method). You can target your div by using an ID, CSS class name, or any other CSS selector in JavaScript.
This allows you to pretty much do the follow anywhere you want, not inline.
// on dom ready...
var div = document.getElementById('myDiv'), // replace with any other selector method
myObject = new Object(div);
Need to find your div? https://developer.mozilla.org/en-US/docs/DOM/Document.querySelectorAll
If you know beforehand how the page will be structured, you could use for example:
document.getElementsByTagName("div")[4]
to access the 5th div.
Problem:
Extract all html between two headers including the headers html. The header text is known, but not the formatting, tag name, etc. They are not within the same parent and might (well, almost for sure) have sub children within it's own children).
To clarify: headers could be inside a <h1> or <div> or any other tag. They may also be surrounded by <b>, <i>, <font> or more <div> tags. The key is: the only text within the element is the header text.
The tools I have available are: C# 3.0 utilizing a WebBrowser control, or Jquery/Js.
I've taken the Jquery route, traversing the DOM, but I've ran into the issue of children and adding them appropriately. Here is the code so far:
function getAllBetween(firstEl,lastEl) {
var collection = new Array(); // Collection of Elements
var fefound =false;
$('body').find('*').each(function(){
var curEl = $(this);
if($(curEl).text() == firstEl)
fefound=true;
if($(curEl).text() == lastEl)
return false;
// need something to add children children
// otherwise we get <table></table><tbody></tbody><tr></tr> etc
if (fefound)
collection.push(curEl);
});
var div = document.createElement("DIV");
for (var i=0,len=collection.length;i<len;i++){
$(div).append(collection[i]);
}
return($(div).html());
}
Should I be continueing down this road? With some sort of recursive function checking/handling children, or would a whole new approach be better suited?
For the sake of testing, here is some sample markup:
<body>
<div>
<div>Start</div>
<table><tbody><tr><td>Oops</td></tr></tbody></table>
</div>
<div>
<div>End</div>
</div>
</body>
Any suggestions or thoughts are greatly appreciated!
My thought is a regex, something along the lines of
.*<(?<tag>.+)>Start</\1>(?<found_data>.+)<\1>End</\1>.*
should get you everything between the Start and end div tags.
Here's an idea:
$(function() {
// Get the parent div start is in:
var $elie = $("div:contains(Start)").eq(0), htmlArr = [];
// Push HTML of that div to the HTML array
htmlArr.push($('<div>').append( $elie.clone() ).html());
// Keep moving along and adding to array until we hit END
while($elie.find("div:contains(End)").length != 1) {
$elie = $elie.next();
htmlArr.push($('<div>').append( $elie.clone() ).html());
};
// htmlArr now has the HTML
// let's see what it is:
alert(htmlArr.join(""));
});
Try it out with this jsFiddle example
This takes the entire parent div that start is in. I'm not sure that's what you want though. The outerHTML is done by $('<div>').append( element.clone() ).html(), since outerHTML support is not cross browser yet. All the html is stored in an array, you could also just store the elements in the array.