Use meta box drag and drop to order items independently

As suggested by sri, I will answer my own question 🙂

The drag and drop of the meta boxes is for remembering the order of the meta boxes for each user, so if user A like to have meta box 5 on top because he uses that feature more, he doesn’t have to do move the meta box every time. I decided that changing that behaviour was not a good idea, as users who know of that behaviour could get confused. Also would mean messing around with the database.

So I instead used the already built in jquery ui sortable to make my custom fields sortable. I created a container div for the sortable fields area, with an id “sortable”, then added a table for each field group (I wanted a group of fields to be sortable, not individual fields). Then a tr for each field. One of the custom fields in each table I used to save the order as a post meta.

<div id="sortable">
    <table>
        <tr><th class="title">My title</td></tr>
        <tr><td>Code postfield 1 - 1</td></tr>
        <tr><td>Code postfield 1 - 2</td></tr>
        <tr><td><input name="ordering_1" type="hidden" class="ordering" value="$meta"></td></tr>
    </table>
    <table>
        <tr><th class="title">My title</td></tr>
        <tr><td>Code postfield 2 -1 </td></tr>
        <tr><td>Code postfield 2 -2</td></tr>
        <tr><td><input name="ordering_2" type="hidden" class="ordering" value="$meta"></td></tr>
    </table>
</div>

Of course this is a over-simplification, as I use arrays to generate the postfields codes, but just to show the structure.

Then I added the javascript:

To add a value to the order custom fields based on their position:

$("#sortable > table").each(function(index) {
    console.log(index+1);
    var count = index+1;
    $(this).find(".ordering").val(count);
}) 

To make the tables sortable. I had to add the handle to the title, otherwise, trying to upload a file started the dragging mode:

$("#sortable").sortable({ 
    cursor: "move", 
    containment: "parent",
    cursorAt: { left: -5,top:-5 },
    distance: 10,
    handle:"th.title"
});

Then to update the order as I drag and dropped:

$('#sortable').on("sortstop", function( event, ui ) {
    $("#sortable > table").each(function(index) {
        console.log(index+1);
        var count = index+1;
        $(this).find(".ordering").val(count);
    }) 
} );

The final thing was to order the tables based on my custom order. I modified the code:

<div id="sortable" style="float:left">
    <? $field_groups = array();
    if (get_post_meta($post->ID,"order_eng_1_1",true)) {
        for ($i = 1; $i <= 20; $i++){
            $field_groups[get_post_meta($post->ID,"order_eng_".$i."_1",true)] = $i;
        }
        ksort($field_groups);
        foreach ($field_groups as $field_order => $field_id) {?>
            <table class="form-table dropable">
                <tr>
                    <th class="title">My Title <?= $field_order?></th>
                </tr>
                <tr><td>Code postfield 1 - 1</td></tr>
                <tr><td>Code postfield 1 - 2</td></tr>
                <tr><td><input name="ordering_1" type="hidden" class="ordering" value="$meta"></td></tr>
            </table>
         <? }
    } else {
        for ($i = 1; $i <= 20; $i++) {?>
            <table class="form-table dropable">
                <tr><th class="title">My title <?=$i?></th></tr>
                <tr><td>Code postfield 1 - 1</td></tr>
                <tr><td>Code postfield 1 - 2</td></tr>
                <tr><td><input name="ordering_2" type="hidden" class="ordering" value="$meta"></td></tr>
            </table>
         <? }
     }?>
</div>

This is my first time trying to explain my code, it’s probably not comprehensive at all, so anything I can do to make it more clear, suggestions are very welcome 😉

Leave a Comment