views:

66

answers:

4

Note: If you're 'just' a jQuery developer some things in this post may look a tad complex (Base62 encoding etc.) - it's really not. Although the more technical details are relevant to the question, the core is that jQuery won't select stuff with capitals. Thanks!

Hi folks!

So I have a list generated by Ajax. When you click the list's title, it's ID is sent and the list item appears alongside it. Standard stuff.

Since we're using an auto_increment ID, we don't want the users knowing how many submissions there are in the database. So, I'm encoding it into Base62, then decoding back again. [Note that this is - or, should be, irrelevant to the problem].

So as my list is generated, this code is output. We're using CodeIgniter PHP alongside the jQuery - this is in a loop of database results. $this->basecrypt->encode() is a simple CI library to convert an integer (the ID) to Base62:

$('#title-<?php echo $this->basecrypt->encode($row->codeid); ?>').click(function() {
        alert("clicked");
        [...]

And then, further down the page:

<div id="title-<?php echo $this->basecrypt->encode($row->codeid);?>" class="title">

As you can see, this is all generated in the same loop - and viewing the outputted source code shows, for example:

$('#title-1T').click[...] and then <div id="title-1T" [...]

So, jQuery shouldn't have any trouble, right? It was all working fine until we started Base62-ing the IDs. I believe that jQuery can't/won't select our IDs when they contain capital letters.

Now forgive me if I'm wrong - I am, relatively speaking, fairly new to jQuery - but to test my point I changed my $this->basecrypt->encode() into Base36. Before, it was using 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ After, it was using 0123456789abcdefghijklmnopqrstuvwxyz

With no capital letters, jQuery could select (and show the alert for testing purposes) just fine.

So what can I do? Is it safe for me to continue just using numbers and lowcase letters, in Base36 - and if so, what's the maximum integer size this can go up to? If not, what can I do about jQuery's problematic selection process?

Thanks!

Jack

EDIT: Included below is some example code from the page.

This is a part of the script returned in the file ajaxlist.php - it's called from Ajax and appears a couple of seconds after the page loads. I added in alert("clicked"); right near the beginning to see if that would appear - sadly, it doesn't... $(document).ready(function() {

    $('#title-<?php echo $this->basecrypt->encode($row->codeid); ?>').click(function() {
        alert("clicked");
        var form_data = {
            id: <?php echo $this->basecrypt->encode($row->codeid); ?>
        };

        $('.resultselected').removeClass('resultselected');
        $(this).parent().parent().addClass('resultselected');

        $('#col3').fadeOut('slow', function() {
            $.ajax({
                url: "<?php echo site_url('code/viewajax');?>",
                type: 'POST',
                data: form_data,
                success: function(msg) {
                    $('#col3').html(msg);
                    $('#col3').fadeIn('fast');
                }
        });
        });
    });
}); 
</script>

Also returned from the same file, at the same time as the code above (just beneath it) is this:

<div class="result">

    <div class="resulttext">

        <div id="title-<?php echo $this->basecrypt->encode($row->codeid);?>" class="title">
            <?php echo anchor('#',$row->codetitle); ?>
        </div>   [.......]

If this helps anymore, let me know!


EDIT 2: ACTUAL OUTPUT RETURNED TO THE BROWSER.

This was taken from Firebug, and is the returned data (Ajax) to the browser:

    <script type="text/javascript">
    $(document).ready(function() {

        $('#title-1T').click(function() {
            alert("clicked");

            var form_data = {
                id: 1T      };

            $('.resultselected').removeClass('resultselected');
            $(this).parent().parent().addClass('resultselected');

            $('#col3').fadeOut('slow', function() {
                $.ajax({
                    url: "http://localhost:8888/code/viewajax",
                    type: 'POST',
                    data: form_data,
                    success: function(msg) {
                        $('#col3').html(msg);
                        $('#col3').fadeIn('fast');
                    }
            });
            });
        }); 
    }); 
    </script>

    <div class="result">

        <div class="resulttext">

<div id="title-1T" class="title">

                <a href="http://localhost:8888/#"&gt;&lt;p&gt;This is an example </p></a>        </div>`

            <div class="summary">
                gibberish summary text      </div>

            <div class="bottom">


                <div class="author">
                    by <a href="http://localhost:8888/user/7/author"&gt;author&lt;/a&gt;         </div>

                <div class="tagbuttoncontainer">
                                        <div class="tagbutton listv">
                                                    <span>tag1</span>
                        </div>  
                                </div>

                <!-- Now insert the rating system -->
                <div class="ratingswrapper">

                    <p>4.0</p> 
                </div>

            </div>

        </div>

    </div>

Come on - you cannot say that shouldn't work... can you?!

+1  A: 

Why does

<div id="title" id="1T" [...]

contain two ids?

Joel McCracken
Sorry! That was an honest mistake, just a typo. I've fixed up the question.
Jack Webb-Heller
+2  A: 

I don't think that jQuery is the problem.

Please double-check that the IDs you generate are unique to the page and conform to the definition of ID tokens:

ID and NAME tokens must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens ("-"), underscores ("_"), colons (":"), and periods (".").

Also do a validity test of your output HTML to make sure your HTML is not broken in places where you did not look.

Tomalak
I'll go one better and say that I'm 100% positive that jQuery isn't the problem!
Pointy
@Pointy: Yeah, I'm also sure it isn't. The error must be somewhere else.
Tomalak
Well the ID definitely conforms, as they will always follow the structure "title-[something here]". The only Base62 characters will be 0-9, a-z, and A-Z, so it definitely complies.
Jack Webb-Heller
And, I just validated - no errors. Hmmm...
Jack Webb-Heller
@Jack: hm. I think revealing a simplified, but still non-working code sample might be necesary.
Tomalak
OK Tomalak, I've added some more to my original question :)
Jack Webb-Heller
@Jack: Close, but no cigar. :) Better add a sample of the generated HTML, this is much easier to debug than the code that *generates* the HTML.
Tomalak
That IS the generated HTML! Or, do you mean $this->basecrypt?
Jack Webb-Heller
Oh, wait, I see what you mean now... Well, the generated HTML is prettymuch exactly the same, except the PHP echoes are replaced with something Base62-ed like '1T' or '3S' or something. It always matches up with the jQuery selector, mind you.
Jack Webb-Heller
+1  A: 

This doesn't solve your specific issue, but would couldn't you just do something like this:

$('#container-of-the-divs div[id^=title-]').click(function(e) {
    alert('Clicked div with ID: ' + e.target.id);
});

You could also just add a class to these elements and select that instead. If you're looking for performance with a ton of items, you could also add the click event onto a parent item, and then do an if statement inside which would create only one event listener, instead of N event listeners. Example:

$('#container-of-the-divs').click(function(e) {
    if (e.target.id.substring(0, 6) == 'title-') {
        alert('Clicked div with ID: ' + e.target.id);
    }
});

Or you could just check if $(e.target).hasClass() like mentioned before.

Updated: Here is a working example based off the code you gave:

<div class="result">
    <div class="resulttext">
        <div id="title-A0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" class="title">
            <a href="#">A0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</a>
        </div>
        <div id="title-B0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" class="title">
            <a href="#">B0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</a>
        </div>
        <div id="title-C0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" class="title">
            <a href="#">C0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</a>
        </div>
        <div id="title-D0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" class="title">
            <a href="#">D0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</a>
        </div>
    </div>
</div>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"&gt;&lt;/script&gt;
<script type="text/javascript">
$('div.resulttext div.title').click(function() {
    $i = $(this);
    alert('Clicked div with ID: ' + $i.attr('id'));
});
</script>
William
Just tried your first snippet. Well, this is a bit strange, it won't detect that happening either...?!
Jack Webb-Heller
@Jack Webb-Heller: Yeah there was a typo, I updated both snippets and they both worked for me.
William
Ouch! Tried them both after your correction. Still no luck :'(
Jack Webb-Heller
OK, I've posted some more examples to my original question.
Jack Webb-Heller
@Jack Webb-Heller: Updated my answer with a working example.
William
This just keeps getting stranger. Works perfectly and as expected with the example. Works perfectly and as expected when encoding in Base36. But, epicly fails when encoding in Base64...?!
Jack Webb-Heller
@Jack Webb-Heller: Probably because base64 usually has at least one "=" at the end of the string, which as stated above isn't valid as an ID. base36 is only a-z0-9.
William
@Jack Webb-Heller: Why don't you just post the exact output, and remove content that is confidential, but the html structure/id values alone.
William
But, there's no "=" output anywhere! An example would look exactly like this - <div id="title-1T" [...] - no equals anywhere. And the allowed characters I've set for Base62 (sorry, I meant Base62, if I said Base64 it's because I'm all coded-out ;) is a-z, A-Z, and 0-9...
Jack Webb-Heller
@William, I can't post the exact output as it's generated via Ajax and Right-Click->View Source doesn't show that. Sorry for the amateur reply, but, Firebug won't output <script> tags either, so a Copy-Paste from Firebug seems off the agenda...
Jack Webb-Heller
and if you remove the encoding, it works fine? with the CURRENT version?
William
OK, sorry! I got the output from Firebug's XHR. I'll append it to my question now :)
Jack Webb-Heller
And, @William, yep, removing the capital A-Z encoding (since my IDs aren't high enough for letters yet) it therefore only generates numbers - and that works fine. With the latest jQuery...
Jack Webb-Heller
What do you mean by removing the capital A-Z, you're taking the encoded output, and then removing any A-Z characters? Or you're just outputting the ID's (without encoding them)
William
OK William, I've added some actual Outputted data to the second edit of my question.What I mean is, base62's characters are 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. Base36's characters are 0123456789abcdefghijklmnopqrstuvwxyz - just the same, without capital A-Z included as character options. So, I mean I'm sending the integer ID to be encoded using 0-9 and a-z, not 0-9,a-z AND A-Z. Makes sense?
Jack Webb-Heller
@Jack Webb-Heller: You're re-applying the click event on the newly pulled HTML right?
William
@Jack Webb-Heller: Also, your document.ready() method will not fire when the AJAX is pulled as the document has already been loaded, since you're pulling it via AJAX just put the JavaScript code under the div element and have it execute right instantly.
William
Um, yep... I think so? The jQuery code is sent along with the pulled HTML at the same time, in the same loop, so the values will be the same. I've also tried jQuery's live() thinking that might make a difference, but, nope...
Jack Webb-Heller
OK, I'll try your last suggestion now! I was using that previously though with no luck, but, hey, things might be different second time round...
Jack Webb-Heller
Nope! Still no luck! This is ridiculous... :S
Jack Webb-Heller
@Jack Webb-Heller: Well the method isn't going to be fired with it there, so even if it didn't fully solve the problem, if you did find the problem before, you wouldn't have known because the event was never being fired at all. Make sure your JavaScript is after the HTML.
William
FHDUOASE`P1! etc. placed Javascript definitely after the HTML. nothing's happening still. :'(
Jack Webb-Heller
sorry for my impatient tone, only, I've been stuck with this problem for 4 hours...
Jack Webb-Heller
If I were you, I'd start commenting a large amount of your code out one by one until you find the problem. First, make your "click" event ONLY contain the alert(), comment the rest out. Do an alert BEFORE the click event, to make sure the AJAX is pulling the data and executing the data correctly.
William
OK - so, I'm in the UK, and it's nearly midnight here. I need some sleep or I'll go mad. Just briefly, I tried doing what you suggested. I commented out EVERYTHING between <script>, but added an alert("Hello!") to the top. That worked fine. Then, I kept the alert in - at the top, mind you - and uncommented the rest. The alert never fires... But, I don't understand why not, as surely it should fire before processing anything else, so providing it's before the jQuery part it's treated irregardlessly of it?
Jack Webb-Heller
Thanks, William, for having the patience of a saint!
Jack Webb-Heller
It's most likely because there is an error somewhere, so JavaScript is "dieing". Check your error logs in Firefox. Good night.
William
+4  A: 

Ok I found your problem... you need to put quotes around the variable in the form_data definition (demo):

    var form_data = {
        id: "<?php echo $this->basecrypt->encode($row->codeid); ?>"
    };

I also had to add a return false; so the demo doesn't try to follow the link

fudgey
+1 - finally!!!
Anurag
:O - wow... thanks so much Fudgey! I mean, I guess looking back on it now it's a fairly simple one, but I would've never thought of that myself... just tried it on my source and it works perfectly. You, sir, are brilliant!
Jack Webb-Heller