I want to get all the <a> tags from an Html page with this JavaScript method:
function() {
var links = document.getElementsByTagName('a');
var i=0;
for (var link in links){i++;}
return i;
}
And i noticed it's won't return the correct number of a tags.
Any idea what can by the problem?
Any idea if there is any other way to get all the href in an Html ?
Edit
I tried this method on this html : http://mp3skull.com/mp3/nirvana.html .
And i get this result:"1". but there are more results in the page.
You don't need a loop here. You can read length property.
function getACount() {
return document.getElementsByTagName('a').length;
}
You don't have to loop over all of them just to count them. HTMLCollections (the type of Object that is returned by getElementsByTagName has a .length property:
$countAnchors = function () {
return document.getElementsByTagName('a').length;
}
Using getElementsByTagName("a") will return all anchor tags, not only the anchor tags that are links. An anchor tags needs a value for the href property to be a link.
You might be better off with the links property, that returns the links in the page:
var linkCount = document.links.length;
Note that this also includes area tags that has a href attribute, but I assume that you don't have any of those.
UPDATE Also gets href
You could do this
var linkCount = document.body.querySelectorAll('a').length,
hrefs= document.body.querySelectorAll('a[href]');
EDIT See the comment below, thanks to ttepasse
I would cast them to an array which you then slice up, etc.
var array = [];
var links = document.getElementsByTagName("a");
for(var i=0; i<links.length; i++) {
array.push(links[i].href);
}
var hrefs = array.length;
The JavaScript code in the question works as such or, rather, could be used to create a working solution (it’s now just an anonymous function declaration). It could be replaced by simpler code that just uses document.getElementsByTagName('a').length as others have remarked.
The problem however is how you use it: where it is placed, and when it is executed. If you run the code at a point where only one a element has been parsed, the result is 1. It needs to be executed when all a elements have been parsed. A simple way to ensure this is to put the code at the end of the document body. I tested by taking a local copy of the page mentioned and added the following right before the other script elements at the end of document body:
<script>
var f = function() {
var links = document.getElementsByTagName('a');
var i=0;
for (var link in links){i++;}
return i;
};
alert('a elements: ' + f());
</script>
The results are not consistent, even on repeated load of the page on the same browser, but this is probably caused by some dynamics on the page, making the number of a elements actually vary.
What you forget here was the length property. I think that code would be:
var count = 0;
for (var i = 0; i < links.length; i++) {
count++;
}
return count;
Or it would be:
for each (var link in links) {
i++;
}
length is used to determine or count the total number of the element which are the result.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for (For Loop)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for_each...in (Foreach Loop)
Related
I'm learning JS but have hit a roadblock. I have links that have the attribute "number". I'd like to extract the value of "number" from each link, set it as a new variable, and then assign an onclick action to each link incorporating the corresponding value. I've been able to extract each value but don't know how to use them in the onclicks.
HTML
<a class="button call" href="#" number="6135555556">Call pager</a>
<a class="button call" href="#" number="6135555555">Call cell</a>
JS
var data = document.getElementsByClassName("call");
var numbers = '';
for (var i = 0; i < data.length; i++) {
numbers += data[i].getAttribute("number");
numbers[i].onclick = console.log("call " + numbers[i]);
}
If you want to the particular value on click of particular link then you can use this code.
var data = document.getElementsByClassName("call");
var numbers = [];
for (var i = 0; i < data.length; i++) {
data[i].onclick = getNumber;
}
function getNumber(){
numbers.push(this.dataset['number']);
alert(this.dataset['number']);
}
Here is the DEMO
There is no number property on anchor tag, so for your need we can use data-* property which allows you to store needful information on html.
This may not be entity correct, but assuming what you wanted was to console log the contained phone number whenever a link was clicked, there are probably 3 main changes you'd want to look at.
1) I'm guessing you wanted to connect your onclick event to the link element with the number in it data[i], rather to the number itself?
2) += will concatenate each found value on to the previous one. This may be what you wanted, although in the below code I've changed it only to log the current number
3) onclick expects to be passed a function, which it will then run when the click event is fired. Wrapping your console log in a function provides it to the onClick in the format it expects.
Assuming all that's right, the js to work with the above links should look something like this:
var data = document.getElementsByClassName("call");
for (var i = 0; i < data.length; i++) {
data[i].onclick = function() { console.log("call " + this.getAttribute("number")); }
}
Hope that helps :)
Edit: Updated the code to fix the bug james montagne pointed out below. The getAttribute is now performed within the context of the click event, meaning the issue with scoping is avoided. Sorry about that, completely missed the issue.
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.
i'm creating a new dom element:
var bubbleDOM = document.createElement('div');
bubbleDOM.setAttribute('class', 'selection_bubble');
document.body.appendChild(bubbleDOM);
then i populate data to this DOM in a manner of text using .responseText with
XMLHttpRequest and finally change it's css:
.selection_bubble {
position: absolute;
}
Everything works great until now. my problem is that a part of the data i've populated to
the DOM element has a href link such as:
/prop.php?id=333
which makes them think the link is really coming from my website, so when the user clicks on it he goes to:
http://www.mywebsite.com/prop.php?id=333
and i want those links to appear from the website (which i know it's name and there is
only one i'm pulling the data from) as they should be:
http://www.thesitei'mpullingfrom.com/prop.php?id=333
How can i change this either with CSS or JS?
To improve my original comment on the question:
In order to fix only the URLs that start with a slash (/), something like the following command should be run:
(edited in accordance with the comment fixing the expression)
responseText.replace(/(href=")(\/[^"]+)"/g, "$1http://thesiteimpullingfrom.com$2\"")
Parse the (X)HTML into a DOM Object then get all the anchor Elements and replace their href attributes. E.g.:
var i = 0, n ;
var l ;
var s ;
l = dom.getElementsByTagName("a") ;
for( n = l.length; i < n ; i++ ) {
o = l[i] ;
if( !! ( s = o.getAttribute("href") ) ) {
// add domain to s
o.setAttribute("href") ;
}
}
I am not experienced in Javascript. After the page finishes loading, I need to iterate thru all URLs a given page and perform some cleanings.
How do I do that?
Something like
for i = 0 to (number of URLS on the page) {
doSomething (URL(i));
}
thanks
If you want to link through all anchors, use document.links, like this:
for(var i = 0, l=document.links.length; i<l; i++) {
doSomething(document.links[i].href);
}
This is a collection already maintained by the browser (for prefetching under the covers mostly, but other reasons as well)...no need for a document.getElementsByTagName() here. Note: this also gets <area> elements, as long as they have a href attribute...also a valid form of navigation.
I'd always recommend having jQuery around for times like this as it makes it far easier.
For example on page load:
$(document).ready(function(){
$('a').each(function(index) {
alert(index + ': ' + $(this).text());
});
});
Use this function:
var anchors = document.getElementsByTagName("a");
for (anchor in anchors){
doSomething(anchor):
}
Or just plain Javascript with a bit of readability:
var myURL = document.getElementsByTagName('a');
for(var i=0; i<myURL.length; i++)
console.log(myURL[i].getAttribute('href'));
What I want to do is to have a function that executes to define a "current" class to an <li> tag, depending on the page name. For example, if the page is named index.php, I want the function to determine if the link within the <li> tag is the same name as the page (index.php). I'm sorry I'm not the best at explaining this. Here's an example code.
<ul id="navigation"><li><a href="index.php"></li></ul>.
I want the class "current" defined into the <li> tag if the link is the same as the page name. Is there any way to do this? Or am I on a futile mission?
I think what you are asking is you want to change the look of links that are pointing to the present page. Here is what the code would look like.
var list=document.getElementsByTagName('a');
var page=window.location.pathname;
var i=list.length;
while(i--){
if(list[i].src.indexOf(page)>0){
list[i].className='current';
}
}
Note this is not a very accurate method. The basic structure is correct, but for example a link somewebsite.com is actually pointing to somewebsite.com/index.php. So depending on the link this could cause a problem on the home page. Also, depending on how your links are setup you are probably going to have to the parse the page variable. It will return something like. /help/faq/foo.php while the page may only have a link to faq/foo.php. This all depends a lot on the setup of your site so I will leave it for you to tweak.
You added more details since I posted so I thought I would note that you would only need to make a list of the links in the <li> tags not all the <a> tags in the page.
Well...okay...
function liClass(context) {
//Choose your parent node
context = context || document;
var pathparts = window.location.pathname.split('/'); //split on path
var curfile = pathparts[pathparts.length-1]; //last item is the filename right?
var lis = context.getElementsByTagName('li');
for (var i=0, l=lis.length; i<l; i++) {
var a = lis[i].getElementsByTagName('a');
if (!a.length) continue; //li has no a, moving on...
//Okay, this match might need some work, tried
//to make it fairly forgiving, tweak for your needs
if (a[0].href.indexOf(curfile) != -1) lis[i].className = 'current';
}
}
Feel free to try it out, let me know if it works or not, cause I did not test it...
Compare the href's of the location object, and the anchor DOM element. If they match, then that is the current class.
If the current page is http://www.example.com/home which contains a relative link,
Questions
Then the href property of the DOM object will contain the absolute path, and not just the relative part.
link.href = "http://www.example.com/questions"
A function that loops through each link could then be written as,
function markCurrent(list) {
var listItems = list.getElementsByTagName("li");
for(var i = 0; i < items.length; i++) {
var link = listItems[i].getElementsByTagName("a")[0];
if(link && (link.href == location.href)) {
listItems[i].className += ' current';
}
}
}
Pass the root <ul> node to the function.
markCurrent(document.getElementById("navigation"));