views:

12653

answers:

5

I have some html like this:

<div id="1">
    <p>
     Volume = <input type="text" />
     <button rel="3.93e-6" class="1" type="button">Check answer</button>
    </p>
    <div></div>
</div>

and some JS like this:

$("button").click(function () {
    var buttonNo = $(this).attr('class');
    var correct = Number($(this).attr('rel'));

    validate (Number($("#"+buttonNo+" input").val()),correct);
    $("#"+buttonNo+" div").html(feedback);
});

What I'd really like is if I didn't have to have the class="1" on the button (I know numeric classes aren't valid, but this is a WIP!), so I could determine buttonNo based on the id of the parent div. In real life there are multiple sections looking like this.

  1. How do I find the id of the div parenting the button.

  2. What would be a more semantic way to store the answer in the button code. I want to make this as foolproof as possible for a non programmer to copy and paste without breaking things!

+2  A: 

To get the id of the parent div:

$(buttonSelector).parents('div:eq(0)').attr('id');

Also, you can refactor your code quite a bit:

$('button').click( function() {
 var correct = Number($(this).attr('rel'));
 validate(Number($(this).siblings('input').val()), correct);
 $(this).parents('div:eq(0)').html(feedback);
});

Now there is no need for a button-class

explanation
eq(0), means that you will select one element from the jQuery object, in this case element 0, thus the first element. http://docs.jquery.com/Selectors/eq#index
$(selector).siblings(siblingsSelector) will select all siblings (elements with the same parent) that match the siblingsSelector http://docs.jquery.com/Traversing/siblings#expr
$(selector).parents(parentsSelector) will select all parents of the elements matched by selector that match the parent selector. http://docs.jquery.com/Traversing/parents#expr
Thus: $(selector).parents('div:eq(0)'); will match the the first parent div of the elements matched by selector.

You should have a look at the jQuery docs, particularly selectors and traversing:

Pim Jager
Simple and easy! Why the div:eq(0)? What's that mean?
Rich Bradshaw
I think the 'div:eq(0)' will be wrong if this is all wrapped in at least one div, because you're doing an absolute traversal of the DOM rather than a relative one. Which would be fine if the first element were the "closest" parent, but this suggests otherwise: http://tinyurl.com/bbegow
Robert Grant
(hence my use of parent() instead)
Robert Grant
+5  A: 

Try this:

$("button").click(function () {
    $(this).parents("div:first").html(...);
});
Buu Nguyen
+3  A: 

1.

$(this).parent().attr("id");

2.

There must be a large number of ways! One could be to hide an element that contains the answer, e.g.

<div>
    Volume = <input type="text" />
    <button type="button">Check answer</button>
    <span style="display: hidden">3.93e-6&lt;/span>
    <div></div>
</div>

And then have similar jQuery code to the above to grab that:

$("button").click(function () 
{
    var correct = Number($(this).parent().children("span").text());
    validate ($(this).siblings("input").val(),correct);
    $(this).siblings("div").html(feedback);
});

bear in mind that if you put the answer in client code then they can see it :) The best way to do this is to validate it server-side, but for an app with limited scope this may not be a problem.

Robert Grant
I have no idea how to format HTML in this editor, but this seems a rather simpler way to grab the parent than Pim's ('div:eq(0)') stuff; unless I'm missing a subtlety here then html elements all have one direct parent, which you just get with parent().
Robert Grant
parent will indeed return the closest parent, however, what happens if he decides that he wants to add a fieldset or something around the question but inside the Div.
Pim Jager
Actually in his own example this will return the <p>
Pim Jager
Indeed, if more divs add in then the 'div:eq(0)' could be wrong. Change the structure, change the code that parses it :)And yeah I simplified the example to the minimum html necessary for the example; it should call parent twice.
Robert Grant
+1  A: 

JQUery has a .parents() method for moving up the DOM tree you can start there.

If you're interested in doing this a more semantic way I don't think using the REL attribute on a button is the best way to semantically define "this is the answer" in your code. I'd recommend something along these lines:

<p id="question1">
    <label for="input1">Volume =</label> 
    <input type="text" name="userInput1" id="userInput1" />
    <button type="button">Check answer</button>
    <input type="hidden" id="answer1" name="answer1" value="3.93e-6" />
</p>

and

$("button").click(function () {
    var correctAnswer = $(this).parent().siblings("input[type=hidden]").val();
    var userAnswer = $(this).parent().siblings("input[type=text]").val();
    validate(userAnswer, correctAnswer);
    $("#messages").html(feedback);
});

Not quite sure how your validate and feedback are working, but you get the idea.

Parrots
+20  A: 

You could use event delegation on the parent div. Or use the closest method to find the parent of the button.

The easiest of the two is probably the closest.

var id = $("button").closest("div").attr("id");
Mark
+1 Thanks you for recommending closest, everyone always recommends parent or parents...
bendewey
I really like closest because you can do something like .closest("div.foo") and code is not tied to the structure.
Mark
LIke the use of closest here!
Rich Bradshaw