5

I'm trying to wrap <label>, <input type="text" />, and <select> elements with <div class="formItem"></div> to fix some positioning on a form that I can't directly edit. For some reason, when I try to use the following code:

$("label").before("<div class=\"formItem\">");
$("input[type=text]").after("</div>");
$("select").after("</div>");

It doesn't work. It just adds <div class="formItem"></div> before every <label> which doesn't help me.

I've taken a look at .wrap(); but it isn't clear how I can use that to wrap multiple elements like I'm trying to do. Here's an example of the HTML markup:

<label>Text Box</label>
<input type="text" name="Text Box" />

<label>Select Menu</label>
<select name="Select Menu">
    <option>Example</option>
</select>

There's about 10 sets, 9 text boxes, and 1 select box if that matters.

4b0
  • 21,981
  • 30
  • 95
  • 142
JacobTheDev
  • 17,318
  • 25
  • 95
  • 158
  • ` – j08691 Mar 18 '13 at 21:16
  • 2
    The DOM simply doesn't work that way. you can't append a *part* (such as just the opening or just the closing tag) of an element because an element doesn't really have parts. It's a single object that has properties. – Kevin B Mar 18 '13 at 21:19
  • It does have for elements, I just put in some relevant code. The question isn't about for elements, it's about wrapping things. – JacobTheDev Mar 18 '13 at 21:21
  • 1
    Right, but you can't wrap something by appending part of an element before and part after. you have to create a new element and move the target elements into it. jQuery's `.wrap` method (and other similar wrapping methods) does that for you. – Kevin B Mar 18 '13 at 21:22

3 Answers3

7

That's just not how the jQuery API works. At all.

.before():

Insert content, specified by the parameter, before each element in the set of matched elements.

.after():

Insert content, specified by the parameter, after each element in the set of matched elements.


Don't think in terms of open- and close-tags. Think in terms of actual elements.

You are probably looking for .wrapAll().

$('label').each(function () {
    $(this).next('input, select').andSelf().wrapAll('<div class="formItem"/>');
});
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Thank you, that worked. I think I get why mine was wrong (although some of this more nuanced jQuery logic goes over my head). I'll accept in 5 minutes when it'll let me. – JacobTheDev Mar 18 '13 at 21:25
1

With jquery, you can only add complete elements. That is, they must include the end tags. This is because what you are really doing is calling document.createElement('div'). So to solve this problem, keeping as close to the original code as possible, just use:

var newEl = $("select").after("<div class=\"formItem\"></div>");
$("input[type=text]").appendTo(newEl);
$("select").appendTo(newEl);
Jeremy Blalock
  • 2,538
  • 17
  • 23
  • Good explanation as to why mine doesn't work, thank you. That code doesn't appear to work however. – JacobTheDev Mar 18 '13 at 21:20
  • "With jquery, you can only add complete elements. That is, they must include the end tags." Sorry, not true. http://stackoverflow.com/questions/9675487/element-vs-element-in-jquery – j08691 Mar 18 '13 at 21:22
  • @j08691 that's reading a bit literally. It just so happens that jQuery's element-creation method is liberal in the syntax it accepts. – Matt Ball Mar 18 '13 at 21:23
1

Try this:

$('label').next().andSelf().wrap("<div class=\"formItem\">");

It will enclose label elements plus its following sibling in a div.formItem.

Nelson
  • 49,283
  • 8
  • 68
  • 81
  • This is close, but the `.andSelf()` doesn't appear to work. I'll fiddle with it and see if I can figure it out. – JacobTheDev Mar 18 '13 at 21:19
  • If you're using jQuery 1.9, it's been renamed to `.addBack()` – Kevin B Mar 18 '13 at 21:20
  • `.wrap()` is still the wrong method to use here. OP needs `.wrapAll()`. Also, this won't wrap a `
    ` around each label/input or label/select pair.
    – Matt Ball Mar 18 '13 at 21:21