views:

149

answers:

9

Hi guys,

as the title says, I keep getting "undefined" when I try to get the id attribute of an element, basically what I want to do is replace an element with a input box when the value is "other".

Here is the code:

function showHideOther(obj){ 

    var sel = obj.options[obj.selectedIndex].value;
    var ID = $(this).attr("id");

    alert(ID);


    if(sel=='other'){ 

        $(this).html("<input type='text' name='" + ID + "' id='" + ID + "' />");

    }else{
        $(this).css({'display' : 'none'});
    }
}  

The HTML:

          <span class='left'><label for='race'>Race: </label></span>
          <span class='right'><select name='race' id='race' onchange='showHideOther(this);'>
            <option>Select one</option>
            <option>one</option>
            <option>two</option>
            <option>three</option>
            <option value="other">Other</option>
          </select>
          </span>

It is probably something small that I am not noticing, what am I doing wrong?

Thanx in advance!

+1  A: 

What are you expecting $(this) to refer to?

Do you mean sel.attr("id"); perhaps?

Daniel Earwicker
A: 

In the function context "this" its not referring to the select element, but to the page itself

  • Change var ID = $(this).attr("id"); to var ID = $(obj).attr("id");

If obj is already a jQuery Object, just remove the $() around it.

GerManson
+3  A: 

your using this in a function, when you should be using the parameter.

You only use $(this) in callbacks... from selections like

$('a').click(function() {
   alert($(this).href);
})

In closing, the proper way (using your code example) would be to do this

obj.attr('id');

Zane Edward Dockery
"this" will always refer to the object you are working ON. And it can be used anywhere. Of course, if you know what you are doing.
GerManson
instead of `$(this).href`, you could just `this.href` and also `obj.id`... btw, `obj.attr('id');` should be `$(obj).attr('id');` based on OP...
Reigel
+1  A: 

Change

var ID = $(this).attr("id");

to

var ID = $(obj).attr("id");

Also you can change it to use jQuery event handler:

$('#race').change(function() {
    var select = $(this);
    var id = select.attr('id');
    if(select.val() == 'other') {
        select.replaceWith("<input type='text' name='" + id + "' id='" + id + "' />");
    } else {
        select.hide();
    }
});
fantactuka
obj seems like it's already a jQuery object.
efritz
If it's passed from the onchange="" attribute it will be a reference to HTMLElement, not a jQuery object
fantactuka
+1  A: 

Remove the inline event handler and do it completly unobtrusive, like

​$('​​​​#race').bind('change', function(){
  var $this = $(this),
      id    = $this[0].id;

  if(/^other$/.test($(this).val())){
      $this.replaceWith($('<input/>', {
          type: 'text',
          name:  id,
          id: id
      }));
  }
});​​​
jAndy
+1 for unobtrusive approach!
Igor Zinov'yev
Why...........?
Tim Down
It's much cleaner and maintainable
fantactuka
While this code may be 'cleaner and maintainable', it doesn't answer the OP in an explantory way.
belugabob
@belugabob: which really is a good reason to downvote!
jAndy
Whether the "unobtrusive" version is cleaner and more maintainable or not depends on the size of the application and, to an extent, personal taste. As an approach, it has significant downsides. For example, if a user makes a selection from the list before the document has finished loading, the event handler will not have been unobtrusively added and will not be called.
Tim Down
@Tim Down: You can't believe that for real. If you have such kind of worries just load the external `js code` in your document `head`.
jAndy
I'm not sure what you mean. I'm not referring to loading the JavaScript. The usual technique for adding event listeners unobtrusively is to do it once the document has loaded. My point is that the user may well be able to interact with the document before it has fully loaded.
Tim Down
@Tim Down: I can't see the "significant downside" then. What would the user be able to interact with (or to do anything in general) if the page isn't fully loaded yet?
jAndy
Sections of the page that have already loaded. The browser doesn't wait for the whole DOM to download before starting to render it. Try it: create a page on your server that sends some HTML, flushes the response and then waits for a while before serving the remainder of the page.
Tim Down
Here's a relatively old but interesting article on the subject: http://peter.michaux.ca/articles/the-window-onload-problem-still
Tim Down
@Tim Down: Well I'm aware of that article (it really is interesting). But still, writting unobtrusive code is a good thing. Assigning `inline javascript` can also cause a lot of trouble. What if a user clicks on an element with an inline handler, but the according javascript isn't loaded yet, boom error. So I'd prefer to have no reaction instead of an error here. And the maintainabilty is just so big, instead of checking for errors in two places (`html + js`) is just bad karma to me.
jAndy
The you put the relevant JavaScript somewhere in the page before the element with the inline handler. Inside `<head>` would do. Bosh, no error. Really, I'm not trying to say you should always use event handler attributes, particularly for larger projects, and in fact I only use them myself in a limited number of situations. I'm just trying to encourage thinking for oneself rather than jumping straight onto the bandwagon of unobtrusiveness and pointing out a way in which the "unobtrusive" approach works less well than the alternative.
Tim Down
A: 

I recommend you to read more about the this keyword.

You cannot expect "this" to select the "select" tag in this case.

What you want to do in this case is use obj.id to get the id of select tag.

Hrishi
A: 

You could also write your entire function as a jQuery extension, so you could do something along the lines of `$('#element').showHideOther();

(function($) {
    $.extend($.fn, {
        showHideOther: function() {
            $.each(this, function() {
                var Id = $(this).attr('id');
                alert(Id);

                ...

                return this;
            });
        }
    });
})(jQuery);

Not that it answers your question... Just food for thought.

efritz
A: 

You can do

onchange='showHideOther.call(this);'

instead of

onchange='showHideOther(this);'

But then you also need to replace obj with this in the function.

David
+1  A: 

Because of the way the function is called (i.e. as a simple call to a function variable), this is the global object (for which window is an alias in browsers). Use the obj parameter instead.

Also, creating a jQuery object and the using its attr() method for obtaining an element ID is inefficient and unnecessary. Just use the element's id property, which works in all browsers.

function showHideOther(obj){ 
    var sel = obj.options[obj.selectedIndex].value;
    var ID = obj.id;

    if (sel == 'other') { 
        $(obj).html("<input type='text' name='" + ID + "' id='" + ID + "' />");
    } else {
        $(obj).css({'display' : 'none'});
    }
}
Tim Down