2

if I have this html

<div>
  <p>1</p>
</div>

<div>
  <p>1</p>
  <p>2</p>
</div>

and this jquery

$('div').each(function () {
    $(this).find('p').eq(1).html('test');
});

i get this

<div>
  <p>1</p>
</div>

<div>
  <p>1</p>
  <p>test</p>
</div>

what jquery should I use to get this instead?

<div>
  <p>1</p>
  <p>test</p>
</div>

<div>
  <p>1</p>
  <p>test</p>
</div>

there is no match for eq(1) on the first div so it just skips it. how can I add a child if none exists or update it if one does exist, without having to split the jquery into several statements?

kenwarner
  • 28,650
  • 28
  • 130
  • 173
  • What do you mean by `without having to split the jquery into several statements`? Are `if` statements forbidden? – Frédéric Hamidi Aug 07 '11 at 06:36
  • it is my understanding that 1 line of code is generally better than 5 lines of code if it does the same thing and is easily understandable. – kenwarner Aug 07 '11 at 06:39
  • Possible duplicate of [jQuery "get or create element" convenience method](https://stackoverflow.com/questions/7404575/jquery-get-or-create-element-convenience-method) – KyleMit Sep 19 '17 at 19:20

3 Answers3

2

The easiest way i found is this lets see someone else come with smaller solution

<div>
  <p>1</p>
</div>

<div>
  <p>1</p>
  <p>2</p>
</div>

$('div').each(function () {
    var elm= $(this).find('p').eq(1);
    if(elm.length==0)
    {
        $(this).find('p').after('test');
    }
    else
    {
        elm.html('test')
    }
});

http://jsfiddle.net/geBXF/

Praveen Prasad
  • 31,561
  • 18
  • 73
  • 106
  • yup, that's basically what i came up with too. i was hoping somebody a little more familiar with jquery than i am would know a more concise syntax – kenwarner Aug 07 '11 at 06:47
2

If your <div> elements contain at most two paragraphs, you can do:

$("div").each(function() {
    var $this = $(this);
    $this.find("p").add($("<p>")).eq(1).html("test").appendTo($this);
});

If there are more than two paragraphs, things get more complicated and you'll have to use insertAfter() instead of appendTo() to add the new element to the DOM:

$("div").each(function() {
    var $this = $(this);
    $this.find("p").add($("<p>")).eq(1).html("test")
         .insertAfter($this.find("p").eq(0));
});

You can see the results in this fiddle.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
1

In the interest of keeping a separation of concerns, here's an jQuery function that will either locate an existing element or create a new element.

So just include this jQuery anywhere:

$.fn.upsert = function(selector, htmlString) {
  // upsert - find or create new element
  // find based on css selector     https://api.jquery.com/category/selectors/
  // create based on jQuery()       http://api.jquery.com/jquery/#jQuery2
  var $el = $(this).find(".message");
  if ($el.length == 0) {
    // didn't exist, create and add to caller
    $el = $("<span class='message'></span>");
    $(this).append($el);
  }

  return $el;
}; 

Then you could use like this:

$("input[name='choice']").click(function(){
  var $el = $(this).closest("form").upsert(".message", "<span class='message'></span>");
  // guaranteed to have element - update if we want
  $el.html("You've chosen "+this.value)
})

Ideally, it would be nice to simply pass in a selector and have jQuery create an element that matches that selector automatically, but it doesn't seem like there's a light weight vehicle for creating an element based off the CSS Selector syntax (based on Question 1 & Question 2).

Some further enhancements might provide more fine tuned control for DOM insertion instead of just always using .append().

Demo in jsFiddle | Demo in Stack Snippets:

$.fn.upsert = function(selector, htmlString) {
  // upsert - find or create new element
  // find based on css selector    https://api.jquery.com/category/selectors/
  // create based on jQuery()       http://api.jquery.com/jquery/#jQuery2
  var $el = $(this).find(".message");
  if ($el.length == 0) {
    // didn't exist, create and add to caller
    $el = $("<span class='message'></span>");
    $(this).append($el);
  }

  return $el;
}; 

$("input[name='choice']").click(function(){
  var $el = $(this).closest("form").upsert(".message", "<span class='message'></span>");
  // guaranteed to have element - update if we want
  $el.html("You've chosen "+this.value)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<form action="">
  <div>
    <input type="radio" id="ca" name="choice" value="A" /><label for='ca'>A</label>
    <input type="radio" id="cb" name="choice" value="B" /><label for='cb'>B</label>
    <input type="radio" id="cc" name="choice" value="C" /><label for='cc'>C</label>
  </div>
</form>
KyleMit
  • 30,350
  • 66
  • 462
  • 664