简体   繁体   中英

How can I randomize the order of two sets of elements in jquery/javascript, so that they remain in sync with each order?

Consider the following mark up:

<div id="elements1">
    <div>Item 1</div>
    <div>Item 2</div>
    <div>Item 3</div>
    <div>Item 4</div>
    <div>Item 5</div>
</div>
<br/>
<ul id="elements2">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
</ul>

I want to generate a random order that both of these lists would use. So a desired output could look like:

<div id="elements1">
    <div>Item 2</div>
    <div>Item 5</div>
    <div>Item 3</div>
    <div>Item 1</div>
    <div>Item 4</div>
</div>
<br/>
<ul id="elements2">
    <li>Item 2</li>
    <li>Item 5</li>
    <li>Item 3</li>
    <li>Item 1</li>
    <li>Item 4</li>
</ul>

#elements1 will always have the same number of children in it as #elements2 and visa versa.

My pseudo code looks like like:

// see how many child elements in #elements1
// create order array based on # of child elements in i.e. [1,2,3,4,5]
// randomize array
// use order array to reorder both #elements1 and #elements2
// .appendTo() respective parents

Does my strategy seem like it would be efficient for the task at hand? (I will produce working code in a minute.) If not, what would be a better way to go about this? Thanks for your help!

Edit: Here is a fiddle to mess around with.

A simple way would be to use a documentFragment for this:

// lists with equal length
var list1 = document.getElementById('elements1');
var list2 = document.getElementById('elements2');
// a place to stash the items while randomizing
var stash1 = document.createDocumentFragment();
var stash2 = document.createDocumentFragment();

// initial length
var length = list1.childNodes.length;

// choose the same random child from the lists
// put them in their stashes preserving the new order
while(length > 0){
    var random = Math.floor((Math.random()*--length));
    stash1.appendChild(list1.childNodes[random]);
    stash2.appendChild(list2.childNodes[random]);
}

// put the elements back in the lists when done
list1.appendChild(stash1);
list2.appendChild(stash2);

Demo: http://jsfiddle.net/louisbros/NPPpt/

You can sort the the second array of selected elements using an array of values that has sorted the first collection.

// selecting elements
var $elem1 = $('#elements1 div'),
    $elem2 = $('#elements2 li');

// cloning elements (for reordering)
var $ordered1 = $elem1.clone(),
    $ordered2 = $elem2.clone();

// randomizing elements
var helper = [],
    i = -1,
    $randomized1 = $elem1.sort(function () {
        var n = 0.5 - Math.random();
        helper.push(n);
        return n;
    }),
    $randomized2 = $elem2.sort(function () {
        return helper[++i];
    });

// .appendTo() respective parents
$('#elements1').append($randomized1);
$('#elements2').append($randomized2);

http://jsfiddle.net/zGLZG/

To simply randomise the order of an Array , you could do something like this

function shuffleArray(a) { // Fisher-Yates shuffle
    var i = a.length, t, j;
    while (--i) { // loop over each item in array (except 0 because no point)
        j = (Math.random() * (i+1)) | 0; // random Int j <= i
        t = a[i], a[i] = a[j], a[j] = t; // swap items i and j
    }
}

You'll need to convert your HTMLCollection to something non-live so you can use it more easily, for example use

elements1 = document.getElementById('elements1'); // cache for later
nodes1 = Array.prototype.slice.call(elements1.children); // children to Array

elements2 = document.getElementById('elements2');
nodes2 = Array.prototype.slice.call(elements2.children);

Now for the thing you actually want to randomise (if you don't want to modify the function above to shuffle two arrays at the same time, see below)

i = nodes1.length, a = new Array(i);
while (i--) a[i] = i;
// a = [0, 1, 2, 3, ...]

Now simply randomise a , and re-append the nodes following the new order of numbers

shuffleArray(a); // a = e.g. [4, 8, 2, 5, ...]
for (i = 0; i < nodes1.length; ++i) {
    elements1.appendChild(nodes1[a[i]]); // append in new order
    elements2.appendChild(nodes2[a[i]]);
}

Demo . I've ignored var but remember to use it as applicable.


Decided to add a modified version of the shuffle for 2 Arrays , as this will most likely be much faster.

function shuffleTwoArrays(a, b) { // Shuffle 2 arrays same length in same way
    var i = a.length, t, j;
    while (--i) { // loop over each item in array (except 0 because no point)
        j = (Math.random() * (i+1)) | 0; // random Int j <= i
        t = a[i], a[i] = a[j], a[j] = t; // swap items i and j in a
        t = b[i], b[i] = b[j], b[j] = t; // swap items i and j in b
    }
}

Here you would just do

shuffleTwoArrays(nodes1, nodes2); // shuffle both the same
for (i = 0; i < nodes1.length; ++i) {
    elements1.appendChild(nodes1[i]); // re-append to achieve new order
    elements2.appendChild(nodes2[i]);
}

Demo .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM