javascriptjqueryjquery-uiclonejquery-ui-spinner

How do I make a cloned spinner control the correct input?


I am trying to dynamically change the number of jQuery UI spinners. I have no problem removing them, however when adding a new one, what I'm doing is cloning the first one and appending it to the end like this:

function clone_elem() {
    $("#num_people").append($(".num_people").first().clone(true, true));
}

Everything seems to work correctly, however the cloned spinner elements control the original spinner text value instead of the cloned one. How can I make the cloned spinners control the correct input box? Perhaps I should be using something other than clone() for this?

Here's my jsFiddle so you can see what I mean.


Solution

  • In order to clone an element with a spinner, what you want to do is remove the old spinner(s), clone the element, then re-add the spinners:

    $(document).ready(function () {
        make_spinners();
        clone_elem();
    });
    
    function clone_elem() {
        kill_spinners();
        $("#num_people").append($(".num_people").first().clone(true, true));
        make_spinners();
    }
    
    function kill_spinners() {
        $('.spinner').spinner( "destroy" );
    }
    
    function make_spinners() {
        if ($('.spinner').length > 0) {
            $('.spinner').spinner({
                min: 0,
                max: 100,
                stop: function (event, ui) {
                    // Apply the JS when the value is changed
                    if (typeof $(this).get(0).onkeyup == "function") {
                        $(this).get(0).onkeyup.apply($(this).get(0));
                    }
                    if (typeof $(this).get(0).onchange == "function") {
                        $(this).get(0).onchange.apply($(this).get(0));
                    }
                }
            });
        }
    }
    

    Here it is on jsFiddle.

    Edit:

    Note if you are dynamically adding and removing spinners, going, for example, from 49 spinners to 50 spinners can actually take 3-4 seconds on a fairly decent PC. Instead of destroying all of the old spinner(s), you can just destroy the spinners on the object you are cloning, which will speed things up significantly (takes ~300 ms). The actual object duplication is almost instantaneous; what takes a long time is re-applying all the spinners. So this is what I'm doing now in my production script:

    // Destroy spinner on object to be cloned
    $elem.find('.spinner').spinner( "destroy" );
    
    var $clone;
    
    // Add new clone(s)
    while (cur_number < desired_number) {
        $clone = $elem.clone(false, true);
        $("#where_to_put_it").append($clone);
    
        // Increment IDs
        $clone.find("*").each(function() {
            var id = this.id || "";
            var match = id.match(/^(.*)(\d)+$/i) || [];
            if (match.length == 3) {
                this.id = match[1] + (cur_rooms + 1);
            }
        });
    
        cur_number++;
    }
    
    // Re-apply the spinner thingy to all objects that don't have it
    $('.spinner').spinner({
        min: 0,
        max: 100,
        stop: function (event, ui) {
            // Apply the JS when the value is changed
            if (typeof $(this).get(0).onkeyup == "function") {
                $(this).get(0).onkeyup.apply($(this).get(0));
            }
            if (typeof $(this).get(0).onchange == "function") {
                $(this).get(0).onchange.apply($(this).get(0));
            }
        }
    });