4

I have the following code:

HTML:

<ul id="main-menu">
    <li><a href="#page-1" class="slide-item selected">page 1</a></li>
    <li><a href="#page-2" class="slide-item">page 2</a></li>
    <li><a href="#page-3" class="slide-item">page 3</a></li>
    <li><a href="#page-4" class="slide-item">page 4</a></li>
</ul>

<div class="test">
    <div id="page-1" class="page">
    <div class="page-container">
        <h3>page 1</h3>
        <p>
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi consequatur deleniti eum illo itaque
            iusto officia soluta, veritatis? Ab aliquam autem cum doloribus eaque eum in itaque natus rem, vero.
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi consequatur deleniti eum illo itaque
            iusto officia soluta, veritatis? Ab aliquam autem cum doloribus eaque eum in itaque natus rem, vero.
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi consequatur deleniti eum illo itaque
            iusto officia soluta, veritatis? Ab aliquam autem cum doloribus eaque eum in itaque natus rem, vero.
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi consequatur deleniti eum illo itaque
        </p>
        </div>
    </div>
</div>

JQUERY:

    $.fn.insertAt = function(index, elements){
    var children = this.children();
    if(index >= children.size()){
        console.log(index+'>='+children.size());
        this.append(elements);
        return this;
    }
    var before = children.eq(index);
    $(elements).insertBefore(before);
    return this;
}; //http://upshots.org/javascript/jquery-insert-element-at-index

jQuery('.slide-item').on('click', function(){
        var item = jQuery('a[href="'+jQuery(this).attr('href')+'"]').parent();
        var index = jQuery("ul li").index(item);
        //console.log(index);
        var target = jQuery(this).attr('href').substr(1);

        if(jQuery(jQuery(this).attr('href')).length){
            console.log('loaded');
        }else {
            jQuery.get("pages/"+target+".html", function(data){
                jQuery('.test').insertAt(index, data);
            });
        }

        return false;
    });

Basically as you can probably see based on which link you click it loads a page, Now my actual question is:

Is there anyway to keep them in order?

For example if i click the links in this order 1,4,2,3 then that's the order the pages get appended to the page:

<div id="page-1" class="page">
<div id="page-4" class="page">
<div id="page-2" class="page">
<div id="page-3" class="page">

However i need it to be independent of the order you click the links in.. so for example if i click 1,3,2,4 then i need the pages to append in order (1,2,3,4):

<div id="page-1" class="page">
<div id="page-2" class="page">
<div id="page-3" class="page">
<div id="page-4" class="page">

I have recently tried to do it through index, by getting the index of the clicked items li and then trying to use the insertAt function i found here to insert the div at that index... But it does not work entirely for example if i click 4 then 3 gets appended after 4.

Any Help Would be Greatly Appreciated.

8 Answers8

1

I suggest you simplify the entire system by creating empty placeholder DIVs for each page. The order is then already in place.

JSFiddle: http://jsfiddle.net/TrueBlueAussie/R2T4Z/4/

// Use wrapped DOM ready event to provide local $ for jQuery
jQuery(function ($) {
    $('.slide-item').on('click', function () {
        var $link = $(this);
        var target = $link.attr('href').substr(1);

        // If the div has any content it is "loaded" (if you know the page has child elements use children().length as it will be faster)
        if ($('#' + target).html().length) {
            console.log('loaded');
        } else {
            // Dummy test code - REMOVE THIS
            $('#' + target).html("I AM LOADED:" + target);
            // End dummy test code
            $.get("pages/" + target + ".html", function (data) {
                $('#' + target).html(data);
            });
        }

        return false;
    }).each(function (index) {
        // create an empty placeholder DIV (in order) with unique matching id for each new entry
        var $link = $(this);
        var id = $link.attr("href").substr(1);
        // If page not already present...
        if (!$('#' + id).length) {
            $('.test').append($('<div>').attr("id", id));
        }
    });
});

*Note: jQuery(function ($) {YOU CODE HERE}); is a shortcut for jQuery(document).ready($){YOUR CODE HERE}); and provides a locally scoped $ so your jQuery code is shorter.

Update: http://jsfiddle.net/TrueBlueAussie/ugZST/12/

After much discussion, the requirements also need to work with a horizontal slider. The existing was a one-shot type and not compatible with dynamic content, so I have replaced that too using a slideToPage function that uses absolute animations to bring pages into and out of view:

function slideToPage($page){
    var $wrapper = $('.wrapper');
    var width = $wrapper.outerWidth();
    var height = $wrapper.outerWidth();

    // Set size of all children and stop previous animations
    $wrapper.children().css({width: width, height: height}).stop();

    // Find the current page (positioned at 0)
    var $currentpage = $wrapper.find('.currentpage');
    if ($page.is($currentpage)){
        console.log("Stay on current page");
        return;
    }
    console.log("$currentpage.index() " + $currentpage.index());
    console.log("$page.index() " + $page.index());

    // Is the new page index before or after (left or right scroll)
    var slideRight = $page.index() > $currentpage.index();
    var delta = slideRight ? 1 : -1;

    // Position offscreen (to left or right of the screen)
    $page.css({left: delta * width});

    // Animate new panel to positon 0
    $page.animate({left: 0}, 1000).show();

    // Animation old panels offscreen (left or right) then hide
    $currentpage.animate({left: -delta * width}, 1000, function(){
        $currentpage.hide();
    });

    $currentpage.removeClass('currentpage');
    $page.addClass('currentpage');

    // hide all other pages (just to be sure)
    $wrapper.children().not($currentpage).not($page).hide();
}

// Use wrapped DOM ready event to provide local $ for jQuery
jQuery(function ($) {
    $('.scrollitem').on('click', function () {
        var $link = $(this);
        var target = $link.attr('href').substr(1);

        // If the div has any content it is "loaded" (if you know the page has child elements use children().length as it will be faster)
        var $page = $('#' + target);
        var $target = $page.find('.page-container');
        if ($target.html().length) {
            console.log('loaded');
            slideToPage($page);
        } else {
            // Dummy test code - REMOVE THIS
            $target.html("I AM LOADED:" + target + "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi consequatur deleniti eum illo itaque Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi consequatur deleniti eum illo itaque Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi consequatur deleniti eum illo itaque");
            slideToPage($page);
            $page.css('display', '');
            // End dummy test code
            $.get("pages/" + target + ".html", function (data) {
                $target.html(data);
                slideToPage($page);
                $page.css('display', '');
            });
        }

        return false;
    }).each(function (index) {
        // create an empty placeholder DIV (in order) with unique matching id for each new entry
        var $link = $(this);
        var id = $link.attr("href").substr(1);
        // If page not already present...
        if (!$('#' + id).length) {
            $('.wrapper').append($('<div class="page"><div class="page-container"></div></div>').attr("id", id).css('display', 'none'));
        }
    });
});
iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
  • Thank you for the note, helps alot.. The thing is, I cant have empty place holders, as i slide horizontally through all the pages until the clicked item is in view... so i cant have it scroll through 46 empty divs to reach its end point. –  Jul 10 '14 at 09:33
  • Puzzled by that one... Empty divs potentially take up no space at all. Just addClass/removeClass/show etc them on load to change styling. Can you show the sliding code? – iCollect.it Ltd Jul 10 '14 at 09:34
  • its quiet a lot of code, what would you need only the javascript? or everything? –  Jul 10 '14 at 09:39
  • Just need to know how you are scrolling (JS would do). If simply hiding the divs is enough to eliminate them from your scroller, try this one: http://jsfiddle.net/TrueBlueAussie/R2T4Z/6/ – iCollect.it Ltd Jul 10 '14 at 09:40
  • That helps... Will migrate the scrolling into example. – iCollect.it Ltd Jul 10 '14 at 09:43
  • Your slider is not currently compatible with dynamically loaded pages anyway. Will modify your sample to include my code instead. – iCollect.it Ltd Jul 10 '14 at 09:50
  • I know sorry the most recent version that i do not have access to right now is though.. but thank you –  Jul 10 '14 at 09:51
  • It's more about giving an example that works. Will see what I can do :) – iCollect.it Ltd Jul 10 '14 at 09:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57086/discussion-between-trueblueaussie-and-dawid-van-der-hoven). – iCollect.it Ltd Jul 10 '14 at 10:33
0

Your insert-at function will not help you here. As you have found, clicking page 4 then page 3 does not order it correctly because the insert-at function inserts dom elements based on the index of the child element. This means that if you have a list like [page3, page5], inserting any subsequent page n at index n will clearly fall flat.

There are many solutions to this problem and it's difficult to determine which is the best option for your use case, but a simple solution would be to simply iterate through your current list until the nth index has a page number greater than n, then insert it at n. In computer science we call this an online insertion sort. You can read about it here: http://en.wikipedia.org/wiki/Insertion_sort

Here's a demonstration:

$.fn.insertAt = function(index, elements){
    var children = this.children();
    if(index >= children.size()){
        console.log(index+'>='+children.size());
        this.append(elements);
        return this;
    }
    var before = children.eq(index);
    $(elements).insertBefore(before);
    return this;
}; //http://upshots.org/javascript/jquery-insert-element-at-index

jQuery('.slide-item').on('click', function(){
    var item = jQuery('a[href="'+jQuery(this).attr('href')+'"]').parent();
    var index = jQuery("ul li").index(item);
    //console.log(index);
    var target = jQuery(this).attr('href').substr(1);

    if(jQuery(jQuery(this).attr('href')).length){
        console.log('loaded');
    }else {
        //jQuery.get("pages/"+target+".html", function(data){
        //    jQuery('.test').insertAt(index, data);
        //});

        // the insert page number happens to be the index
        var data = '<div id="' + target + '" class="page">' + target + ' content</div>';
        var toInsertPageNumber = index;
        var didInsert = false;
        jQuery('.test').children().each(function(k, v) {
            var currentPageNumber = parseInt($(v).attr('id').replace('page-', ''), 10);

            if (currentPageNumber > index) {
                jQuery('.test').insertAt(k, data);      
                didInsert = true;
                return false;
            }
        });

        // if we didn't insert anything, then it goes to the end of the list.
        if (!didInsert) {
            jQuery('.test').append(data);
        }
    }

    return false;
});

Here's a fiddle containing the above code: http://jsfiddle.net/aDtV5/

sahbeewah
  • 2,690
  • 1
  • 12
  • 18
  • Hi thank you so much for your answer just one thing, the way your getting 'currentPageNumber' is dependent on the numeric value in the id, is there any other way? As there wont be any numeric values, this was purely coincidental that the numbers matched the order. –  Jul 10 '14 at 07:00
  • While this works it is very complicated for the desired end-result (pages in page order). Much simpler to create empty placeholders and update those on load (and only needs a fraction of the code). – iCollect.it Ltd Jul 10 '14 at 09:13
0

Here is a function that would do the trick:

//check by comparing order number
function insert(page){
    //extract order number from element to be inserted
    var placeId = parseInt($(page).attr('id').substr($(page).attr('id').length - 1));
    //loop through each existing page
    $('.page').each(function(){
        //extract order number of current loop iteration
        var id = parseInt($(this).attr("id"));
        //if currently existing item has higher ID than the one to be inserted
        //insert new page before(this works because we can assume order)
        if(id > placeId){
            $([NEW_PAGE]).insertBefore($(this));
            return;
        }
        //in case your trying to load an existing page
        if(id === placeId){return;}
    });
    //page to be loaded is bigger than any already loaded
    $([NEW_PAGE]).insertAfter($('.page').last());
}
Philipp Werminghausen
  • 1,142
  • 11
  • 29
0

No placeholder is required.

jsfiddle: http://jsfiddle.net/Neverever/43gn9/

function insertData(id, data){
    // locate next 'div' and insert before it
    var found = $("li:has(a[href='" + id + "']) ~ li a").get().some(function(elm, idx) {
        var nextDiv = $($(elm).attr('href'));
        return nextDiv.length && nextDiv.before(data);
    });

    // if not found, append to .test
    if(!found)
        $(".test").append(data);

}

jQuery(function($) {
    $('.slide-item').on('click', function(){
        var self = $(this),
            id = self.attr('href'),
            target = id.substring(1);

        if($(id).length) {
            console.log('loaded');
        } else {

            // use jsFiddle ajax api call to return dummy HTML
            $.post("/echo/html/", { html : '<div id="'+target+'">'+target+'</div>'}, function(data) {
                insertData(id, data);
            });

            // uncomment the following for your actual ajax request
            /*
            $.get("pages/"+target+".html", function(data){
                insertData(id, data);
            });
            */
        }
        return false;
    });
});
Neverever
  • 15,890
  • 3
  • 32
  • 50
  • Hey, it seems pretty solid..but a no go on IE8... is there any way to make it compatible with ie8? –  Jul 13 '14 at 09:21
  • found a prototype .some shim for ie8: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some works now –  Jul 13 '14 at 09:48
  • Hey Brilliant answer, just one more thing and then i think your answer is pretty 100%. i have updated the fiddle http://jsfiddle.net/dawidvdh/43gn9/4/ i have added 2 cases the second isn't 100% required.. but would be nice. 1 case: there are two menus 'main-menu' and 'footer-menu' just keeping them in order even though they are separate. And then the second case that i don't think is possible (but trying my luck), when i link is in the actual loaded data (loads the page but not in order). Otherwise Thanks A lot Awesome Answer! –  Jul 13 '14 at 11:36
  • 1
    feel free to modify any codes that fit your requirements. I'm just helping to point out the right logic for inserting. – Neverever Jul 14 '14 at 01:23
0

If is this what you are looking for.. May be you can try this..

 <style>
 .hidden{
   display:none;
 }
</style>

<ul id="main-menu">
<li><a href="#page-1" class="slide-item selected">page 1</a></li>
<li><a href="#page-2" class="slide-item">page 2</a></li>
<li><a href="#page-3" class="slide-item">page 3</a></li>
<li><a href="#page-4" class="slide-item">page 4</a></li>
</ul>



<div id="page-1" class="page hidden">
<div id="page-2" class="page hidden">
<div id="page-3" class="page hidden">
<div id="page-4" class="page hidden">


<script>
 var content = "Content of ";
 $("#main-menu li").on("click",function(e){
   var id = e.target.hash.substring(1);
   $("#"+id).removeClass('hidden')
   .append("div")
   .addClass("page-container")
   .append("h3").text(id)
   .append("p")
   .text(content);
});
</script>

Replace "content" with your content to be displayed inside text method.

harshes53
  • 419
  • 1
  • 6
  • 17
0

You can call .filter() with a function that finds elements with id smaller than that of the page to be inserted. Then insert the page after the last div found.

function addPage(id, page) {
    if (!$('#' + id).length) {
        // find the last page div with id smaller than target id
        $prevDiv = $('div.page', '.test').filter(function () {
            return $(this).attr('id') < id;
        }).last();

        // and append the new page after it
        $prevDiv.after(page);
    }
}

See JSFiddle

flup
  • 26,937
  • 7
  • 52
  • 74
0

Since DOM node lists implement an array-like construct, you can sort them. You can turn this into a jQuery "plugin" pretty easily:

function sortNodes(nodes, compare) {
    if(nodes instanceof jQuery) nodes = nodes.get();
    nodes.sort(compare);
    jQuery.each(nodes, function(i, node) {
        node.parentNode.appendChild(node);
    });
}

And then you would use it like this:

// replace this line in your code:
jQuery('.test').insertAt(index, data);

// with this code:
var $test = jQuery('.test');
$test.append( data );
sortNodes( $test.children(), function(a, b) {
    return a.id > b.id;
});

Explanation: Pass in a list of nodes to the sortNodes method. In your case, pass in a list of pages. Your HTML structure is such:

<div class="test">
   <div id="page-4" class="page"></div>
   <div id="page-1" class="page"></div>
   <div id="page-3" class="page"></div>
   <div id="page-2" class="page"></div>
</div>

So we pass in the list of pages using this line:

jQuery('.test').children();

Then each node is sorted based on the 'compare' function (read about sorting arrays on your own). In this case, we compare the node IDs so that the nodes are sorted alphabetically by their ID (eg 'page-2' > 'page-1').

... and the fiddle:

http://jsfiddle.net/ryanwheale/FpkgH/

Ryan Wheale
  • 26,022
  • 8
  • 76
  • 96
  • I'm sorry either i don't understand or answer or its just not working.. i implemented it.. and it doesn't seem to be ordering at all.. Could you possibly give some form of an explanation? –  Jul 16 '14 at 21:04
-1

You can send clicked index as URL parameter, and send it back to client with data (from server). Then you can append received data to the correct position. Other thing is, if you click the links in this order 1,4,2,3, you can't expect the result in same order since ajax call are performed asynchronously.

jQuery('.slide-item').on('click', function(){
var target = jQuery(this).attr('href').substr(1);
jQuery.get("pages/"+target+".html", function(data){

    if($('.test').length == 0 )
    {
        $('.test').append(data);
    }
    else
    {
        $('.test').each(function(key,val){
          var id = $(val).attr("id");
          var index = id.split("-")[1];
          if(index <= newIndex)
          {
            //here newIndex is clicked link should come from server
            jQuery(data).insertAfter($(this));
          }
        });
    }

});
return false;
});
  • i understand that ajax is preformed asynchronously, the issue here is not the ajax its the appendTo that needs to append in order.. I am very sure that's possible.. –  Jul 01 '14 at 07:19
  • Have you tried your code? It will not insert correctly as a following page may not yet exist *for the index to be less than*. You would need a dummy entry with high-value ID to get this to work as-is, or check for running off the end and append. Also your `each` is on the parent and not the pages. – iCollect.it Ltd Jul 10 '14 at 09:21