I'm using JavaScript to access HTML elements with GetElementById.
Now I changed the HTML/CSS to use Media Queries for large/small screens and by that I needed to double some of the elements. Double, because I want to display and control (javascript) certain elements in different positions of the screen, depending on if they are shown on a PC or on a Smartphone.
Problem, now I can't use getElementById anymore since that must be unique.
But I don't want to use getElementsByName, since that requires lots of changes on the Javascript and basically produces the same amount of code as if I just use different Ids for all elements for each Media Query. Are there better ways?
e.g.
now (with getElementById) - Setting one icon:
document.getElementById("idIcon1").innerHTML = '<src="/Images...>';
with getElementsByNames - Setting the same Icon (used twice on HTML, each in its Media Query section):
document.getElementsByName("Icon1")[0].innerHTML = '<src="/Images...>';
document.getElementsByName("Icon1")[1].innerHTML = '<src="/Images...>';
in HTML:
<div class="screenLarge">
<div name="idIcon1" class="icon1_Large"></div>
...
</div>
<div class="screenSmall">
<div name="idIcon1" class="icon1_Small"></div>
...
</div>
(The classes ScreenLarge / ScreenSmall are Display:none, depending on the Screen size.)
If I use the getElementsByName, that produces 2 lines of code in JS (for [0] and [1]). But that is pretty much the same thing, as if just use two different IDs, one for small and one for large screens.
Any idea is welcome...
thanks
hbi
I like your idea below, you can make that even a bit more generic, e.g.
function myFunction(name, prop, value) {
var el = document.getElementsByName(name);
var i;
for (i = 0; i < el.length; i++) {
switch (prop) {
case "innerHTML": el[i].innerHTML = value; break;
case "color": el[i].style.color = value; break;
...
default: break;
}
}
}
and then call the function like this:
myFunction("idActVerb", "innerHTML", "-");
myFunction("idActVerb", "color", "blue");
perhaps not the most beautiful code but it serves my purpose, so I'll give it a try :-)
thanks partypete and Kamil
What if you created a function that you called for every different set of elements with matching names? That way you'd only have to call it once for each pair.
myFunction(document.getElementsByName("idIcon1"), '<img src="/Images...">');
function myFunction(el, inner) {
var i;
for (i = 0; i < el.length; i++) {
el[i].innerHTML = inner;
}
}
Related
I'm attempting to add a class for highlighting certain elements on my web page. I'm using the jQuery("selector").addClass("class") function to do that. However, it does not work.
function toggleHighlight(selector, on) {
if (on) {
$(selector).addClass("highlighted");
} else {
$(selector).removeClass("highlighted");
}
}
When the on argument is true and the selector argument is a valid selector, it enters the if clause and exits. Unfortunately the classes of the elements the selector refers to, remain unchanged. I seem to missing something fairly fundamental. Help would be appreciated.
EDIT:
I left out the console.log($(selector)); and console.log($(".highlighted")); statements earlier, for clarity. The first returns the right elements, the second, none (called after .addClass()). The elements themselves are <path> elements created by leaflet for a map (from GeoJSON). As such, the HTML is rather hard to reproduce.
I have called the function form a button on the page: <input type="button" id="toggle-0702" class="btn btn-default" onClick="toggleHighlight('.node-0112', true)" value="turn on" \> as well as directly from the JS console. Same results.
EDIT 2:
Apparently this is an issue with either <path> elements, or (more likely) leaflet. To demonstrate this: http://jsfiddle.net/mrz40jgw/. Notice the shy class on the second circle.
We don't have enough information to say exactly what's going on, but unless you've interferred with jQuery, we can rule out that addClass is broken. Thus, one of the following is likely true:
toggleHighlight is not being called when you think it is. Use console.log to debug this.
$(selector) is not yielding what you think it is. Use console.log to debug this.
Or perhaps most likely:
Adding the class highlight does not affect the targeted elements in the manner you think it ought to, perhaps due to a selector of greater specificity. Debug this by manually setting the class and verify how that affects the visual properties of the element.
Note that jQuery already has a toggle for classes, so your code can be simplified to:
function toggleHighlight(selector, on) {
$(selector).toggleClass("highlighted", on);
}
It would appear that the problem lies with jQuery's inability to change classes on SVG elements: jQuery SVG, why can't I addClass?
For the record, here's my solution in light of the new information:
function toggleHighlight(selector, on) {
var oldClasses = [];
var newClasses = "";
var elements = $(selector)
for (var i = 0; i < elements.length; i++) {
oldClasses = $(elements[i]).attr("class").split(" ");
newClasses = "";
for (var j in oldClasses) {
if (oldClasses[j] != "highlighted") {
newClasses += " " + oldClasses[j];
}}
if (on) {
newClasses += " highlighted";
}
$(elements[i]).attr("class", newClasses.trim());
}}
I have two tables on my html page with exact same data but there may be few difference which need to be highlighted.
I and using the below Javascript but seems innerHTML does not work as expected-
function CompareTables()
{
var table1 = document.getElementById("table1")
var table2 = document.getElementById("table2")
for(var i=1; i < table1.rows.length; i++)
{
for(var j=1; j < table2.rows.length; j++){
var tab1Val = table1.rows[i].cells[0].innerHTML;
var tab2Val = table2.rows[j].cells[0].innerHTML;
alert(tab1Val.toUpperCase()+"----"+tab2Val.toUpperCase());
var changes =RowExists(table2,tab1Val);
if(!changes[0])
{
table1.rows[i].style.backgroundColor = "red";
instHasChange = true;
}
}
function RowExists(table,columnValue)
{
var hasColumnOrChange = new Array(2);
hasColumnOrChange[0] = false;
for(var i=1; i < table.rows.length; i++)
{
if(table.rows[i].cells[0].innerHTML == columnValue) /*** why these two does not match**/
{
hasColumnOrChange[0] = true;
}
return hasColumnOrChange;
}
}
Please suggest what wrong here.
(table.rows[i].cells[0].innerHTML == columnValue) never returns true even if all values same.
most browsers have bugs with innerHTML and it is not a recommended property to use. different browsers will do different things, usually messing with whitespace, adding/removing quotes, and/or changing the order of attributes.
long story short, never rely on innerHTML.
In it's place, I would recommend using some of the DOM traversal functions, such as .firstChild and .nodeValue. Notice that these are sensitive of white space, and will have to be tweaked if you have anything else in your TD than just text.
http://jsfiddle.net/tdN5L/
if (table.rows[i].cells[0].firstChild.nodeValue === columnValue)
Another option, as pointed out by Micah's solution, is using a library such as jQuery, which will let you ignore most of these browser issues and DOM manipulation pain. I would not recommend bringing in the overhead of jQuery just for this issue, though.
related:
Firefox innerHTML Bug?
innerHTML bug IE8
innerHTML removes attribute quotes in Internet Explorer
Try and use Jquery's method .text
Depending on the browser(Firefox and Chrome's) innerHTML does not work
JQuery takes care of that issue for you.
I have some data in a sql table. I send it via JSON to my JavaScript.
From there I need to compose it into HTML for display to the user by 1 of 2 ways.
By composing the html string and inserting into .innerHTML property of the holding element
By using createElment() for each element I need and appending into the DOM directly
Neither of the questions below gives a quantifiable answer.
From first answer in first link, 3rd Reason ( first two reasons stated don't apply to my environment )
Could be faster in some cases
Can someone establish a base case of when createElement() method is faster and why?
That way people could make an educated guess of which to use, given their environment.
In my case I don't have concerns for preserving existing DOM structure or Event Listeners. Just efficiency ( speed ).
I am not using a library regarding the second link I provided. But it is there for those who may.
Research / Links
Advantages of createElement over innerHTML?
JavaScript: Is it better to use innerHTML or (lots of) createElement calls to add a complex div structure?
Adding to the DOM n times takes n times more time than adding to the DOM a single time. (:P)
This is the logic I'm personally following.
In consequence, when it is about to create, for instance, a SELECT element, and add to it several options, I prefer to add up all options at once using innerHTML than using a createElement call n times.
This is a bit the same as to compare BATCH operation to "one to one"... whenever you can factorise, you should!
EDIT: Reading the comments I understand that there's a feature (DOM DocumentFragment) that allow us saving such overhead and at the same time taking advantage of the DOM encapsulation. In this case, if the performances are really comparable, I would definitely not doubt and chose the DOM option.
I thought I read somewhere that the createElement and appendElement is faster. It makes sense, considering document.write() and innerHTML have to parse a string, and create and append the elements too. I wrote a quick test to confirm this:
<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function inner() {
var test = '';
for (var i=0; i<10000; i++) {
test += '<p>bogus link with some other <strong>stuff</strong></p>';
}
console.time('innerHTML');
document.getElementById('test').innerHTML = test;
console.timeEnd('innerHTML');
}
function jq() {
var test = '';
for (var i=0; i<10000; i++) {
test += '<p>bogus link with some other <strong>stuff</strong></p>';
}
console.time('jquery');
jQuery('#test').html(test);
console.timeEnd('jquery');
}
function createEl() {
var dest = document.getElementById('test');
console.time('createel');
//dest.innerHTML = '';//Not IE though?
var repl = document.createElement('div');
repl.setAttribute('id','test');
for (var i=0; i<10000; i++) {
var p = document.createElement('p');
var a = document.createElement('a');
a.setAttribute('href','../'); a.setAttribute('target','_blank');
a.appendChild(document.createTextNode("bogus link"));
p.appendChild(a);
p.appendChild(document.createTextNode(" with some other "));
var bold = document.createElement('strong');
bold.appendChild(document.createTextNode("stuff"));
p.appendChild(bold);
repl.appendChild(p);
}
dest.parentNode.replaceChild(repl,dest);
console.log('create-element:');
console.timeEnd('createel');
}
</script>
<button onclick="inner()">innerhtml</button>
<button onclick="jq()">jquery html</button>
<button onclick="createEl()">Create-elements</button>
<div id="test">To replace</div>
</body>
</html>
In this example, the createElement - appendChild method of writing out HTML works significantly faster than innerHTML/jQuery!
I'm trying to use Anarchy Media Player on my site but had to change code a bit because my media files/urls are not in standard format. It pulls the video files now but javascript is finding ALL links and tries to add player to it. I even added id "video" to video link but still finds all links on the page and adds player to it.
How can I separate those links to get player added to them only?
Here is the part of the javascript that looks for the links:
var all = document.getElementsByTagName('a');
for (var i = 0, o; o = all[i]; i++) {
if(o.idName="video" && o.className!="amplink") {
// Add player code...
}
}
Thank you for your help.
Not sure why you're not just doing this:
var all = document.getElementsByTagName('a');
for (var i = 0, len = all.length; i<len; i++) {
if(all[i].idName=="video" && all[i].className!="amplink") {
// Add player code...
}
}
That should work for you. Note that I stored the value of the array length so you don't have to look it up every iteration. Minimal, but does increase performance slightly.
You were also setting idName to video when you did idName="video" in your if statement, not comparing (that will always return true, which was your problem). Use the double equals operator (==) or the triple equals operator (===) to compare values.
Are you sure you don't mean if (o.className == "amplink")?
Besides that what you have is fine (except for the o.idName="video" bit which always returns true): http://jsfiddle.net/VpMVe/
you can get elements using querySelectorAll, but it's not supported in older browsers (that would be older then IE8, FF3.5, Opera 10, Chrome 4 and Safari 3.1). It's similar to targetting elements using CSS selectors (and jQuery). here's a demo getting all links with class video to turn red.
function get(selector) {
return document.querySelectorAll(selector)
}
(function() {
//get all <a> with class "video"
var video = get('a.video');
//for each, turn them red
for (var i = 0; i < video.length; i++) {
video[i].style.color = 'red';
//in your case, do whatever you want for each target here
}
}())
There is no idName attribute. It's just o.id. And there can only be one id, so that's likely to cause some issues. Also you're assigning it, rather than comparing for equality which always evaluates to true. If you have more than one class, e.g. class="amplink someOtherClass" != amplink will evaluate to true. Better to do a match on the className. Like this:&& !o.className.match(/ampLink/)
Make a video on another class and do matches instead.
I have some code doing this :
var changes = document.getElementsByName(from);
for (var c=0; c<changes.length; c++) {
var ch = changes[c];
var current = new String(ch.innerHTML);
etc.
}
This works fine in FF and Chrome but not in IE7. Presumably because getElementsByName isn't working in IE. What's the best workaround?
In case you don't know why this isn't working in IE, here is the MSDN documentation on that function:
When you use the getElementsByName method, all elements in the document that have the specified NAME attribute or ID attribute value are returned.
Elements that support both the NAME attribute and the ID attribute are included in the collection returned by the getElementsByName method, but elements with a NAME expando are not included in the collection; therefore, this method cannot be used to retrieve custom tags by name.
Firefox allows getElementsByName() to retrieve elements that use a NAME expando, which is why it works. Whether or not that is a Good Thing™ may be up for debate, but that is the reality of it.
So, one option is to use the getAttribute() DOM method to ask for the NAME attribute and then test the value to see if it is what you want, and if so, add it to an array. This would require, however, that you iterate over all of the nodes in the page or at least within a subsection, which wouldn't be the most efficient. You could constrain that list beforehand by using something like getElementsByTagName() perhaps.
Another way to do this, if you are in control of the HTML of the page, is to give all of the elements of interest an Id that varies only by number, e.g.:
<div id="Change0">...</div>
<div id="Change1">...</div>
<div id="Change2">...</div>
<div id="Change3">...</div>
And then have JavaScript like this:
// assumes consecutive numbering, starting at 0
function getElementsByModifiedId(baseIdentifier) {
var allWantedElements = [];
var idMod = 0;
while(document.getElementById(baseIdentifier + idMod)) { // will stop when it can't find any more
allWantedElements.push(document.getElementById(baseIdentifier + idMod++));
}
return allWantedElements;
}
// call it like so:
var changes = getElementsByModifiedId("Change");
That is a hack, of course, but it would do the job you need and not be too inefficient compare to some other hacks.
If you are using a JavaScript framework/toolkit of some kind, you options are much better, but I don't have time to get into those specifics unless you indicate you are using one. Personally, I don't know how people live without one, they save so much time, effort and frustration that you can't afford not to use one.
There are a couple of problems:
IE is indeed confusing id="" with name=""
name="" isn't allowed on <span>
To fix, I suggest:
Change all the name="" to class=""
Change your code like this:
-
var changes = document.getElementById('text').getElementsByTagName('span');
for (var c=0; c<changes.length; c++) {
var ch = changes[c];
if (ch.className != from)
continue;
var current = new String(ch.innerHTML);
It's not very common to find elements using the NAME property. I would recommend switching to the ID property.
You can however find elements with a specific name using jQuery:
$("*[name='whatevernameYouWant']");
this will return all elements with the given name.
getElementsByName is supported in IE, but there are bugs. In particular it returns elements whose ‘id’ match the given value, as well as ‘name’. Can't tell if that's the problem you're having without a bit more context, code and actual error messages though.
In general, getElementsByName is probably best avoided, because the ‘name’ attribute in HTML has several overlapping purposes which can confuse. Using getElementById is much more reliable. When specifically working with form fields, you can more reliably use form.elements[name] to retrieve the fields you're looking for.
I've had success using a wrapper to return an array of the elements. Works in IE 6, and 7 too. Keep in mind it's not 100% the exact same thing as document.getElementsByName, since it's not a NodeList. But for what I need it for, which is to just run a for loop on an array of elements to do simple things like setting .disabled = true, it works well enough.
Even though this function still uses getElementsByName, it works if used this way. See for yourself.
function getElementsByNameWrapper(name) {
a = new Array();
for (var i = 0; i < document.getElementsByName(name).length; ++i) {
a.push(document.getElementsByName(name)[i]);
}
return a;
}
Workaround
var listOfElements = document.getElementsByName('aName'); // Replace aName with the name you're looking for
// IE hack, because it doesn't properly support getElementsByName
if (listOfElements.length == 0) { // If IE, which hasn't returned any elements
var listOfElements = [];
var spanList = document.getElementsByTagName('*'); // If all the elements are the same type of tag, enter it here (e.g.: SPAN)
for(var i = 0; i < spanList.length; i++) {
if(spanList[i].getAttribute('name') == 'aName') {
listOfElements.push(spanList[i]);
}
}
}
Just another DOM bug in IE:
Bug 1: Click here
Bug 2: Click here