2

I have the following mockup (here's a jsbin: http://jsbin.com/vadutezaci/1/edit?html,css,output):

<div class='jt-header'>a header 1</div>
<div class='jt-detail'>a detail 1
  <div class='jt-item-price'>12.34</div>
</div>
<div class='jt-header'>another header 2<div class='jt-item-price'>34.45</div></div>
<div class='jt-detail'>another detail 2
</div>

Basically, if a jt-item-price is within a jt-detail, I'd like to append to the preceding jt-header; this is the first couplet above. If it is within a jt-header already, leave as is (the second couplet) so that the above becomes:

<div class='jt-header'>a header  <div class='jt-item-price'>12.34</div>
</div>
<div class='jt-detail'>a detail
</div>
<div class='jt-header'>another header<div class='jt-item-price'>34.45</div></div>
<div class='jt-detail'>another detail
</div>

How would I do this? I would think detach().appendTo() but how would I detect that the jt-item-price is within a detail and how would I tell it to the preceding jt-header (there might be intervening non-jt-header elements)?

Edit #1

A more realistic example which seems to break using next():

<div class='jt-row'>
<div class='jt-header'>a header 1</div>
</div>
<div class='jt-row'>
<div class='jt-detail'>a detail 1
  <div class='jt-item-price'>12.34</div>
</div>
</div>
<div class='jt-row'>

<div class='jt-header'>another header 2<div class='jt-item-price'>34.45</div></div>
</div>
<div class='jt-row'>
<div class='jt-detail'>another detail 2
</div>
</div>
halfer
  • 19,824
  • 17
  • 99
  • 186
timpone
  • 19,235
  • 36
  • 121
  • 211

9 Answers9

5

Jquery append() accepts a function, you can use following logic:

$('.jt-header').append(function(){
    return $(this).next('.jt-detail').children('.jt-item-price');
});

-jsFiddle-

A. Wolff
  • 74,033
  • 9
  • 94
  • 155
  • thx - so something like this but I've simplified and there's some stuff between jt-header and jt-detail (but not another jt-detail). So I need to say get the next 'jt-detail' and grab it's jt-item-price. But there could be a wrapper or something like in the edit which looks like it will bust using next. Although there's probably some way to do it than I just don't know. – timpone Feb 23 '15 at 06:06
2

Considering your edit #1, you should be able to do it by combining .closest() / .prev() / .find()

Like this

$(function(){
    $('#move').on('click',function(){
        $('.jt-detail .jt-item-price').each(function(){
            $this = $(this);
            $this.appendTo($this.closest('.jt-row').prev().find('.jt-header'));
        }); 
    });
});

Check the fiddle here

The condition is to keep the current html structure with your jt-row :)

Robin Leboeuf
  • 2,096
  • 1
  • 13
  • 14
  • +1, this one's better than my answer in a way because you're only selecting `jt-item-price`'s inside `jt-detail`, that way you'll only iterate through the elements that have to be corrected , so less iterations but one thing that I'm wondering is what happens if your previous `jt-header` is already having some `jt-item-price` . If that's a possibility in OP's html you need to change your answer a bit. I assume we're not likely to have that case - either you have `jt-item-price` inside `jt-detail` or inside `jt-header` but not in both in two consequent rows, need to be clarified by him :) – Arkantos Feb 28 '15 at 17:28
2

Fairly simple actually, just have to properly traverse the DOM tree

Per your example here is the step by step breakdown:

  • FOR EACH element with the class="jt-item-price"
  • IF the parent class is equal to jt-detail
  • THEN append the element to the preceding jt-header element present in jt-row
  • ELSE Move to the next jt-item-price element & repeat

Here is what the code looks like:

$('.jt-item-price').each(function(){
  if($(this).parent().hasClass('jt-detail')){
    var $headerCheck = $(this).parents('.jt-row');
    var loopCheck = false;

    while(!loopCheck){
      $headerCheck = $headerCheck.prev();
      if($headerCheck.find('.jt-header').length > 0){
        loopCheck = true;
      }
    }

    $(this).appendTo($headerCheck);
  }
})

~Live CodePen~

Adjit
  • 10,134
  • 12
  • 53
  • 98
1

you just have to play with element positioning using jQuery.parent() and jQuery.prev() methods.

It is easy in your case because your classes are already defined, so in this case, the code below should do the trick:

jQuery(document).ready(function(){
  // Button required for testing purposes only
  jQuery('.moveIt').on('click', movePrice);
  // If script should run automatically
  // move everything out of this function
  function movePrice() {
    // For maintaining purposes
    var _price = 'jt-item-price';
    var _from = 'jt-detail';
    var _to = 'jt-header';
    // Vudu stuff
    jQuery('div.'+_price).each(function(){
        // Get elements
        var _this = jQuery(this);
        var _parent = _this.parent();
        var _parentClass = _parent.attr('class');
        // If parent matches the wrapper we want
        // to take the element out of
        if( _parentClass == _from ) {
            // Get its parent previous element
            var _finalWrapper = _this.parent().prev();
            // If previous element class matches our
            // destination's class
            if( _finalWrapper.attr('class') == _to ) {
                // Put price there
                jQuery(_finalWrapper).append( _this );
            }
         }
     });
   }
});

/**
 * Answer to: http://stackoverflow.com/questions/28661854/how-to-move-an-element-into-if-it-is-contained-in-another-element
 */
jQuery(document).ready(function(){
    // Testing with no Row wrappers
    jQuery('.unwrap').on('click', function(){
        jQuery('.row > .jt-header').unwrap();
    });
    // Button required for testing purposes only
    jQuery('.moveIt').on('click', movePrice);
    // If script should run automatically
    // move everything out of this function
    function movePrice() {
        // For maintaining purposes
        var _price = 'jt-item-price';
        var _from = 'jt-detail';
        var _to = 'jt-header';
        // Vudu stuff
        jQuery('div.'+_price).each(function(){
            // Get elements
            var _this = jQuery(this);
            var _parent = _this.parent();
            var _parentClass = _parent.attr('class');
            // If parent matches the wrapper we want
            // to take the element out of
            if( _parentClass == _from ) {
                // Get its parent previous element
                var _finalWrapper = _this.parent().prev();
                // If previous element class matches our
                // destination's class
                if( _finalWrapper.attr('class') == _to ) {
                    // Put price there
                    jQuery(_finalWrapper).append( _this );
                }
            }
        });
    }
});
.jt-header {
    clear: both;
    border: 1px solid #f5f5f5;
    padding: 0px .5em;
    margin: .5em;
    background: #f1f1f1;
}
.jt-detail, 
.jt-item-price {
    display: inline-block;
}
.jt-detail { padding: 1em; }
.jt-item-price {
    margin: 1em 0;
    background: #f1f1f1;
    padding: .5em;
    font-weight: bold;
}
.row {
    display: block;
    padding: .5em;
    border: 1px solid #efefef;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">
    <div class='jt-header'>Product 1</div>
    <div class='jt-detail'>Product Details
      <div class='jt-item-price'>12.34</div>
    </div>
</div>
<div class="row">
    <div class='jt-header'>Product 2 
        <div class='jt-item-price'>34.45</div>
    </div>
    <div class='jt-detail'>Product Details</div>
</div>
<div class="row">
    <div class='jt-header'>Product 3</div>
    <div class='jt-detail'>Product Details
      <div class='jt-item-price'>20.55</div>
    </div>
</div>
<hr />
<button class="moveIt">Move Prices</button>
<button class="unwrap">Unwrap from Rows</button>

I hope this helps.

Best regards, José SAYAGO.

jsFiddle Example

José SAYAGO
  • 798
  • 6
  • 15
1

Following the structure you posted in edit#1, you iterate over all jt-row, if there's a jt-item-price whose parent is not jt-header, you move that element in to previous row's jt-header

     $('.jt-row').each(function(){

        /* Assuming you have only jt-detail in each row
         * and you need to append jt-detail to previous row's jt-header
         * if it's not having any jt-detail already */
        var $row = $(this),
        $ele = $row.find('.jt-item-price');
        if($ele && $ele.parent().hasClass('jt-detail')){
           var detached = $ele.detach();
           detached.addClass('changed'); // just for noticing change
           $row.prev().find('.jt-header').append(detached);
        }
      });

Here's a bin to play with :)

Arkantos
  • 6,530
  • 2
  • 16
  • 36
1

here is the fiddle link: http://jsfiddle.net/fjjeo22h/1/

the code:

$(".jt-detail .jt-item-price").each(function () {
    var price = $(this);
    var prevhead = $(this).parent(".jt-detail").prev(".jt-header");
    if (!prevhead.find('.jt-item-price').length > 0) {
        price.appendTo(prevhead);
    }
});
user8942
  • 141
  • 7
1

Try

// filter `.jt-item-price` , 
// return elements not already children of `jt-header`
var itemPrice = $(".jt-item-price").not(function (i, el) {
    return $(el).parent().is(".jt-header")
});

// `itemPrice` parent element
var item = itemPrice.parents(".jt-row");

// if `item` contains child element `jt-header` ,
// append `itemPrice` at current `item`
if (item.find(".jt-header").is("*")) {
    item.find(".jt-header").append(itemPrice)
} 
// else filter `.jt-row` by index 1 less than current `item`,
// append `itemPrice` to `.jt-header` at selected `.jt-row`
else {
    item.siblings(".jt-row").eq(item.index(".jt-row") - 1)
    .find(".jt-header").append(itemPrice)
};

// filter `.jt-item-price` , 
// return elements not already children of `jt-header`
var itemPrice = $(".jt-item-price").not(function (i, el) {
    return $(el).parent().is(".jt-header")
});

// `itemPrice` parent element
var item = itemPrice.parents(".jt-row");

// if `item` contains child element `jt-header` ,
// append `itemPrice` at current `item`
if (item.find(".jt-header").is("*")) {
    item.find(".jt-header").append(itemPrice)
} 
// else filter `.jt-row` by index 1 less than current `item`,
// append `itemPrice` to `.jt-header` at selected `.jt-row`
else {
    item.siblings(".jt-row").eq(item.index(".jt-row") - 1)
    .find(".jt-header").append(itemPrice)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div class='jt-row'>
    <div class='jt-header'>a header 1</div>
</div>
<div class='jt-row'>
    <div class='jt-detail'>a detail 1
        <div class='jt-item-price'>12.34</div>
    </div>
</div>
<div class='jt-row'>
    <div class='jt-header'>another header 2
        <div class='jt-item-price'>34.45</div>
    </div>
</div>
<div class='jt-row'>
    <div class='jt-detail'>another detail 2</div>
</div>
guest271314
  • 1
  • 15
  • 104
  • 177
0

Use this Demo Here

$(function(){
    $('#move').on('click',function(){
        $('.jt-detail .jt-item-price').each(function(){
            $this = $(this);
            $this.appendTo($this.closest('.jt-row').prev().find('.jt-header'));
        }); 
    });

});
I'm Geeker
  • 4,601
  • 5
  • 22
  • 41
0

You can use nextUntil which will just iterate through until it finds the element. With this you can have as many elements between header and detail as you like (which is the objective from what I understand?)

JSFIDDLE

Run the below snippet. I just made the header bold so you can see it working.

$('.jt-header').each(function () {
    var $this = $(this)
    $this.append(function () {
        var el = $this.next('.jt-detail')
        if (el.length < 1) el = $this.nextUntil('.jt-detail').next('.jt-detail');
        return el.children('.jt-item-price');
    });
});
.jt-header {
    font-weight:bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="jt-header">a header 1</div>
<div class="jt-detail">a detail 1
    <div class="jt-item-price">12.34</div>
</div>
<div class="jt-header">another header 2
    <div class="jt-item-price">34.45</div>
</div>
<div class="jt-detail">another detail 2</div>

<hr />

<div class="jt-header">a header 1</div>
<div class="something-between">La la la I'm in the way :P</div>
<div class="jt-detail">a detail 1
    <div class="jt-item-price">12.34</div>
</div>
<div class="jt-header">another header 2
    <div class="jt-item-price">34.45</div>
</div>
<div class="jt-detail">another detail 2</div>
Jamie Barker
  • 8,145
  • 3
  • 29
  • 64