1

This is a fragment of an html doc my app has generated from a webserver log file:

<div class='ip' id='220.181.108.95'>
  <h3>220.181.108.95</h3>
  <h4 class='records' id='220.181.108.95'>
    Records:
  </h4>
  <div class='records' id='220.181.108.95'>
    <div class='record' id='220.181.108.95'>
      <p>ip: 220.181.108.95 timestamp: 15/Jun/2011:21:21:14 +0100 access_request_type: GET page: / status_code: 200 </p>
    </div>

    <div class='record' id='220.181.108.95'>
      <p>ip: 220.181.108.95 timestamp: 13/Jun/2011:08:18:09 +0100 access_request_type: GET page: / status_code: 200 </p>
    </div>

    <div class='record' id='220.181.108.95'>
      <p>ip: 220.181.108.95 timestamp: 12/Jun/2011:13:23:11 +0100 access_request_type: GET page: / status_code: 200 </p>
    </div>
  </div>
</div>

<div class='ip' id='123.125.71.60'>
  <h3>123.125.71.60</h3>
  <h4 class='records' id='123.125.71.60'>
    Records:
  </h4>
  <div class='records' id='123.125.71.60'>
    <div class='record' id='123.125.71.60'>
      <p>ip: 123.125.71.60 timestamp: 11/Jun/2011:04:19:31 +0100 access_request_type: GET page: /stylesheet.css status_code: 200 </p>
    </div>  
  </div>
</div>

Here is some JQuery I've been using to toggle the records div:

<script>
      $(document).ready(function(){
            $("h4.records").click(function(){
                  var id = $(this).attr("id");
                  $("div#".concat(id, ".records")).toggle();
            });
      });
</script>

I've two problems with the way it's functioning:

  1. It only works when the id is a letter or string of letters. Numbers and dots stop the toggle working. Replace the id's with "a" and "b" respectively and it toggles.
  2. It may toggle, but then it toggles all divs with a class of "record:, even though I've passed in elem#id.class as the selector to toggle.

Is there a way I can keep the id as the ip-address (which is the obvious unique reference for the record) and toggle only the exact div by class and id and not all divs with that class?

Any help is much appreciated. I know several languages but it's rare I write in javascript, and I've not used JQuery before.

JQuery version is 1.6.1, JQueryUI is also being loaded, version 1.8.13.


Some quick notes:

  1. Pretty much everyone had a hand in getting me to the code I'm using now, so it's unfortunate I can only pick one correct answer.
  2. @Liangliang Zheng's answer works, but didn't work once I added in a "hide on load" javascript function, so @Matt Ball gets the tick for a more flexible solution.
  3. I've started to use a custom attribute for the ip address, thanks to @Dhruva Sagar and @Liangliang Zheng for suggesting it.
  4. Ip addresses elsewhere are now prefixed, thanks to @James Khoury for that suggestion.
  5. I've no idea why anyone would get downvoted for their answers, they're all at least partially correct.

I've put the solution to work in an app for reading webserver access logs and checking their geographical info via the ip, the code is up on Github if anyone's interested https://github.com/yb66/GeoIPalise. I'll give everyone here props with the next git push! Thanks to you all!

ian
  • 12,003
  • 9
  • 51
  • 107

5 Answers5

4

The problem is twofold:

  1. As I commented on the question, you're trying to select an element by ID when that ID contains characters which have special meaning. You need to escape those characters, or use document.getElementByID.
  2. As @Dhruva points out, the DOM must not contain elements with duplicate IDs. Use a class instead, or strip out the duplicate IDs entirely. Once the DOM contains more than one element with a given ID, all bets are off, and Cthulhu will probably be summoned shortly thereafter.

All that said, you can just use .closest() and forget about element IDs entirely.

HTML

<div class='ip' id='220.181.108.95'>
  <h3>220.181.108.95</h3>
  <h4 class='records'>
    Records:
  </h4>
  <div class='records'>
    <div class='record'>
      <p>ip: 220.181.108.95 timestamp: 15/Jun/2011:21:21:14 +0100 access_request_type: GET page: / status_code: 200 </p>
    </div>

    <div class='record'>
      <p>ip: 220.181.108.95 timestamp: 13/Jun/2011:08:18:09 +0100 access_request_type: GET page: / status_code: 200 </p>
    </div>

    <div class='record'>
      <p>ip: 220.181.108.95 timestamp: 12/Jun/2011:13:23:11 +0100 access_request_type: GET page: / status_code: 200 </p>
    </div>
  </div>
</div>

<div class='ip' id='123.125.71.60'>
  <h3>123.125.71.60</h3>
  <h4 class='records'>
    Records:
  </h4>
  <div class='records'>
    <div class='record'>
      <p>ip: 123.125.71.60 timestamp: 11/Jun/2011:04:19:31 +0100 access_request_type: GET page: /stylesheet.css status_code: 200 </p>
    </div>  
  </div>
</div>

JavaScript

$(function(){
    $("h4.records").live('click', function(){
        $(this).closest('div.ip').find('div.records').toggle();
    });
});

Other WTFs:

(No personal offense meant. It's the code, not you.)

Demo: http://jsfiddle.net/mattball/fE9Xf/

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Right, my mistake was a) to think that it's the combination of class and id that was the unique selector, not just id. That's my misunderstanding / lack of knowledge cleared up, and b) to stay up too late writing code to see my own mistakes! Many thanks. – ian Jun 16 '11 at 04:03
  • You're welcome, I've definitely been there. Go to sleep and you'll spin code into gold in the morning. – Matt Ball Jun 16 '11 at 04:06
  • I was using `concat` because of the multiple args, using `+` more than once is a bit icky - why doesn't javascript having interpolation? It's so much cleaner! – ian Jun 16 '11 at 04:15
  • At any rate, just use `.closest()` like I suggested and it's a moot point. – Matt Ball Jun 16 '11 at 04:15
1

In HTML multiple nodes can't have the same 'id'. That is the problem here. Instead of id, you should use some other attribute, perhaps a data-id and it should work then.

Dhruva Sagar
  • 7,089
  • 1
  • 25
  • 34
1

The issue is that when you are try to select $('#220.181.108.95'), what jQuery is querying the DOM for is:

Element with id of 220 and classes 181, 108 and 95
eg. <div id="220" class="181 108 95"></div>

To fix this, you need to escape the periods in the selector. To do this, you need to add a \\, as per: http://api.jquery.com/category/selectors/

So your selector needs to be $('#220\\.181\\.108\\.95')

You can do this easily by using a replace().

id.replace('.','\\.');
See edit...

Also, id's should be unique.

EDIT:

As Matt Bell links to, there is a better and preferred way to do this (which I did not know)

function jq(myid) { 
   return '#' + myid.replace(/(:|\.)/g,'\\$1');
}

would be used:

$( jq('220.181.108.95') )

http://docs.jquery.com/Frequently_Asked_Questions#How_do_I_select_an_element_by_an_ID_that_has_characters_used_in_CSS_notation.3F

Alastair Pitts
  • 19,423
  • 9
  • 68
  • 97
  • That is not the best regexp solution to use. http://docs.jquery.com/Frequently_Asked_Questions#How_do_I_select_an_element_by_an_ID_that_has_characters_used_in_CSS_notation.3F – Matt Ball Jun 16 '11 at 03:41
  • @Matt Bell: Thanks for that, I didn't know that existed. – Alastair Pitts Jun 16 '11 at 03:44
1

I would recommend you to use custom attribute instead of id.

<div class='record' ip='1.2.3.4'><p></p></div>

Therefore, in your js, you can :

$('div.record[ip="' + target_ip + '"]').toggle();
Liangliang Zheng
  • 1,763
  • 11
  • 16
0

http://www.w3.org/TR/html40/types.html#type-name

Id's must start with a letter. From the w3c:

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 (".").

I suggest you add "ip" to the beginning of the id's (e.g id='ip220.181.108.95')

EDIT as noticed by some others that you have multiple id's i suggest you name the first one with an id and the child records can be access in jQuery by

jQuery("div#id .records").toggle();

this will toggle all elements inside your div with the specified id.

James Khoury
  • 21,330
  • 4
  • 34
  • 65
  • This restriction is lifted in HTML5, and regardless, is not the source of the OP's problem. – Matt Ball Jun 16 '11 at 03:37
  • 1
    there is no mention of html5 in the OP – James Khoury Jun 16 '11 at 03:38
  • thanks for the w3 link, reading it has prompted me to substitute the dots for underscores, and I'll prepend "ip" too. – ian Jun 16 '11 at 04:00
  • @Iain if you are looking at HTML5 and CSS3 i'd suggest you read some sites like html5Rocks and css3info (just google them) and se some awesome stuff coming out of the w3c these days. – James Khoury Jun 16 '11 at 04:03