Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

jquery

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.

like image 388
Batu.Khan Avatar asked May 02 '14 13:05

Batu.Khan


1 Answers

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:

  • JavaScript:
    • Conditional ('ternary') operator (condition ? conditionIsTrue : conditionIsFalse).
    • Math.floor().
    • typeof.
  • jQuery:
    • add().
    • addBack().
    • addClass().
    • children().
    • index().
    • Greater-than (:gt()) selector.
    • Less-than (:lt()) selector.
    • nextAll().
    • on().
    • parent().
    • prevAll().
    • removeClass().
    • siblings().
    • slice().
like image 66
David Thomas Avatar answered Nov 10 '22 14:11

David Thomas