Treadstone
Treadstone

Reputation: 35

DOM Manipulation with jQuery

This should be fairly simple, but I seem to be missing something. I'm working on a little project in which the code that I'm writing aims to do a few things across the document. Here's the HTML code that I want to apply my script's actions to:

<div class="entry">
    <p>First paragraph</p>
    <p>Second paragraph</p>
    <p>Third paragraph</p>
    <p>One more...</p>

    <div class="bttn"><a href="#">Click for More</a></div>
</div>

This template (that is, several p elements and one div with the class "bttn" inside a wrapper div with the class "entry") is repeated multiple times across the document. Users add content to the paragraphs and eventually publish it to the site. And here's what I wish to do in my .js file:

  1. Count all p elements inside the div elements with the class "entry".
  2. Throughout the entire document, if there are more than two p elements inside each div classed "entry", I want to leave the first two untouched and move the rest to a new div element with the class "slider". This new element would be placed after the "Click for More" button.
  3. Add the class "show" to the already existent div element classed "bttn", but only to those that belong to the classes "entry" that contained more than two paragraphs and were moved to the new div with the class "slider". The new button would look like this: div class="bttn slider" (plus the anchor tag and everything else as shown in the template).

So basically, if the user who published the content added more than two paragraphs to the HTML template, when the doc is ready I want the script to take the other paragraphs, move them to a new div element, and add a new class to the button. In the end when the "Click for More" button is clicked, the extra paragraphs that were removed and inserted afterwards will fade in nicely (since I'll keep them hidden under the button once I've re-inserted them). Here's what I've done so far:

$('.entry').each(function () {
    $counter = $('.entry p').length;

    if ($counter > 2) {
        $removeExtra = $(this).find('p').not(':eq(1)').not(':eq(0)').detach();

        $(this).find('.bttn').after(function () {
            return '<div class="slider">' + $removeExtra.html() + '</div>';
        }).addClass('show');
    }
});

Issues:

Would greatly appreciate any help with this. Thanks in advance!

Upvotes: 1

Views: 222

Answers (3)

Sampson
Sampson

Reputation: 268452

My initial suggestion would be to use :gt:

$(".entry").each(function(){
    var ps = $("p:gt(1)", this);
    if ( ps.length ) {
        $(".bttn", this).addClass("show").after(function(){
            return $("<div>").addClass("slider").append(ps);    
        });
    }        
});​​​​​​​​​​​​​​​​​​​​​​​

I apologize if I got the details mixed up in any degree - the question is a bit verbose.

Fiddle: http://jsfiddle.net/FfmaB/1/

Upvotes: 3

Nitin S
Nitin S

Reputation: 7601

review the documentation for $.html() function:
http://api.jquery.com/html/

If the selector expression matches more than one element, only the first match will have its HTML content returned.


I believe this is what you wanted:

$(document).ready(Process);

function Process() {
    $('.entry').each(function() {
        var $this = $(this);
        $counter = $this.find('p').length;

        if ($counter > 2) {
            var $Extras = $this.find('p').not(':eq(1)').not(':eq(0)');

            $($Extras.get().reverse()).each(function() {
                var extra = $(this).html();
                $this.find('.bttn').after('<div class="slider">' + extra + '</div>');
                $this.find('p').not(':eq(1)').not(':eq(0)').detach();
            })
            $this.find(".bttn").addClass('show');
        }
    });
}​

DEMO: http://jsfiddle.net/xcuh9/2/

regards,
Nitin J.

Upvotes: 1

nnnnnn
nnnnnn

Reputation: 150070

This line:

$counter = $('.entry p').length;

Is finding all paragraphs within any ".entry" div, when you want paragraphs just within the current ".entry" div. Also you don't need to use the callback-function syntax with .after() since you're using it to add after only one element at a time.

Try this instead:

$('.entry').each(function () {
    var $this = $(this),
        $p = $this.find('p');

    if ($p.length > 2) {
        $('<div class="slider"/>').appendTo(this).append($p.slice(2));
        $this.find('.bttn').addClass('show');
    }
});

I've used the .append() method rather than .after() since .append() will automatically add them after the last child of the element you're appending to. You don't need to detach the paragraphs before appending them to the new div because .append() will move them (at least, it will move them when there is only one target element - as is the case here).

I've used .slice() rather than your .not(':eq(1)').not(':eq(0)').

Note that you should declare variables with var or they become globals.

Upvotes: 3

Related Questions