Ajax problem

45 views
Skip to first unread message

Chris Sansom

unread,
Jun 22, 2019, 1:08:03 PM6/22/19
to Prototype & script.aculo.us
With apologies in advance for the length...

I retired a few years ago from web development, where I used Prototype quite a bit in the later years. I'm somewhat rusty now, but I'm trying to do some development for purely personal use. It won't be on the open web, just on our local server. It's all been going fine (if slowly!), but now I've hit a brick wall with Ajax.

Here's what I'm trying to do:
*  I have a form with one or more <select>s, each within its own <li>, all in the one <ul>.
*  Each <select> contains the same list of items from a table in a MySQL database.
*  The last <select> has nothing selected and has the class "addselect".
*  The idea is that if I select something from that last <select>, an onChange event is triggered which calls an Ajax script to add a new <select> (containing the same items). This is set up on page load in a "document.observe ('dom:loaded', function()..." at the top of the javascript file.
*  Each <select> has a sequential name and id, eg 'name="country[0]" id="country_0"', the next country[1] and country_1, and so on. The newly created <select> would have the next name and id in sequence.
I hope that all makes some sense.

So...
The handler function set up in 'dom:loaded...' gets the next index number for the name and id of the new <select>, and sends this ('entryindex') and the name of the table to search ('list') to the Ajax script, and off we go:

var pars = 'list=' + list + '&entryindex=' + newIndex;
var url = base + 'ajaxgetmenu';
// send request to do the business
var myAjax = new Ajax.Request (url, {
method: 'post',
parameters: pars,
onSuccess: function (req) {
var xml = req.responseText;
...

This far, everything is hunkydory. Safari's Web Inspector shows exactly the correct result - a nice new <select> with the correct name and contents - under 'XHRs' in its Resources tab. So I know the script is being called and is operating correctly. But where it falls over is with what comes next:

var newhtml = xml.getElementsByTagName('newhtml')[0].firstChild.nodeValue;

This, incidentally, is taken from a script I developed back in my professional days which I know worked perfectly. But something is failing here, cuz it don't work! If I put an alert in ('ok') before this line, it pops up, but if I put it after this line it doesn't. And yet no errors are reported. And of course the next line doesn't work either:

$('thisid').up('ul').insert (newhtml);

(And I know "$('thisid')" is right because I put that in an alert to check it.)

So I simplified it. I abandoned XML and made the result plain text instead, to be inserted verbatim. Again, the script produces a perfect result in Safari's Web Inspector, but even when newhtml is plain text (and I skip the xml.getElementsByTagName line altogether), it fails. And again, an alert before the insert line works, but not one after it.

I've tried everything I can think of, so if anyone has any wisdom on this I'd be very grateful indeed. It's probably something dead obvious staring me in the face...

Walter Lee Davis

unread,
Jun 23, 2019, 11:09:13 AM6/23/19
to prototype-s...@googlegroups.com
One of the best ways to work this out is to build a cut-down example, in one page, that tries to do what you're after here. If you can get that to fail, post it as a Gist or similar. From your description, you're going about things the right way.

One other piece of advice. Rather than using a numerical index to get the first of some collection, try making your method iterate over the found results and operate conditionally. This will give you another place to put console.log statements to further refine exactly what is going on inside:

> var newhtml = xml.getElementsByTagName('newhtml')[0].firstChild.nodeValue;

When you say that the correct code to insert is there in the Ajax response, that cuts that part of the problem out, and your example page can just do something dumb like this:

var data_to_insert = $('some_example_element').remove().innerHTML.toString();

...where you have a DOM element like <div id="some_example_element"><select ... your element here ...></div>

I have found over the years that when you have the correct element (legally possible to insert into a given container), then Prototype (and the browser) just does what you'd expect. But if what you are getting back from the server doesn't make any sense in terms of your DOCTYPE (say you are trying to insert raw text into a <ul>, rather than trying to insert a <li>, then the browser will just ignore you. It's not a Prototype error, in other words, just the browser being pedantic when being called in a JS update mode.

Finally, see if you can get your server to return the HTML of the picker, fully rendered. Then you should be able to do this:

new Ajax.updater('parent_element_id', '/server/url', {
parameters: { foo: 'bar', ...}, // optional, pass to your server some parameters
insertion: 'bottom' // this lets you put the rendered element at the bottom of the existing children of the parent
});

If #parent_element_id is a <li>, then the HTML you get from /server/url?foo=bar should be:

<li>
<select ... >
<option ...></option>
</select>
</li>

... with all of the whitespace removed.

Walter
> --
> You received this message because you are subscribed to the Google Groups "Prototype & script.aculo.us" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to prototype-scripta...@googlegroups.com.
> To post to this group, send email to prototype-s...@googlegroups.com.
> Visit this group at https://groups.google.com/group/prototype-scriptaculous.
> To view this discussion on the web visit https://groups.google.com/d/msgid/prototype-scriptaculous/fb84d882-af7d-45f8-a400-b98701228f19%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Walter Lee Davis

unread,
Jun 23, 2019, 11:12:16 AM6/23/19
to prototype-s...@googlegroups.com
Sorry, typo here:

> On Jun 23, 2019, at 11:09 AM, Walter Lee Davis <wa...@wdstudio.com> wrote:
>
> If #parent_element_id is a <li>, then the HTML you get from /server/url?foo=bar should be:


I meant, if the parent element is a <UL>.

Walter

Chris Sansom

unread,
Jun 23, 2019, 12:22:18 PM6/23/19
to prototype-s...@googlegroups.com
Hi Walter

Thanks for your long and thoughtful response… some of which I understood! (I did say I was rusty, and I was never extremely advanced - never got into OOP for instance unless it was laid out on a plate for me as prototype does).

Just a quick question - do you know whether there’s some limit to the length of a string that can be returned via Ajax, and whether it can contain carriage returns, tabs or whatever? What I’m returning here is politely formatted and indented html with 76 options in the select.
> You received this message because you are subscribed to a topic in the Google Groups "Prototype & script.aculo.us" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/prototype-scriptaculous/CFLnDmlHmhM/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to prototype-scripta...@googlegroups.com.
> To post to this group, send email to prototype-s...@googlegroups.com.
> Visit this group at https://groups.google.com/group/prototype-scriptaculous.
> To view this discussion on the web visit https://groups.google.com/d/msgid/prototype-scriptaculous/586F2AE6-E77E-4ECC-B18A-F1D80C04685A%40wdstudio.com.
> For more options, visit https://groups.google.com/d/optout.

--
Cheers... Chris
Chris Sansom: composer and… whatnot
http://www.chrissansom.net/
https://www.youtube.com/channel/UCVUKb7vK0KiIaiLhs7zht2Q
https://soundcloud.com/mfthoad

‌I got rid of my vacuum cleaner - it was only gathering dust.‌
— Tim Vine‌

Chris Sansom

unread,
Jun 24, 2019, 7:27:24 AM6/24/19
to prototype-s...@googlegroups.com
Hi again Walter

> On 23 Jun 2019, at 16:09, Walter Lee Davis <wa...@wdstudio.com> wrote:
>
> One of the best ways to work this out is to build a cut-down example, in one page, that tries to do what you're after here. If you can get that to fail, post it as a Gist or similar. From your description, you're going about things the right way.

I’ve followed your advice to make a stripped-down version of the problem (though I’m afraid I’ve no idea what a Gist is), and it answered one of my two problems: there was something wrong with my XML (it may have been as basic as the wrong Content-type), so js ignored the response. However, it still won’t insert the new code where I want it (or indeed anywhere). I’ve narrowed it down, I’m pretty sure, to a problem of scope - and I’m sure it’s basic stuff. I don’t need to bore you with the very simple initial html or the even simpler code executed by the Ajax call, because I know that’s all fine now, but here’s the relevant js:

////////////

var base = 'http://10.0.1.2:90/~dvds/';

// initialization routines
document.observe ('dom:loaded', function() {
// set up handler for variable numbers of selects
var addselects = $$('.addselect');
for (var i = 0; i < addselects.length; i++) {
var addselect = addselects[i];
addselect.onchange = newSelect;
}
});

// handler for adding new field to array
function newSelect() {
// var thisid = this.id;
var newhtml;

var url = base + 'ajaxtest';
// send request to do the business
var myAjax = new Ajax.Request (url, {
method: 'post',
onSuccess: function (req) {
var xml = req.responseXML;
var id = xml.getElementsByTagName('id')[0].firstChild.nodeValue;

if (id) {
newhtml = '\t\t<li>\r\t\t\t<select class="addselect" name="newlist" id="newlist" />\r\t\t\t\t<option value="" selected="selected"></option>\r';
// loop
var newid, newname;
var ids = xml.getElementsByTagName('id');
var names = xml.getElementsByTagName('name');
for (var i = 0; i < ids.length; i++) {
newid = ids[i].firstChild.nodeValue;
newname = names[i].firstChild.nodeValue;
newhtml += '\t\t\t\t<option value="' + newid + '">' + newname + '</option>\r';
}
newhtml += '\t\t\t</select>\r\t\t</li>\r';
// $('thisid').up('ul').insert (newhtml);
}
else {
alert (’ng');
newhtml = '<li>No good.</li>';
}
},
onFailure: function() {
alert ('Script failure.');
newhtml = '<li>No good.</li>';
}
});

// alert (newhtml);
if (newhtml) {
this.up('ul').insert (newhtml);
}
}

////////////

What I need to do is /either/ make the value of the original ‘this' available to the inner part of the code (the line that’s commented out immediately after the last html += …) /or/ make newhtml available to the pouter part of the code for the 'if (newhtml)’ block at the end. But I can’t figure out how to do it because it doesn’t meet my normal assumptions about global and local variables.

I really feel I’m almost there with this problem - just this last hurdle to get over!
‌Goodness is about what you do. Not who you pray to.‌
— Terry Pratchett‌

Walter Lee Davis

unread,
Jun 24, 2019, 5:51:59 PM6/24/19
to prototype-s...@googlegroups.com


> On Jun 23, 2019, at 12:22 PM, Chris Sansom <chris57...@gmail.com> wrote:
>
> Hi Walter
>
> Thanks for your long and thoughtful response… some of which I understood! (I did say I was rusty, and I was never extremely advanced - never got into OOP for instance unless it was laid out on a plate for me as prototype does).
>
> Just a quick question - do you know whether there’s some limit to the length of a string that can be returned via Ajax, and whether it can contain carriage returns, tabs or whatever? What I’m returning here is politely formatted and indented html with 76 options in the select.

Yup. That will stop a JavaScript insertion like you want to do cold. Try replacing all of the newlines [and carriage returns, if any] with a single space before you pass that value back to the Ajax request. It doesn't matter how long the line is, as long as it's one line. I generally substitute /[\n\r]+/ with ' '.

Walter
> To view this discussion on the web visit https://groups.google.com/d/msgid/prototype-scriptaculous/845B17E5-CF17-408D-AB00-5F4C827F8260%40gmail.com.

Chris Sansom

unread,
Jun 24, 2019, 6:25:25 PM6/24/19
to prototype-s...@googlegroups.com
On 24 Jun 2019, at 22:51, Walter Lee Davis <wa...@wdstudio.com> wrote:
>
> Yup. That will stop a JavaScript insertion like you want to do cold. Try replacing all of the newlines [and carriage returns, if any] with a single space before you pass that value back to the Ajax request. It doesn't matter how long the line is, as long as it's one line. I generally substitute /[\n\r]+/ with ' ‘.

Thanks, Walter, but I’ve just this minute found the problem, and it was, as I knew it would be, a stupid one. Where I had "$('thisid').up('ul').insert (newhtml);”, all I had to do was take the quotes away from round ‘thisid’! I was sending a literal string to $ instead of the value represented by the variable and, oddly enough, there isn’t an element in the DOM with the id ‘thisid’!

And btw, it does work with a few ‘\r's in it.

Thanks a million for your help. Now I have to fix the next problem...
‌As every parent of a small child knows, converting a large object into small fragments is considerably easier than the reverse process.‌
— Andrew S. Tanenbaum‌

Reply all
Reply to author
Forward
0 new messages