Randomize a sequence of div elements with jQuery
Asked Answered
T

5

60

I'm trying to do my frist steps with jQuery but I have some trouble to understand how to find a list of child elements from a div parent element. I'm used to work with ActionScript 2 and ActionScript 3 so i could mistake some concept, like what is the better way to randomize a sequence of div elements with jQuery!

I have this simple portion of HTML code:

<div class="band">
    <div class="member">
        <ul>
            <li>John</li>
            <li>Lennon</li>
        </ul>
    </div>
    <div class="member">
        <ul>
            <li>Paul</li>
            <li>McCartney</li>
        </ul>
    </div>
    <div class="member">
        <ul>
            <li>George</li>
            <li>Harrison</li>
        </ul>
    </div>
    <div class="member">
        <ul>
            <li>Ringo</li>
            <li>Starr</li>
        </ul>
    </div>
</div>

I have attempted some way to do that like map .member divs in one array and then changing the sort order but without success.

function setArrayElements (element_parent) {
    var arr = [];
    //alert (element_parent[0].innerHTML);
    for (var i = 0; i < element_parent.children().length; i ++) {
        arr.push (element_parent[i].innerHTML);
    }
}
setArrayElements($(".band"));

when i attempted to alert element_parent[0] i thought to get the first child of my .member list of divs but it isn't.

if i make an alert with element_parent[0].innerHTML i see that:

<div class="member">
    <ul>
        <li>John</li>
        <li>Lennon</li>
    </ul>
</div>
<div class="member">
    <ul>
        <li>Paul</li>
        <li>McCartney</li>
    </ul>
</div>
<div class="member">
    <ul>
        <li>George</li>
        <li>Harrison</li>
    </ul>
</div>
<div class="member">
    <ul>
        <li>Ringo</li>
        <li>Starr</li>
    </ul>
</div>

Why? How can I do to get exactly one of the members like this?

<div class="member">
    <ul>
        <li>Paul</li>
        <li>McCartney</li>
    </ul>
</div>

I'm sure this should be easy but I just don't know how :(

please help
thanks
vittorio


EDIT:

Thanks for the fastness and this various ways to get the selected children, I'll take a note of these ways for the future!
I tried this methods, but it seems I couldn't get the entire div (please tell'me if i mistake something, it' could be too much possible!!).

I shoud get this content:

<div class="member">
    <ul>
        <li>Ringo</li>
        <li>Starr</li>
    </ul>
</div>

but with one of this methods like $("div.band div.member:eq(2)") or the other useful ways, I get this:

alert ($('div.band div.member')[0]);
/* result
<ul>
    <li>Ringo</li>
    <li>Starr</li>
</ul>
*/

so is there a way to get the .member div container too in my node?

Tadashi answered 7/10, 2009 at 20:13 Comment(2)
you will get the container div too. If you use Firebug and console.log, the output element is the first div with class memberAzevedo
ah! thanks, now I know I wasn't so wrong! bye byeTadashi
A
101
$('div.band div.member');

will give you a jQuery object containing <div> that match the selector i.e. div with class member that are descendents of a div with class band.

The jQuery object is an array-like object in that each matched element is assigned a numerical property (think like an index) of the object and a length property is also defined. To get one element is

// first element
$('div.band div.member')[0];

or

// first element
$('div.band div.member').get(0);

Instead of selecting all elements, you can specify to select a specific element according to position in the DOM. For example

// get the first div with class member element
$("div.band div.member:eq(0)")

or

// get the first div with class member element
$("div.band div.member:nth-child(1)")

EDIT:

Here's a plugin I just knocked out

(function($) {

$.fn.randomize = function(childElem) {
  return this.each(function() {
      var $this = $(this);
      var elems = $this.children(childElem);

      elems.sort(function() { return (Math.round(Math.random())-0.5); });  

      $this.detach(childElem);  

      for(var i=0; i < elems.length; i++)
        $this.append(elems[i]);      

  });    
}
})(jQuery);

Working Code:

$('button').click(function() {
  $("div.band").randomize("table tr td", "div.member");
});

(function($) {

  $.fn.randomize = function(tree, childElem) {
    return this.each(function() {
      var $this = $(this);
      if (tree) $this = $(this).find(tree);
      var unsortedElems = $this.children(childElem);
      var elems = unsortedElems.clone();

      elems.sort(function() {
        return (Math.round(Math.random()) - 0.5);
      });

      for (var i = 0; i < elems.length; i++)
        unsortedElems.eq(i).replaceWith(elems[i]);
    });
  };

})(jQuery);
body {
  background-color: #000;
  font: 16px Helvetica, Arial;
  color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="band">
  <table>
    <tr>
      <td>
        <div class="member">
          <ul>
            <li>John</li>
            <li>Lennon</li>
          </ul>
        </div>
      </td>
    </tr>
    <tr>
      <td>
        <div class="member">
          <ul>
            <li>Paul</li>
            <li>McCartney</li>
          </ul>
        </div>
      </td>
    </tr>
    <tr>
      <td>
        <div class="member">
          <ul>
            <li>George</li>
            <li>Harrison</li>
          </ul>
        </div>
      </td>
    </tr>
    <tr>
      <td>
        <div class="member">
          <ul>
            <li>Ringo</li>
            <li>Starr</li>
          </ul>
        </div>
      </td>
    </tr>
  </table>
</div>
<button>Randomize</button>

Here's an Editable Working Demo. add /edit to the URL to see the code. If you need any details about how it works, please leave a comment. It could probably do with being made more robust to handle certain situations (like if there are other child elems of the jQuery object the plugin is chianed to) but it'll suit your needs.

Azevedo answered 7/10, 2009 at 20:20 Comment(7)
this example it's great! I'haven't saw this jquery syntax mode, but i'll try it! very usefull to see how i can do it! thanks againTadashi
I've took the liberty to make a modified version of your demo (I've got no idea what are all those other 19 edits). Hopefully this works better for more situations. It sure did on my case.Maure
I believe this is improved by using a better shuffle function, see #2451454Rosenwald
@Rosenwald totally!Azevedo
I took this concept a bit further and made it shuffle the positions of the container divs, as that was what I was looking for... jsfiddle.net/coolwebs/zLgsdno7/4Adventurous
This answer is provably wrong. It does not provide a random sort at all. See here for a demo: phrogz.net/JS/JavaScript_Random_Array_Sort.htmlDeposit
@Deposit feel free to edit to include a better pseudorandom shuffling algorithmAzevedo
K
40

I modified Russ Cam's solution so that the selector is optional, and the function can be called on multiple container elements, while preserving each randomized element's parent.

For example, if I wanted to randomize all LIs within each '.member' div, I could call it like this:

$('.member').randomize('li');

I could also do it like this:

$('.member li').randomize();

So the two ways to call this are as follows:

$(parent_selector).randomize(child_selector);

OR

$(child_selector).randomize();

Here's the modified code:

$.fn.randomize = function(selector){
    (selector ? this.find(selector) : this).parent().each(function(){
        $(this).children(selector).sort(function(){
            return Math.random() - 0.5;
        }).detach().appendTo(this);
    });

    return this;
};

Minified:

$.fn.randomize=function(a){(a?this.find(a):this).parent().each(function(){$(this).children(a).sort(function(){return Math.random()-0.5}).detach().appendTo(this)});return this};
Khat answered 1/8, 2012 at 19:43 Comment(3)
I tried using this but get the error: Uncaught TypeError: Object [object Object] has no method 'randomize' Cardew
@jlg, you must call it on a JQuery object, like this: $(selector).randomize(optional_selector). You cannot call it on its own, like this: $.randomize(selector)Khat
This should be the accepted answer.Russ Cam's solution gave some regressive results in the presence of other Maths Funcions. This works just fine.Mcgruter
L
9

Randomization using sort does not always randomize the elements. It is better to use a shuffle method like the one from How can I shuffle an array?

Here's my updated code

(function($) {
    $.fn.randomize = function(childElem) {
        function shuffle(o) {
            for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
            return o;
        };
        return this.each(function() {
            var $this = $(this);
            var elems = $this.children(childElem);

            shuffle(elems);

            $this.detach(childElem);  

            for(var i=0; i < elems.length; i++) {
                $this.append(elems[i]);      
            }
        });    
    }
})(jQuery);
Loni answered 5/8, 2014 at 14:8 Comment(1)
I tried the accepted solution but the randomness was not good enough in my opinionHilaria
P
1

Just in case you are ok with doing it without jquery:

/** @param {HTMLElement} parent */
const shuffleChildren = (parent) => {
    const cards = [...parent.children];
    for (const child of cards) {
        const newPlace = Math.floor(Math.random() * cards.length);
        parent.insertBefore(child, cards[newPlace + 1] || null);
    }
};

(an answer unrelated to jquery was closed as duplicate of this one, so posting here)

Progressionist answered 25/10, 2020 at 17:34 Comment(0)
I
1

This solution is inspired by the existing solutions, but uses the Fisher-Yates algorithm for sorting.

This ensures that the elements are randomized fairly, which is important for many use cases.

$.fn.randomizeChildrenOrder = function() {
    this.each(function(){
        let childrenArray = $(this).children().toArray();
        let shuffledChildrenArray = fisherYatesShuffle(childrenArray);
        $(shuffledChildrenArray).detach().appendTo(this);
    });

    return this;
    
    // https://mcmap.net/q/45002/-how-to-randomize-shuffle-a-javascript-array
    function fisherYatesShuffle(array){
        let currentIndex = array.length, temporaryValue, randomIndex;
        while (currentIndex !== 0) {
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;
            temporaryValue = array[currentIndex];
            array[currentIndex] = array[randomIndex];
            array[randomIndex] = temporaryValue;
        }
        return array;
    }
}

Usage: call the function on the parent element of the elements you need to shuffle. If the elements share the parent element with elements that need to be left alone, add an extra parent around them, so this is no longer the case.

$('.member ul').randomizeChildrenOrder();

Generalized:

$(parentSelector).randomizeChildrenOrder();
Inconvincible answered 1/4, 2021 at 15:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.