Get all href values in <ul> group and compare with current url

M

michael

Is it possible to get all href URLs contained in a unordered list and place
them in an array?
Or in fact two different arrays, differently named one for each <ul> group?

<ul>
<li><a href="lemurs.html">Lemurs</a></li>
<li><a href="chameleons.html">Chameleons</a></li>
</ul>

<ul>
<li><a href="sharks.html">Sharks</a></li>
<li><a href="crocs.html">Crocodiles</a></li>
</ul>

I want to fetch an array of the links within a <ul> group, and
document.write the above, but with class variable in which I can
set a different value, depending on if the current url filename matches
exactly any one of the links within the <ul> group the <li> belongs to.

More specifically, I would like to ...

document.write('<li class'+myvariable+'>')
|
... class 'green' or 'blue' -------'
depending on whether filename in any href contained
in an <li> of the <ul> group it is in.
If an exact match is found, write out green, if not blue.

The file name portion of the current URL I simply get through:

var f = window.location.href;
file = i.substring (f.lastIndexOf('/') +1);

It is for drop-down nav-system whereby all list items would of a ul group
would appear either one of two colours as specified in css properties. All
links are internal links on the same domain so one would always match.

I could naturally do it all by having a pre-typed list of all filenames and
run each <li> through an if statement before writing it out. But as there
will be many <li>'s I would prefer a more typeless method if possible.

Many thanks for any tips!

Michael
 
J

Jerome Bei

Try this:

<script>
uls = document.getElementsByTagName("UL");
for (var u=0; u<uls.length; u++) {
alert("entering list ["+u+"]");
lis=uls.getElementsByTagName("LI");
for (var i=0; i<lis.length; i++) {
if (lis.childNodes[0] && lis.childNodes[0].href) {
alert("found link ["+lis.childNodes[0].href+"]");
}
}
}
</script>

(Testes on IE 6.0 only)

--Jerome
 
M

michael

Jerome said:
Try this:

<script>
uls = document.getElementsByTagName("UL");
for (var u=0; u<uls.length; u++) {
alert("entering list ["+u+"]");
lis=uls.getElementsByTagName("LI");
for (var i=0; i<lis.length; i++) {
if (lis.childNodes[0] && lis.childNodes[0].href) {
alert("found link ["+lis.childNodes[0].href+"]");
}
}
}
</script>

(Testes on IE 6.0 only)

--Jerome


Works nicely, fetches the links if there are, and it works on Mozilla too.

Thanks!
Michael

-
The human animal differs from the lesser primates in his passion for
lists of "Ten Best".
-- H. Allen Smith
 
R

RobG

michael said:
Jerome Bei wrote:

Try this:

<script>
uls = document.getElementsByTagName("UL");
for (var u=0; u<uls.length; u++) {
alert("entering list ["+u+"]");
lis=uls.getElementsByTagName("LI");
for (var i=0; i<lis.length; i++) {
if (lis.childNodes[0] && lis.childNodes[0].href) {


If anything is inserted between the <li> and <a> tags, even a space,
this will fail as the <a> element will no longer be the first child
of the <li>.

Probably better to use a recursive function to go down the tree from
the said:
alert("found link ["+lis.childNodes[0].href+"]");
}
}
}
</script>

(Testes on IE 6.0 only)

--Jerome



Works nicely, fetches the links if there are, and it works on Mozilla too.


But will fall over if you insert anything between the <li> and <a>
tags, as that will insert another element and the parent/child
relationship is broken. Below is a script that runs down the tree
to the first <a> and returns that (if there is one).


<script type="text/javascript">
function getULs() {
var h, i, u, lena, lenb, lis, uls;
uls = document.getElementsByTagName("UL");
for ( u=0, lena=uls.length; u<lena; u++) {
alert("entering list [" + u + "]");
lis = uls.getElementsByTagName("LI");
for (i=0, lenb=lis.length; i<lenb; i++) {
if ( (h = hasAchild(lis)) ) {
alert("found link [" + h.id + "]");
}
}
}
}

// Given an element ref, descend element tree until an <a> is found
// Return a reference to the element or null if not found
function hasAchild(x){
isA = false; // global
if (x.nodeName == 'A') {
isA = x;
return isA; // Stop descent on first A element
}
for (var i=0; i<x.childNodes.length; i++) {
// Descend if not found an A yet
if (!isA) hasAchild(x.childNodes);
}
return isA;
}

</script>
</head>
<body id="theBody">
<ul>
<li><span>spacer</span> <a id="a0" href="blah" onclick="getULs();
return false;">the ref</a></li>
<li><a id="a1" href="blah" onclick="getULs(); return false;">the
ref</a></li>
<li>no ref</li>
</ul>
 
R

RobG

RobG said:
michael wrote: [...]

// Given an element ref, descend element tree until an <a> is found
// Return a reference to the element or null if not found
function hasAchild(x){
isA = false; // global
if (x.nodeName == 'A') {
isA = x;
return isA; // Stop descent on first A element
}
for (var i=0; i<x.childNodes.length; i++) {
// Descend if not found an A yet
if (!isA) hasAchild(x.childNodes);
}
return isA;
}


Had a bit of fun with this, here's a neater version:

function hasAchild(x){
isA = ( 'A' == x.nodeName)? x : false;
for (var i=0, j=x.childNodes.length; !isA && i<j; i++) {
hasAchild(x.childNodes);
}
return isA;
}
 
M

Michael Winter

On 04/05/2005 08:35, RobG wrote:

[snip]
If anything is inserted between the <li> and <a> tags, even a space,
this will fail as the <a> element will no longer be the first child
of the <li>.

Probably better to use a recursive function to go down the tree from
the <li> to see if it has an <a> element as a descendant.

Probably better to use getElementsByTagName to find any A elements
within a list, directly. An A element would only be valid within a list
item, so it seems rather pointless to spend time walking through the tree.

[snip]

Mike
 
M

michael

Probably better to use getElementsByTagName to find any A elements

There are obvsiously many ways and maybe this one would be the best. I'm a
bit lost however as how to piece it all together.
Therefore I shall try and explain a bit more what I'm looking for:

I plan to generate a drop-down menu with CSS hover effects that would
simply work on top of bare-bone unordered lists and which will be
javascript generated by a central.js file for easy cross-site modifications.

Disregarding style issues for this example sake, the navigation may be
described simply as a bunch of links, within <LI>'s, within different
groups of <UL>'s.

These UL's will be horizontally aligned in CSS navbar style, and only the
first link of each UL group will be visible until the mouse hovers over it,
at which point the complete array of links of the UL group appears below it.

The first and always visible link of each UL would simply lead to an
overview page containing a summary of contents for its group of links.
So the top-level links, would function a bit like a subject headings,
although they may be considered by the javascript as any of the links
contained within their groups.

The javascript would define a class property for all LI's in each group, as
either:
The active, green class, if the user is on any page linked within its group.
OR
Non-active, blue class, for all other UL's and their LI's contained within.

The purpose of the CSS class is only to indicate in which section the user
currently is, which should make more sense in the CSS version, as only the
first link is displayed, and as the mouse hovers over the currently active
group with its current page on display, or when its over a non-active group.

An example with only two link groups below, if viewed as a page named
"lemurs.html" would style all background of the "flora_and_fauna" group of
links light-green, whilst if viewed as a page named "hiking.html", all
links in the "activities" group would be light-blue. Or, a page not linked
from anywhere within the js menu itself, all groups would remain light-blue.

Note that each link would appear only in one place or the navigation system
woudl not be clear to the user.

My so-called javascript below requires all links and groups pre-defined,
and filenames in the if statements explicity typed out etc.
This may be OK for a few links only but to be used in a => 50 links
nav-system that one can easily modified its not a very good solution.
But anyway, the functionality could be more or less same as below mock-up:

<style>
..green { background-color:lightgreen }
..blue { background-color:lightblue; }
</style>

var f = window.location.href; /* get file name portion of URL */
file = f.substring (f.lastIndexOf('/') +1);


function display_links(group){ /* run function with group argument */
var state = "green";

/* explicity type possible combinations for each group */
if (group == "flora_and_fauna" && file == "flora_and_fauna.html" || group
== "flora_and_fauna" && file == "lemurs.html" || group == "flora_and_fauna"
&& file == "chameloens.html" || group == "flora_and_fauna" && file ==
"mangroves.html"){
return state;
}

/* and again ... */
else if (group == "activities" && file == "activities.html" || group ==
"activities" && file == "hiking.html" || group == "activities" && file ==
"diving.html"){
return state;
}

/* if none is matched, eg. a user is on a page not linked within the .js
navigation system itself, all groups light-blue */
else {
return "blue";
}

}

/* write out each ul, li and link, and run function to set class value */
document.write('<ul>')
document.write('<li class='+display_links('flora_and_fauna')+'><a
href="flora_and_fauna.html">Flora & Fauna</a></li>')
document.write('<li class='+display_links('flora_and_fauna')+'><a
href="lemurs.html">Lemurs</a></li>')
document.write('<li class='+display_links('flora_and_fauna')+'><a
href="chameleons.html">Chameleons</a></li>')
document.write('<li class='+display_links('flora_and_fauna')+'><a
href="mangroves.html">Mangroves</a></li>')
document.write('</ul>')


document.write('<ul>')
document.write('<li class='+display_links('activities')+'><a
href="activities.html">Activities</a></li>')
document.write('<li class='+display_links('activities')+'><a
href="hiking.html">Hiking</a></li>')
document.write('<li class='+display_links('activities')+'><a
href="diving.html">diving</a></li>')
document.write('</ul>')

Any ideas, and especially short code would be greatly appreciated!

Many thanks,
Michael
--

You know the great thing about TV? If something important happens
anywhere at all in the world, no matter what time of the day or night,
you can always change the channel.
-- Jim Ignatowski
 
R

RobG

Michael said:
On 04/05/2005 08:35, RobG wrote:

[snip]
If anything is inserted between the <li> and <a> tags, even a space,
this will fail as the <a> element will no longer be the first child
of the <li>.

Probably better to use a recursive function to go down the tree from
the <li> to see if it has an <a> element as a descendant.


Probably better to use getElementsByTagName to find any A elements
within a list, directly. An A element would only be valid within a list
item, so it seems rather pointless to spend time walking through the tree.

Killjoy.
 
R

RobG

michael said:
Probably better to use getElementsByTagName to find any A elements


There are obvsiously many ways and maybe this one would be the best. I'm a
bit lost however as how to piece it all together.
Therefore I shall try and explain a bit more what I'm looking for:
[...]

')

Any ideas, and especially short code would be greatly appreciated!

Yes, there are a zillon ways to do this, google "DHTML menu".

Forget 'document.write' to create your menu as the page loads, that
will guarantee that non-JavaScript browsers can't use your site.

To get further help:

1. Create your menus as static HTML.

2. Add DHTML behaviour as an add-on, that way non-JavaScript browsers
can still navigate your pages.

3. As part of the above, you can see if a UL group contains an HREF
with the same filename as the current page. If so, change its
parent UL class to 'active'.

I've posted a bit of code that just does the class thing, I've
included multiple classes so I can detect the 'head' ULs (I'm not
interested in descendant ULs, you may be) as an example, but my idea
of what you are trying to do is probably very different to what you
are actually doing.

Note you don't have to define 'convenience' classes like head and sub
if you don't want to.



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title> Z 0 </title>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<style type="text/css">
..high {background-color: #99FFCC; color: black;}
..low {background-color: #33CC00; color: white;}
</style>
<script type="text/javascript">

function doClass(){
var As, ff, x;
var a = RegExp('.*/'); // Needed many times, compile for speed
var f = document.URL.replace(a,''); // filename of current page
var d = document.getElementById('menu');
var uls = d.getElementsByTagName('ul');

for (var i=0, j=uls.length; i<j; i++){
x = uls;

// If the UL className string includes word 'head'
if ( /\bhead\b/.test(x.className) ){

// Get its A elements
As = x.getElementsByTagName('a');

// Search for matching file names
for (var k=0, m=As.length; k<m; k++) {
ff = As[k].href.replace(a,'');

// If match, modify the className string of the parent UL
if ( ff == f ){
x.className = x.className.replace(/\blow\b/,'high');

// No need to continue once we've found one
return;
}
}
}
}
}
</script>
</head>
<body onload="doClass();">
<div id="menu">
<ul class="head low" id="blah">
<li><a href="z0.html">Flora&nbsp;&amp;&nbsp;funa</a>
<ul class="sub">
<li><a href="z1.html">Pretty&nbsp;flowers</a></li>
<li><a href="z2.html">Deadly&nbsp;vines</a></li>
</ul>
</li>
</ul>
<ul class="head low">
<li><a href="z3.html">Aquatic&nbsp;creatures</a>
<ul class="sub">
<li><a href="z4.html">Pretty&nbsp;fish</a></li>
<li><a href="z5.html">Don&#39t&nbsp;eat</a></li>
</ul>
</li>
</ul>
</div>
</body>
</html>
 
R

RobG

RobG said:
michael wrote:
[...]
Any ideas, and especially short code would be greatly appreciated!
[...]

var f = document.URL.replace(a,''); // filename of current page

Sorry, forgot IE doesn't do document.URI, try this:

var f = document.location.href.replace(a,'');

[...]
 
M

michael

interested in descendant ULs, you may be) as an example, but my idea
of what you are trying to do is probably very different to what you
are actually doing.

This looks convertable to exactly what I need, perfect!

And code can't get much shorter. Only, I get a javascript error:

--
Error: illegal character
Source File: file:///tmp/z2.html
Line: 24, Column: 10
Source Code:
if ( \bhead\b.test(x.className) ){

Error: doClass is not defined
--

I don't know how to fix that, I guess second error is due to the first.

Many thanks,
Michael
 
R

RobG

michael said:
This looks convertable to exactly what I need, perfect!

And code can't get much shorter. Only, I get a javascript error:

--
Error: illegal character
Source File: file:///tmp/z2.html
Line: 24, Column: 10
Source Code:
if ( \bhead\b.test(x.className) ){

Error: doClass is not defined
--

I don't know how to fix that, I guess second error is due to the first.

Many thanks,
Michael

Don't know why you get the error, I tested in IE and Firefox. Have
you created files for each link? And included the modified URL/href
line?
 
M

Michael Winter

On 05/05/2005 03:50, RobG wrote:

[snip]
Sorry, forgot IE doesn't do document.URI, try this: [...]

Not URI, no, but URL, yes. Unless Microsoft are lying in their
documentation, it's been included in all versions, on all supported
platforms, since 4.0.

Have you found a version where it fails to work, or are you just
assuming it doesn't?

Mike
 
M

Michael Winter

On 05/05/2005 06:42, michael wrote:

[snip]
I get a javascript error:
[snip]

if ( \bhead\b.test(x.className) ){

Where have the forward slashes gone?

if(/\bhead\b/.test(x.className)) {
Error: doClass is not defined

Yes. The parsing error within the function causes the parser to halt the
creation of the function object. When the browser tries to call doClass
in response to the load event, it fails as the object doesn't exist.

FYI: Your signature separator seems to be broken. It should be
dash-dash-space.

Mike
 
M

michael

Where have the forward slashes gone?

Thanks for mentioning it, I suspect something's wrong with my Linux
font-config set-up or X-windows, or just the newsreader itself.
I just viewed source of the posting and indeed found the missing forward
slashes- I shall try again with "/" and I'm sure it will work much better!

Michael
 
M

michael

<li><a href="z5.html">Don&#39t&nbsp;eat</a></li>
</ul>
</li>
</ul>
</div>
</body>
</html>


This works great!

But does anyone know how to get it working as a horizontally aligned drop
menu, only showing 'sub' list items when hovering over the first <li> of
each <ul class="head low">, so if for example hovering over Flora & Fauna
or any of its sub-links, show:

[Flora & funa] [Aquatic creatures[
[Pretty flowers]
[Deadly vines]

And retaining the background scheme for all items, but with a variable, so
the current page is not actually a link?
 
M

michael

I pieced together a hover-drop-menu derived from
htmldog.com/articles/suckerfish/dropdowns with the colour-by-URL modifier
script solution as posted by Rob earlier.

The new styles basically removes the sub-listed items from view, leaving
only the first LI of the <ul class="head low"> visible until mouse hovers
over it, to bring its sub-points back into view.

The background colour works for the LI's on top, which are always visible,
but not for the sub-points for some reason.
Although font styles do take effect across a complete lists, for example: ..

..high {background-color: #99FFCC; font-size:80%;}

... makes a smaller font on all items in the group containing current URL.

Anyone has any idea why the background colours doesn't take effect behind
the sub-points?

See complete page below:

<html><head><title></title>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<style type="text/css">

#menu, #menu ul {
padding: 0;
margin: 0;
list-style:none;
}

#menu a {
display: block:
width: 10em:
}

#menu ul {
float: left;
width: 10em;
}

#menu li ul {
position: absolute;
width: 10em;
left: -999em;
}

#menu li:hover ul {
left: auto;
}

#menu li:hover ul{
left: auto;
}


#menu li.sfhover ul{ /* ie specific, but alignment not yet working */
left: auto;
margin-top:1em;margin-left:-1em;
}


..high {background-color: #99FFCC; font-size:80%;}
..low {background-color: #33CC00;}

</style>

<script type="text/javascript">

function doClass(){
var As, ff, x;
var a = RegExp('.*/'); // Needed many times, compile for speed
var f = document.URL.replace(a,''); // filename of current page
var d = document.getElementById('menu');
var uls = d.getElementsByTagName('ul');

for (var i=0, j=uls.length; i<j; i++){
x = uls;

// If the UL className string includes word 'head'
if ( /\bhead\b/.test(x.className) ){

// Get its A elements
As = x.getElementsByTagName('a');

// Search for matching file names
for (var k=0, m=As.length; k<m; k++) {
ff = As[k].href.replace(a,'');

// If match, modify the className string of the parent UL
if ( ff == f ){
x.className = x.className.replace(/\blow\b/,'high');

// No need to continue once we've found one
return;
}
}
}
}
}
</script>


<script language="javascript">

/* the suckerfish script to fix IE compatibility in bringing the sub-menu
items back in view, although not as intended directly under its headings */

sfHover = function() {
var sfEls = document.getElementById("menu").getElementsByTagName("LI");
for (var i=0; i<sfEls.length; i++) {
sfEls.onmouseover=function() {
this.className+=" sfhover";
}
sfEls.onmouseout=function() {
this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
}
}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);

</script>


</head>


<body onload="doClass();">

<div id="menu">

<ul class="head low">


<li><a href="z0.html">Flora&nbsp;&amp;&nbsp;puna</a>

<ul class="sub">
<li><a href="z1.html">Pretty&nbsp;flowers</a></li>
<li><a href="z2.html">Deadly&nbsp;vines</a></li>
</ul>


</li>

</ul>
<ul class="head low">


<li><a href="z3.html">Aquatic&nbsp;creatures</a>

<ul class="sub">
<li><a href="z4.html">Pretty&nbsp;fish</a></li>
<li><a href="z5.html">Don&#39t&nbsp;eat</a></li>
</ul>

</li>


</ul>
</div>

</body>
</html>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top