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.
Related
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;
}
}
I'm trying to parse a page with javascript to replace links belonging to a specific class with an iframe to open a corresponding wikipedia page [so that rather than having a link you have an embedded result]. The function detects links properly but something about the replaceChild() action causes it to skip the next instance... as if it does the first replace and then thinks the next link is the one it just worked on, probably as a result of the loop.
For example, if there's 2 links on the page, the first will parse and the second will not even be seen but if there's 3, the first two will be parsed using the attributes from the first and third.
Can anyone suggest an alternative way of looping through the links that doesn't rely on a count function? Perhaps adding them to an array?
Sample Links
wiki it
Sample Javascript
(function(){
var lnks = document.getElementsByTagName("a");
for (var i = 0; i < lnks.length; i++) {
lnk = lnks[i]; if(lnk.className == "myspeciallinks"){
newif=document.createElement("iframe");
newif.setAttribute("src",'http://www.wikipedia.com');
newif.style.width="500px";
newif.style.height="100px";
newif.style.border="none";
newif.setAttribute("allowtransparency","true");
lnk.parentNode.replaceChild(newif,lnk);
}
}
})();
The problem here is that document.getElementsByTagName returns a NodeList and not an array. A NodeList is still connected to the actual DOM, you cannot safely iterate over its entries and at the same time remove the entries from the DOM (as you do when you replace the links).
You will need to convert the NodeList into an array and use the array for iteration:
(function(){
var lnksNodeList = document.getElementsByTagName("a");
// create an array from the above NodeList and use for iteration:
var lnks = Array.prototype.slice.call(lnksNodeList);
for (var i = 0; i < lnks.length; i++) {
var lnk = lnks[i];
if (lnk.className == "myspeciallinks") {
var newif = document.createElement("iframe");
newif.setAttribute("src", 'http://www.wikipedia.com');
newif.style.width = "500px";
newif.style.height = "100px";
newif.style.border = "none";
newif.setAttribute("allowtransparency", "true");
lnk.parentNode.replaceChild(newif, lnk);
}
}
})();
According to the MDN documentation:
Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself. The returned list is live, meaning that it updates itself with the DOM tree automatically. Consequently, there is no need to call several times element.getElementsByTagName with the same element and arguments.
Therefore, the collection shrinks every time you replace an a. You could change your loop to decrement i whenever you do a replace.
Ok to start of i currently have 4 divs with the same id for example:
<div id='name'></div>
<div id='name'></div>
<div id='name'></div>
<div id='name'></div>
and im currently using a javascript function to display the value of the div's for example:
function divCheck(){
alert(document.getElementById('name').innerHTML);
}
the problem im having is when i call the function it only displays the value of the first div.
My goal is to display the values of all the divs and place it into a Textarea input.
I will really truly appreciate it. In advance thank you.
Use class instead of id, and use getElementsByClassName
ID is for use once, and is generally for large item (div etc) which has to be pretty unique, or is to be individually accessed, when you need to access combinations or even apply CSS properties on grpups of html elements without having to type them again and for each id, use class, and apply the common properties to that class, use ID for unique properties.
Similarly here use class, as you can see the function is get*Elements*ByClassName, means it returns a group, and this is what class is for. For this kind of use, use class instead of ID.
As others have said, use classes instead of ids. Each id must be unique. You cannot have more than one object with the same id. Here's how it looks with a class name instead:
<div class='name'></div>
<div class='name'></div>
<div class='name'></div>
<div class='name'></div>
And, here's how you get all objects with a given class name and iterate over them:
function divCheck() {
var elems = document.getElementsByClassName('name');
for (var i = 0; i < elems.length; i++) {
alert(elems[i].innerHTML);
}
}
Unfortunately, getElementsByClassName() was not supported by IE until IE9 so you will have to use a javascript shim that implements it a different way when it doesn't already exist. Or, use a pre-built library like Sizzle or jQuery that already support this type of functionality in older browsers.
Hey why don't you use the class instead of Ids. Give dynamic classNames like class="className-'+id+'"
And the call them using :
$('div[class^="className-'+id+'""]')
Hope it will be useful.
P.S Avoid using same ids for elements.
Since you seem to be after a getElementsByClassName function independant of any library, try the following. It tries querySelectorAll first, if not available it tries getElementsByClassName, and finally does an old school iterate over elements approach.
It will also accept multiple class names and always returns a static NodeList or array of the matched elements (per querySelectorAll). Note that getElemensByClassName returns a live NodeList, so the result must be converted to an array otherwise it might behave a little differently if elements are being added or removed from the document.
/*
Selector must be per CSS period notation, using attribute notation
(i.e. [class~=cName]) won't work for non qSA browsers:
single class: .cName
multiple class: .cName0.cName1.cName2
If no root element provided, use document
First tries querySelectorAll,
If not available replaces periods '.' with spaces
and tries host getElementsByClassName
If not available, splits on spaces, builds a RegExp
for each class name, gets every element inside the
root and tests for each class.
Could remove duplicate class names for last method but
unlikely to occur so probably a waste of time.
Tested in:
Firefox 5.0 (qSA, gEBCN, old)
IE 8 (old method only, doesn't support qSA or gEBCN)
Chrome 14 (qSA, gEBCN, old)
*/
function getByClassName(cName, root) {
root = root || document;
var reClasses = [], classMatch;
var set = [], node, nodes;
// Use qSA if available, returns a static list
if (root.querySelectorAll) {
return root.querySelectorAll(cName);
}
// Replace '.' in selector with spaces and trim
// leading and trailing whitespace for following methods
cName = cName.replace(/\./g, ' ').replace(/^\s+/,'').replace(/\s+$/,'');
// Use gEBCN if available
if (root.getElementsByClassName) {
nodes = root.getElementsByClassName(cName);
// gEBCN usually returns a live list, make it static to be
// consistent with other methods
for (var i=0, iLen=nodes.length; i<iLen; i++) {
set[i] = nodes[i];
}
return set;
}
// Do it the long way... trim leading space also
nodes = root.getElementsByTagName('*');
cName = cName.split(/\s+/);
// Create a RegExp array of the class names to search on
// Could filter for dupes but unlikely to be worth it
for (var j = 0, jLen = cName.length; j < jLen; j++) {
reClasses[j] = new RegExp('(^|\\s+)' + cName[j] + '\\s+|$');
}
// Test each element for each class name
for (var m = 0, mLen = nodes.length; m < mLen; m++) {
node = nodes[m];
classMatch = true;
// Stop testing class names when get a match
for (var n = 0, nLen = reClasses.length; n < nLen && classMatch; n++) {
classMatch = node.className && reClasses[n].test(node.className);
}
if (classMatch) {
set.push(node);
}
}
return set;
}
This is an odd one, for whatever reason, getting the children of an element doens't work in Camino browser. Works in all other browsers. Anyone know how to fix this? Google is no help :(
var site_result_content = document.getElementById(content_id);
site_child_nodes = site_result_content.children;
alert('started');
for(i=0;i<site_child_nodes.length;i++) {
alert('cycle1');
document.getElementById(site_child_nodes[i].id).className = 'tab_content';
ShowHide(site_child_nodes[i].id,'hidden');
}
In this case, the started alert is called, but the cycle1 isn't.
Use childNodes instead. children started out as a proprietary property that in IE, whereas childNodes is in the W3C DOM spec and is supported by every major browser released in the last decade. The difference is that children contains only elements whereas childNodes contains of all types, in particular text nodes and comment nodes.
I've optimized your code below. You should declare all your variables with var, including those used in loops such as i. Also, document.getElementById(site_child_nodes[i].id) is unnecessary: it will fail if the element has no ID and is exactly the same as site_child_nodes[i] otherwise.
var site_result_content = document.getElementById(content_id);
var site_child_nodes = site_result_content.childNodes;
alert('started');
for (var i = 0, len = site_child_nodes.length; i < len; ++i) {
if (site_child_nodes[i].nodeType == 1) {
alert('cycle1');
site_child_nodes[i].className = 'tab_content';
ShowHide(site_child_nodes[i].id, 'hidden');
}
}
I'd hazard a guess that it's not been implemented yet (it was only implemented in Firefox 3.5). You can use childNodes instead, which returns a list of nodes (rather than just elements). Then check nodeType to make sure it's an element.
var site_result_content = document.getElementById(content_id);
site_child_nodes = site_result_content.childNodes;
alert('started');
for(i=0;i<site_child_nodes.length;i++) {
// Check this is actually an element node
if (site_child_nodes[i].nodeType != 1)
continue;
alert('cycle1');
document.getElementById(site_child_nodes[i].id).className = 'tab_content';
ShowHide(site_child_nodes[i].id,'hidden');
}
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