Forum Moderators: open

Message Too Old, No Replies

Append to value of first list from second list selection onchange

append value to url submitted onchange

         

SteveXi7

5:49 pm on Apr 2, 2007 (gmt 0)

10+ Year Member



I have a select menu (firstmenu) which loads the last menu (ajaxloadedmenu) via ajax.

I want to add in a middle menu (filtermenu) which will append a variable to the url value of the first menu thereby filtering the list in the ajaxloadedmenu, so the submitted value becomes "spot.php?LatestArticles&genre=Romance". Ideally without a page refresh.

My Javascript skills are nonexistant, so any help with this will be very much appreciated.

<form>

<select name="firstmenu" onchange="loadDoc(event)">
<option value="spot.php?LatestArticles">Latest Articles</option>
<option value="spot.php?AllArticles">All Articles</option>
<option value="spot.php?ArticlesAlphabetically">Articles Alphabetically</option>
</select>

<select name="filtermenu">
<option value="&genre=Thrillers">Thrillers</option>
<option value="&genre=Romance"">Romance</option>
<option value="&genre=Comedy"">Comedy</option>
</select>

<select name="ajaxloadedmenu" id="topics" onchange="showDetail(event)">
<option value="">Choose a Category First</option>
</select>

</form>

[edited by: SteveXi7 at 5:51 pm (utc) on April 2, 2007]

Dabrowski

10:47 pm on Apr 3, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Can do this easy, your request is a little unclear though, can you elaborate?

Give me an example, you select 'Comedy' off your filter menu and what happens?

SteveXi7

8:52 am on Apr 4, 2007 (gmt 0)

10+ Year Member



If I select Latest Articles in firstmenu, the url "spot.php?LatestArticles" is passed to ajaxloadedmenu which loads the appropriate data via onchange ajax. This is all working perfectly already.

If I then select Comedy from the filtermenu, I want the existing chosen value from the firstmenu (the url) appended with the value from the filter menu "&genre=Comedy" so the resulting filtered list in the third menu is called by the amended url "spot.php?LatestArticles&genre=Comedy".

The ajaxloadedmenu works well with selection from the first list ("spot.php?LatestArticles") and also by manually changing the url ("spot.php?LatestArticles&genre=Comedy") I am just not sure what I need to do to cause the filtermenu to change the url and pass it on to ajaxloadedmenu.

Thanks for helping!

Dabrowski

10:15 am on Apr 4, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



ok, lets have a go with this, fist assign an ID to your select tags so we can find them easily, and add an onChange to your second menu so we can tell when it's clicked. Also add a blank option to menu2 so they can un-filter the list.


<select id="menu1" name="firstmenu" onchange="loadDoc(event)">
<select id="menu2" name="filtermenu" onChange="applyFilter()">
<option value="">View All</option>

ok, now a bit of script, put this inside your HEAD tag, I've commented the code so you can see what it's doing:


function applyFilter() {
// Find select elements
var menu1 = document.getElementById( "menu1");
var menu2 = document.getElementById( "menu2");

// Get selected value from menu1
// menu1.options is a list of your OPTION tags
// menu1.selectedIndex tells you which one is selected
var listURL = menu1.options[ menu1.selectedIndex].value;

// Use a regular expression to remove any genre setting previously added
// Not going to explain regular expressions, they're a nightmare
listURL = listURL.replace( /&genre.*/, "");

// Get selected value from menu2
var URLfilter = menu2.options[ menu2.selectedIndex].value;

// Add URL filter to URL
listURL += URLfilter;

// Put value back to menu1's option
menu1.options[ menu1.selectedIndex].value = listURL;
}

That should do it, but I don't know if it will trigger menu1's onChange event, so you may need to call loadDoc manually from the applyFilter function.

Let me know how it goes!

SteveXi7

11:30 am on Apr 4, 2007 (gmt 0)

10+ Year Member



Ok this is great, thankyou. It is partially working in this manner:

I choose "All Articles" from firstmenu, the ajaxmenu loads the list of all articles.
I then choose "Comedy" from filtermenu, nothing changes in ajaxmenu
I change my choice to "Latest Articles" from firstmenu, still no change,
But then when I select "All Articles" again in firstmenu the correctly filtered list of Comedy Articles appears in ajaxmenu.

How would I go about trying what you suggest in calling loadDoc manually from the applyFilter function?

Many Thanks!

Dabrowski

11:34 am on Apr 4, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Just add it onto the end of the function, change:


}

to


loadDoc();
}

I think this'll probably work but on the select tag it specifies (event), not sure what this is looking for so try it without as I've suggested.

SteveXi7

12:13 pm on Apr 4, 2007 (gmt 0)

10+ Year Member



Using loadDoc(); seems to make no difference although there are no errors reported, when I try with loadDoc(event); Firefox Console reports "event is not defined".

This is the loadDoc() function:

function loadDoc(evt) {
evt = (evt)? evt : ((window.event)? window.event : null);
if (evt) {
var elem = (evt.target)? evt.target : ((evt.srcElement)? evt.srcElement : null);
if (elem) {
try {
if (elem.selectedIndex > 0) {
loadXMLDoc(elem.options[elem.selectedIndex].value);
}
}
catch(e) {
var msg = (typeof e == "string")? e : ((e.message)? e.message : "Unknown Error");
alert("Unable to get XML data:\n" + msg);
return;
}
}
}
}

Dabrowski

6:58 pm on Apr 4, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



ok, event is just used to find the object that called loadDoc, and then do something with it.

Ultimately, it's a wrapper for the loadXMLDoc function, which takes the url as a parameter, in this case from the SELECT OPTION value.

So, replace loadDoc in your function to loadXMLDoc( menu1.options[ menu1.selectedIndex].value);

But I think there's a better way we can do this bypassing the loadDoc function. See if that works first and I'll change it around a bit if it does.

SteveXi7

12:31 am on Apr 5, 2007 (gmt 0)

10+ Year Member



Hey, Many Thanks!

That is much closer to how it should work.

firstmenu maintains a memory of the choice made in the second menu which breaks the user experience if they return to their original firstmenu choice.

Reading that back it sounds completely unclear to me, so here is my test page which explains things better than me:
<url removed>

[edited by: encyclo at 1:36 am (utc) on April 5, 2007]
[edit reason] no URLs please, see TOS [webmasterworld.com] [/edit]

SteveXi7

1:56 am on Apr 5, 2007 (gmt 0)

10+ Year Member



Sorry encyclo, I didn't think this broke the TOS as it was a disabled (unclickable) url to a temporary test page on a non-live domain name. I disabled the url because I did not want a link to a temporary page on my domain.

I stand corrected and will not post any url whatsoever again.

So I will try to explain better.

When I select from firstmenu (Latest Uploads) all is well - the ajaxmenu loads its list of all Latest Uploads.

I then select from the filtermenu (Comedy), again all is well, the ajax menu is filtered and shows all Comedy Latest Uploads.

I change the selection in the firstmenu to Articles Listed Alphabetically, the unfiltered options show in the ajaxmenu (all articles A to Z).

I select a different option (Romance) in the filtermenu, the ajaxmenu changes appropriately, showing Romance A to Z.

But then, if I choose my original selection (Latest Uploads) from the firstmenu, the filtermenu selected item does not change from it's previous selection (Romance), whilst the ajaxmenu still shows the list of Latest Uploads Comedy.

I hope this explains better, and apologies once again for any possible slight breach of etiquette.

And crucially, thankyou so much for your help Dabrowski, I really do appreciate it very much. I am sorry I could not provide you (and anyone eslse interested) with a link to reduce your reading time!

[edited by: SteveXi7 at 2:17 am (utc) on April 5, 2007]

Dabrowski

10:05 am on Apr 5, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Hi mate, had a look at the site and noticed that problem myself before I read your post, so I knew exactly what you meant.

No problem, now that's working we'll replace the loadDoc function with one of our own, try this. Change both your select tags to call the applyFilter function:


<select id="menu1" name="firstmenu" onchange="applyFilter()">
<select id="menu2" name="filtermenu" onChange="applyFilter()">


function applyFilter() {
var menu1 = document.getElementById( "menu1");
var menu2 = document.getElementById( "menu2");
var url = "";

url += menu1.options[ menu1.selectedIndex].value;
url += menu2.options[ menu2.selectedIndex].value;

loadXMLDoc( url);
}

This way is actually shorter and easier - all we do is put the 2 OPTION value strings together, and pass it to loadXMLDoc. This way we don't need to worry about modifying values of menu OPTIONs.

SteveXi7

1:54 pm on Apr 5, 2007 (gmt 0)

10+ Year Member



Thankyou so much, that is just about perfect and has really made my day!

I have had some trouble with the XML and ampersands, but have worked around it within my xml templates and php. However, I am unable to reach the option label displaying in the third menu to clean it up. I think is being built by this code:

function appendToSelect(select, value, content) {
var opt;
opt = document.createElement("option");
opt.value = value;
opt.appendChild(content);
select.appendChild(opt);
}

Is there a Javascript equivalent to url_decode which I can add to this function? My title in the last menu is currently showing as Drum+%26+Bass, which would need to be decoded to Drum & Bass.

Thanks

[edited by: SteveXi7 at 1:55 pm (utc) on April 5, 2007]

Dabrowski

2:11 pm on Apr 5, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



This problem is 2 fold, first you have the %26, and second you have '+'.

To convert it back to a normal string you can use the unescape function:

unescape( string);

In this case it would give you 'Drum+&+Bass'. If the space character was also escaped (%20) this would be easier. Use a regexp to sort the '+'s out:

string = string.replace( /\+/, " ");

So then you should get 'Drum & Bass'.

So to put that in your function you'd modify it as:

function appendToSelect(select, value, content) {
var opt;
var realVal = unescape( value);
opt = document.createElement("option");
opt.value = realVal.replace( /\+/, " ");
opt.appendChild(content);
select.appendChild(opt);
}

That should work.

SteveXi7

2:31 pm on Apr 5, 2007 (gmt 0)

10+ Year Member



That hasn't made any difference, so I am guessing that I guessed the wrong function!

Whould it be the function containing appendToSelect which needs the change?

function buildTopicList() {
var select = document.getElementById("topics");
var items = req.responseXML.getElementsByTagName("item");
for (var i = 0; i < items.length; i++) {
appendToSelect(select, i,
document.createTextNode(getElementTextNS("", "title", items[i], 0)));
}
document.getElementById("ultraspot").innerHTML = "";
}

Dabrowski

2:48 pm on Apr 5, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I think I see what happened, I picked the wrong parameter.

In this function, you have select, value, content as parameters; 'select' is the SELECT tag to append (DOM Object), 'value' is the value='?' parameter of the OPTION, and 'content' is the actual text in the OPTION.

So instead of modifying 'value' we should have modified 'content'. Try that.

SteveXi7

4:00 pm on Apr 5, 2007 (gmt 0)

10+ Year Member



I have tried with this (and many guessed variations), but again nothing seems to change at all:

function appendToSelect(select, value, content) {
var opt;
opt = document.createElement("option");
opt.value = value;
var realCont = unescape(content);
opt.content = realCont.replace( /\+/, " ");
opt.appendChild(content);
select.appendChild(opt);
}

I have tried using unescape() all over, but always either no change, or the whole section breaks down.

Dabrowski

4:47 pm on Apr 5, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Man you really are new to JS!

ok, modify your function quoted above to:

function appendToSelect(select, value, content) {
var opt;
opt = document.createElement("option");
opt.value = value;
var realCont = unescape(content);
opt.content = realCont.replace( /\+/, " ");
opt.appendChild(content);
select.appendChild(opt);
}

See you were creating a new variable, opt.content which would contain the correct string, but then opt.appendChild( content) is called to add the text to the OPTION.

content there referrs to the function's content parameter. You need to remove the opt.content= line as it's not needed, and change the opt.appendChild( content) to opt.appendChild( realCont.replace.....);

You almost had it right!

SteveXi7

5:28 pm on Apr 5, 2007 (gmt 0)

10+ Year Member



Yes, a complete noob! Thanks for your patience!

Where you say to modify my function to: the function in your code box is identical to the one above!

I have tried your instructions below, but the debugger shows that it fails at this line (opt.appendChild(realCont.replace( /\+/, " "));) so I guess there is something missing from the code box?

function appendToSelect(select, value, content) {
var opt;
opt = document.createElement("option");
opt.value = value;
var realCont = unescape(content);
opt.appendChild(realCont.replace( /\+/, " "));
select.appendChild(opt);
}

Dabrowski

6:01 pm on Apr 5, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Don't worry, I was a noob once!

I forgot how hard it was trying to work though code somebody else has written. A couple of things, sorry I forgot to modify that function after I copied from your post, you did the mod ok though.

I checked your test page again, it appears you have accidentally duplicated the appendToSelect function, but that's not the problem. I checked the buildtopiclist function and it's passing a textNode object, rather than actual text, so another slight modification as follows:

function appendToSelect(select, value, content) {

content.data = unescape( content.data);
content.data = content.data.replace( /\+/, " ");

var opt = document.createElement("option");
opt.value = value;
opt.appendChild( content);

select.appendChild(opt);
}

You see the textNode object contains the string in a variable called data (content.data) and because it's already stored here it's easier not to create another var. By saying content.data = (do something with content.data) we're diverting the output back into itself.

Apart from that I've just rearranged the code slightly to tidy it up. 1st rule of coding is tidy code is easier to work with!

SteveXi7

7:42 pm on Apr 5, 2007 (gmt 0)

10+ Year Member



That is brilliant, thankyou so much!

I found that the regex needed changing slightly as it only removed the first +, so that line became
content.data = content.data.replace( /\+/g, " ");

When selecting a menu for the first time I have noticed an error reported in the Console (although I don't see any problem with the way that it is working):
uncaught exception: [Exception... "Index or size is negative or greater than the allowed amount" code: "1" nsresult: "0x80530001 (NS_ERROR_DOM_INDEX_SIZE_ERR)" location: "http://example.com/jambbb/community.php Line: 833"]

which is referring to one of these lines in the applyFilter function, depending on if the firstmenu or secondmenu is selected first.

832: url += menu1.options[ menu1.selectedIndex].value;
833: url += menu2.options[ menu2.selectedIndex].value;

Is there a means of testing whether both selections have been made to avoid the error?

Thanks again!

[edited by: jatar_k at 10:00 pm (utc) on April 5, 2007]
[edit reason] please use example.com [/edit]

Dabrowski

8:57 pm on Apr 5, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Nicely done mate, glad you spotted the /g, that's what happens when you write code that's not tested! Well done for finding it though.

As for the selectedIndex error, there's 2 ways we can solve it, either specify the default in the HTML:


...
<option value="http://bristol.fm/jambbb/ultraspot.php?LatestUploads" SELECTED>Latest Uploads</option>
...
<option value="" SELECTED>All Genres</option>
...

Notice we've added the 'SELECTED' flag on the OPTION tags. Or we can specify a default in the JS:

function applyGenreFilter() {
var menu1 = document.getElementById( "menu1");
var menu2 = document.getElementById( "menu2");
var idx1 = menu1.selectedIndex ¦¦ 0;
var idx2 = menu2.selectedIndex ¦¦ 0;
var url = "";
url += menu1.options[ idx1].value;
url += menu2.options[ idx2].value;
loadXMLDoc(url);
}

The expression 'menu1.selectedIndex ¦¦ 0' means try menu1.selectedIndex, if there's nothing there then use 0. The ¦¦ means OR.

***NOTE VERY IMPORTANT*** THE ¦ CHARACTER HAS BEEN INCORRECTLY INTERPRETED ON THIS PAGE, IT'S THE SYMBOL ABOVE '\'.

You can shorten this further by saying

menu1.options[ (menu1.selectedIndex ¦¦ 0)].value
, but sometimes code shortened too much can be difficult to read back. This is ok though.

SteveXi7

12:21 am on Apr 6, 2007 (gmt 0)

10+ Year Member



I tried the Javascript method, but it didn't work straight off for me. I have gone with the html, as I can pass the selected in as a parameter in the template Smarty function.

I do have a further question though (hope I'm not being too pushy!).

Looking at the result now, it does exactly what I wanted it to do. But seeing it working reveals something that I hadn't thought of - when the final selection is made (in the ajaxloadedmenu) the article is loaded below. If the visitor starts out on making another another selection, the article disappears immediately, which doesnt seem right - it should persist until another ajaxloadedmenu choice is made, which will load the new article. The content should persist until fresh content is selected to load.

Many, many thanks for your assistance during my first Javascript foray. I won't be a noob forever, I know this because of the increase in understanding I have made today. For me, your explanations work jsut as well as your solutions!

Dabrowski

12:48 am on Apr 6, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



No you're not being pushy at all. The current JS I'm working on is current 415 lines, so this is very small time.

The function showDetail fills in the info window. This line:

if (select && select.options.length > 1) {

Says if it has found the SELECT tag, and it has more than one option (the first is always 'Choose a Category First' or 'Refine Choice'). When the list resets I think the onChange event is being fired, and because then nothing is selected in that box the list clears.

If that is the case, all we have to do is change that IF statement to also check something has been selected.

if (select && select.options.length > 1 && select.selectedIndex) {

The only thing is I'm not sure if selectedIndex will be reset, so it may remember the old value. Try it and see.

As for explanations, I have spent many years learning. I find that someone giving me the solution is no good as it teaches me nothing, so I always explain. That way you'll know for next time.