Make your Twitter Bootstrap carousel images zoom 100% horizontal and vertical while keeping the aspect ratio

I noticed that the responsive design of the carousel in Bootstrap’s new version does not keep the aspect ratio of images resulting in white spaces (or whatever your background color is) around the images when you show them in a browser window that is smaller or larger than you designed for. Setting the width and height of the images would never work correctly due to the outer div’s that the carousel is nested in – trust me I googled and tried countless workarounds. The following css places somewhere after your bootstrap css file will override the container.

.carousel{
    height:100%;
}

.carousel-inner{
    height: 100%;
}

.carousel-inner>.active{
    height:100%;
}

But wait, that’s not all! The next step was to fill the inner div with a base64 transparent image with a width and height set to 100% in order to trick the browser into thinking that something was there that took up all the space. The following html replaces the example code in Bootstrap.

<div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
        <!-- Indicators -->
        <ol class="carousel-indicators">
            <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
            <li data-target="#carousel-example-generic" data-slide-to="1"></li>
        </ol>

        <!-- Wrapper for slides -->
        <div class="carousel-inner">
            <div class="item active" id="image1">
                <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="100%" height="100%">
                <div class="carousel-caption">

                </div>
            </div>
            <div class="item" id="image2">
                <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="100%" height="100%">
                <div class="carousel-caption">

                </div>
            </div>

    </div>

Finally, we need to add the image that will be scaled as a background image so that way at least in IE9+ and Chrome and Firefox the image will be shown in the original aspect ratio. To do this just use the following CSS.

#image1{
    background-image:  url(images/image1.jpg);
    background-attachment:fixed;
    background-position:center;
    background-repeat: no-repeat;
    -webkit-background-size: cover;
    -moz-background-size: cover;
    -o-background-size: cover;
    background-size: cover;
}
#image2{
    background-image:  url(images/image2.jpg);
    background-attachment:fixed;
    background-position:center;
    background-repeat: no-repeat;
    -webkit-background-size: cover;
    -moz-background-size: cover;
    -o-background-size: cover;
    background-size: cover;
}

That’s it!

Preserve your ‘onclick’ event when your button is moved to the menu in an Extjs5 toolbar via the overFlowHandler config

Not really a bug but something that you should really keep in mind when coding your next app if you happen to take advantage of the feature that allows the items in the toolbar to morph into menu items by setting the overflowHandler config to ‘menu’.

Here’s the scenario – you have a button on the toolbar with the xtype of ‘button and maybe an action of ‘doSomething’. You adhere to the MVC standards but you may be a little old school like 2013 and you use something like this in your controller.

var me = this;

me.control({
            'button[action= doSomething’]':{
                click           : me. doSomething’
            }
        });

You will also have to add the following because in Extjs5 the atype is changed when the button is moved to the menu automatically.

var me = this;

me.control({
            'button[action= doSomething’]':{
                click           : me. doSomething’
            },
            'menuitem[action= doSomething’]' :{
                click           : me. doSomething’
            }
        });

Don’t let this stop you from taking full advantage of the features available to you in Extjs5. Setting this is a great practice because I firmly believe it’s the details like being able to see the toolbar items when the user’s screen is smaller than you expected and I admit that I did not always set this in the past but now with my new focus on the small stuff

Mask all rows of a grid except the selected row in Extjs 4 or 5 – you pick

Extjs5 Notice – You will need this override in addition to use this – Ext.dom.Element.

In a lot of instances I find myself wanting the focus of a grid to rest solely on the selected row, and at the same time I want the remaining grid rows to become un-selectable as the focus should be on the selected row. What! You can’t imagine that scenario? Think about a grid with a form attached to it, clicking the row will populate the form, but if I click another row it will change the form to the contents of the new row. Maybe the user wanted to select the row, maybe not? Maybe I wanted the user to update the record they clicked on before moving on to the next row. Maybe there are just too many damn rows and this amounts to using a ruler to single out the working row – wait did I just show my age.

Fortunately, masking the non-selected rows on an Extjs grid is pretty easy. The code below has been tested in Extjs 4 and 5, there was a small class change in the grid row structure in Extjs5  so just find the line under Extjs4 in the comments and uncomment that while commenting out the line under Extjs5. I also noticed a Ext.dom.Element issue in Extjs 5 that popped up when upgrading this code so you will need the override discussed in this post() if you want to use this in Extjs5.

The following override will add the following methods for you to use to your grids:

maskAllRowsExceptCurrent : This masks all of the rows in the current view.

unmaskAllRows: This unmasks the rows.

Ext.define('overrides.Panel', {
    override    :'Ext.grid.Panel',

    constructor: function() {
        var me = this;

        me.callParent(arguments);
    },

    maskAllRowsExceptCurrent: function(){
        //noinspection JSUnresolvedFunction
        var grid            = this,
            nodes           = grid.getView().getNodes(),
            isBuffered      = grid.getStore().buffered,
            currentRecord   = grid.getView().getSelectionModel().getSelection()[0];

        if(currentRecord){

            grid.getSelectionModel().suspendEvents(false);
            // Show hide scrollbar so that the records out of view
            // will not be visible when the user scrolls using a
            // buffered grid.

            if(isBuffered){
                var gridViewDiv = grid.getEl().query('.x-grid-view');

                Ext.fly(gridViewDiv[0]).setStyle({
                    'overflow'  : 'hidden'
                });
                // Extjs 5
                nodes =  grid.getEl().query('.x-grid-row');
                // Extjs 4
                // nodes =  grid.getEl().query('.x-grid-data-row');
            }

            //var element = Ext.get(me.getUsersGrid().getView().getRow(index));
            for (var i = 0; i < nodes.length; i++) {
                var node    = nodes[i],  cells = node.childNodes,
                    record  =  grid.getView().getRecord(node);

                if (record != currentRecord){

                    for(var j = 0; j < cells.length || 0; j++) {
                        Ext.fly(cells[j]).mask();
                    }
                }
            }

            grid.hasMask = true;

        } else {

            grid.hasMask = false;

        }
    },

    unmaskAllRows: function(){
        //noinspection JSUnresolvedFunction
        var grid        = this,
            nodes       = grid.getView().getNodes(),
            isBuffered  = grid.getStore().buffered;
        //noinspection JSUnresolvedFunction
        grid.getSelectionModel().resumeEvents();
        // Show hide scrollbar so that the records out of view
        // will not be visible when the user scrolls using a
        // buffered grid.

        if(isBuffered){
            var gridViewDiv = grid.getEl().query('.x-grid-view');
            Ext.fly(gridViewDiv[0]).setStyle({
                'overflow'  : 'auto'
            });
            // Extjs 5
            nodes =  grid.getEl().query('.x-grid-row');
            // Extjs 4
            // nodes =  grid.getEl().query('.x-grid-data-row');
        }

        //var element = Ext.get(me.getUsersGrid().getView().getRow(index));
        for (var i = 0; i < nodes.length; i++) {
            var node    = nodes[i],
                cells = Ext.get(node).query('td');

            for(var j = 0; j < cells.length; j++) {
                Ext.fly(cells[j]).unmask();
            }
        }
        grid.hasMask = false;
    }
});
 

Small issue in Ext.dom.Element()’s query method that will potentially throw an error on empty query selectors in Extjs 5

There appears to be a small hangup in the Ext.dom.Element query method that allows an empty string to pass into the code which throws the following error: Uncaught SyntaxError: Failed to execute ‘querySelectorAll’ on ‘Element’: The provided selector is empty.

The error appears to take place when the selector[i] is an empty string in the code below, this scenario would easily pass the typedef validation since an empty string is well, an empty string, but then subsequently fails when passed to the querySelector. The following override will correct this issue in extjs5. Again if you need to know how to set up your overrides folder please see this post.

Ext.define('overrides.Element', {
    override    :'Ext.dom.Element',
    constructor: function() {
        var me = this;

        me.callParent(arguments);
    },
    query: function(selector, asDom, /* private */ single) {
        var dom = this.dom,
            results, len, nlen, node, nodes, i, j;

        if (!dom) {
            return null;
        }

        asDom = (asDom !== false);

        selector = selector.split(",");

        if (!single) {
            // only allocate the results array if the full result set is being
            // requested.  selectNode() uses the 'single' param.
            results = [];
        }

        for (i = 0, len = selector.length || 0; i <  len; i++) {
            if (typeof selector[i] === 'string' && selector[i] !== '') {

                if (single) {
                    // take the "fast path" if single was requested (selectNode)
                    node = dom.querySelector(selector[i]);
                    return asDom ? node : Ext.get(node);
                }

                nodes = dom.querySelectorAll(selector[i]);

                for (j = 0, nlen = nodes.length || 0; j < nlen; j++) {
                    results.push(asDom ? nodes[j] : Ext.get(nodes[j]));
                }
            }
        }

        return results;
    }
});

Whoa, my dual trigger (clearable) comboboxes in extjs4 stopped working after upgrading to extjs5

If you previously used configs like trigger2Cls and onTrigger2Click to add a second trigger to your comboboxes then you will notice that they do not appear after upgrading to extjs5, but don’t worry the correction is simple and takes only seconds. Just like the textfield that is discussed in this post (), the combobox in Extjs5 uses the trigger config to add multiple triggers after the dropdown caret. Change the following snippets in your comboboxes from:

Something Old:

trigger2Cls     : 'x-form-clear-trigger',
onTrigger2Click: function() {
                    var me = this;
                    me.clearValue();
},

Something New:

triggers: {
         // Dropdown trigger is always present
         //picker: {handler: 'onTriggerClick', scope: 'this'},
         clear: {
                  cls           : 'x-form-clear-trigger',
                  handler  : 'clearValue',
                  scope     : 'this'
          }
}

Just in case you were wondering what clearValue is, it’s an event I made up in the controller that sets the value to ‘’. That’s all there is to it.