Batu.Khan
Batu.Khan

Reputation: 3065

Is there a built-in method for selecting closest n siblings

Consider the structure below

Buttons

What i want is to select closest 9 buttons on clicking on a button and do lets say changing bg colors. And here's my custom code already doing that

$(document).on("click", "#footer button", function(){
    var index = $(this).index();
    var len = $("#footer button").length;
    $("#footer button").css({
        "background-color" : "#ccc"
    });
    if (index < 5) $("#footer button:lt(9)").css({
        "background-color" : "#c99"
    });
    else if (index > (len - 6)) $("#footer button:gt(-10)").css({
        "background-color" : "#c99"
    });
    else $("#footer button").slice((index -4), (index + 5)).css({
        "background-color" : "#c99"
    });
});

Now, i find using if .. else .. blocks for jquery selectors lame somehow. Of course we gotta use it if we have to but in this case do we? Are there any built-in methods to chain for such purpose in jquery?

HERE is the fiddle to play with.

Upvotes: 6

Views: 109

Answers (2)

David Thomas
David Thomas

Reputation: 253318

There's no built in method to do this, but it's easily done without using if/else:

$(document).on("click", "#footer button", function () {
    var that = $(this),
        index = that.index(),
        prev = that.prevAll('button:lt(4)'),
        next = that.nextAll('button:lt(4)');
    that.siblings().removeClass('highlight');
    that.add(prev).add(next).addClass('highlight');
});

JS Fiddle demo.

Incidentally, a simple plugin could be easily created/used:

(function($){
    $.fn.rangedHighlight = function(range,highlight) {
        var that = this,
            prev = that.prevAll().slice(0,range),
            next = that.nextAll().slice(0,range);
        that.siblings().addBack().removeClass(highlight);
        that.add(prev).add(next).addClass(highlight);
        return this;
    };
})(jQuery);

$('#footer').on('click', 'button', function(){
    $(this).rangedHighlight(4,'highlight');
});

JS Fiddle demo.

Unfortunately I hadn't noticed, until it was pointed out in the comments, the necessity of always highlighting the full specified range of elements, even if that offsets the clicked element from the centre of the highlighted section. There doesn't seem to be any way of doing this without using an if/else of some sort (though I'm trying to simplify it).

While the above remains true (there is no built-in method), I did decide to rewrite the plugin to offer the choice to do so (in case it's of any use to you):

(function($){
    $.fn.rangedHighlight = function(opts) {
        var that = this,
            index = that.index(),
            s = $.extend({
                'range' : 9,
                'highlight' : 'highlight',
                'highlightClicked' : false,
                'alwaysShowFull' : true,
                'returnRange' : false
            }, opts),
            eitherSide = Math.floor(s.range - 1)/2,
            all = that.parent().children(),
            leftLimited = index < eitherSide,
            rightLimited = index > all.length - eitherSide - 1,
            rangeMin, rangeMax, returnObject;

        that.addClass(s.highlightClicked, 'string' === typeof s.highlightClicked);

        if (!leftLimited && !rightLimited) {
            rangeMin = index - eitherSide;
            rangeMax = index + eitherSide + 1;
        }
        else if (s.alwaysShowFull && (leftLimited || rightLimited)) {
                rangeMin = leftLimited ? 0 : all.length - s.range;
                rangeMax = leftLimited ? s.range : all.length;
        }
        else if (!s.alwaysShowFull && (leftLimited || rightLimited)) {
                rangeMin = leftLimited ? 0 : index - eitherSide;
            rangeMax = leftLimited ? index + eitherSide + 1 : all.length;
        }

        that.siblings('.' + s.highlight).removeClass(s.highlight);
        all.slice(rangeMin, rangeMax).addClass(s.highlight);

        returnObject = s.returnRange === false ? this : all.slice(rangeMin,rangeMax);

        return returnObject;
    };
})(jQuery);

$('#footer').on('click', 'button', function(){
    $(this).rangedHighlight({
        // Number: number of elements _in total_ to be highlighted:
        'range' : 7,
        // String: the class-name to be applied to selected elements:
        'highlight' : 'highlight',
        // Boolean: shows the full range even if that range 'overlaps'
        // the start/end points:
        'alwaysShowFull' : true,
        // Boolean: return the selected range (true) or the clicked
        // element (true), for chaining purposes:
        'returnRange' : false,
        // String: specific class to add to the clicked element:
        'highlightClicked' : false,
    });
});

JS Fiddle demo.

References:

Upvotes: 3

Bill Criswell
Bill Criswell

Reputation: 32921

You can just use .slice(), the only thing you have to watch out for is a negative start index which is easy to get around.

$(document).on("click", "#footer button", function(){

    var index = $(this).index(),
        range = 9, 
        startIndex = index - range, 
        endIndex = index + ( range + 1 );

    $("#footer button")
        .removeClass('selected')
        .slice(startIndex > 0 ? startIndex : 0, endIndex)
        .addClass('selected');

});

Small demo: http://jsfiddle.net/x2QJP/9/

Upvotes: 0

Related Questions