Using jQuery.resize to Enhance Responsive Design

Twitter Bootstrap provides some convenient grid scaffolding to use for responsive design applications.  The xs-sm-md and lg classes allow you to adjust your design based on the screen real estate available.  Sometimes, however, this isn’t enough.  Recently when working on laying out a couple portlets horizontally on a page I discovered that I needed some additional control over the display (or lack thereof) of some columns in my portlets. In short, I wanted to hide some of those columns when the width of the screen was at a certain horizontal threshold.  The standard bootstrap classes were not granular enough for my needs.    Time for some jQuery wizardry.

Plan:

  1. Use $(window).resize to listen for the window size changing
  2. Check the size of the desired elements against certain thresholds
  3. Perform DOM manipulation as necessary to add/remove elements
	<div class="panel panel-primary panel-heading-custom">
		<div class="panel-heading">
			<h2 class="">
				Extending Responsive Design with jQuery.resize()
			</h2>
		</div>
		<div class="row row1">
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">Panel Left</div>
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">Panel Right</div>
		</div>
		<div class="row">
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">
				Width:<span id="panel-left-width"></span>
			</div>
			<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-header">
				Width:<span id="panel-right-width"></span>
			</div>
		</div>
		<div class="row">
			<div id="panel-left" class="col-lg-6 col-md-6 col-sm-6 col-xs-6 panel-body-custom">
				<div class="panel-body-inner-left">
					<table>
						<thead>
							<th class="c1">Column 1</th>
							<th class="c2">Column 2</th>
							<th class="c3">Column 3</th>
							<th class="c4">Column 4</th>
						</thead>
						<tbody>
							<tr>
								<td class="c1"></td>
								<td class="c2"></td>
								<td class="c3"></td>
								<td class="c4"></td>
							</tr>
						</tbody>
					</table>
				</div>
			</div>
			<div id="panel-right" class="col-lg-6 col-md-6 col-sm-6 col-xs-6  panel-body-custom">
				<div class="panel-body-inner-right">
					<table>
						<thead>
							<th class="c1">Column 1</th>
							<th class="c2">Column 2</th>
							<th class="c3">Column 3</th>
							<th class="c4">Column 4</th>
						</thead>
						<tbody>
							<tr>
								<td class="c1"></td>
								<td class="c2"></td>
								<td class="c3"></td>
								<td class="c4"></td>
							</tr>
						</tbody>
					</table>
				</div>
			</div>
		</div>
	</div>

The following bit of javascript will check the size of the panels and remove a column if the width of the panel becomes less than 500 (and then 400) pixels.

$('document').ready( function() {
	// initialize panel width display
	resizer();

	$(window).resize(function(){
	    resizer();
	});
});

function resizer() {
    $('#panel-left-width').text($('#panel-left').width());
    $('#panel-right-width').text($('#panel-right').width());
    plw = $('#panel-left').width();
    prw = $('#panel-right').width();
    $('#panel-left-width').text(plw);
    $('#panel-right-width').text(prw);
    if (plw &lt; 500) {
	$('.c4').hide();
    } else {
	$('.c4').show();
    }
    if (prw &lt; 500) {
	$('.c4').hide();
    } else {
	$('.c4').show();
    }
    if (plw &lt; 400) {
	$('.c3').hide();
    } else {
	$('.c3').show();
    }
    if (prw &lt; 400) {
	$('.c3').hide();
    } else {
	$('.c3').show();
    }
}

Simple and effective. This could be extended to other uses as well, taking into account different devices in addition to screen size.

See a working demo here.

Advertisements

Converting HTML Tags to XPage Tags

As xPages becomes a common platform for other modern web frameworks, you may find yourself writing a lot of standard HTML (such as Twitter Boostrap markup) in your Domino Designer source panel.  Here is a convenient reminder that most HTML tags can be converted to xPage tags by adding the xp: prefix.

So,

<div>...</div>

becomes

 

 <xp:div>...</xp:div>

Now it may not be immediately obvious why this would be a useful pattern but xPage tags have with them all the API hooks that are available for the standard xPages controls.  One use case could be to leverage server side logic to selectively render certain parts of your code.   For example you might have some mark up that resembles this:

<div id="show-when-editable" class="row">
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</div>
<div id="hide-when-editable" class="row">
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</div>

If you want to show the first div in edit mode and the second otherwise you could convert the above to the following:

<xp:div styleClass="row">
<xp:this.rendered><![CDATA[#{javascript:document.isEditable()==true}]]></xp:this.rendered>
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</xp:div>
<xp:div styleClass="row">
<xp:this.rendered><![CDATA[#{javascript:document.isEditable()==false}]]></xp:this.rendered>
<div class="col-md-2">...</div>
<div class="col-md-4">...</div>
<div class="col-md-6">...</div>
</xp:div>

A couple things to note about this type of tag conversion:

–  <xp:div> tags don’t respect normal HTML attributes.  For this reason, you need to also convert class="class-name" to styleClass="class-name"

– To use other attributes you can add an attribute block.  For example, code to add a data-title="My Title" to your tag would look like this:

<xp:this.attrs>
<xp:attr name="data-title">
<xp:this.value><![CDATA[#{javascript:'My Title'}]]></xp:this.value>
</xp:attr>
</xp:this.attrs>

– I haven’t tested this with all legal HTML tags, so there may be some incompatibilities but it does work well with most of the standard building block tags used for construction Boostrap grids and tables.  If you know of other limitations, please feel free to share.

A Stoplight Status Component Using Bootstrap

I had a request to create a “stoplight” status section in a recent application.  The purpose of this section would be for the users to register a red, yellow, green status to a number of categories on a product development information form.

Using a combination of Twitter Bootstrap and jQuery I was able to create a nice looking component that provided the exact functionality they were hoping to achieve.

Here is a link to the demo page where you can see this in action.

Button Code

<div>
<a id="cat_1_btn" class="cat1" href="javascript:statusToggle('cat1')" role="button">
Strategic Support</a>
<a id="cat_2_btn" class="cat2" href="javascript:statusToggle('cat2')" role="button">
Competitive Landscape</a>
<a id="cat_3_btn" class="cat3" href="javascript:statusToggle('cat3')" role="button">
Product Development</a>
</div>

statusToggle Code

All the below function does is to check the current CSS class of the button and toggle it to the next one.  Very simple.

function statusToggle(category) { 
 if ($("." + category).hasClass("btn btn-success btn-xs btn-custom")) { 
   $("." + category).removeClass("btn btn-success btn-xs btn-custom")
   $("." + category).addClass("btn btn-danger btn-xs btn-custom")
   $("." + category + "Status").val("danger")
 } else if ($("." + category).hasClass("btn btn-danger btn-xs btn-custom")) { 
   $("." + category).removeClass("btn btn-danger btn-xs btn-custom")
   $("." + category).addClass("btn btn-warning btn-xs btn-custom")
   $("." + category + "Status").val("warning")
 } else if ($("." + category).hasClass("btn btn-warning btn-xs btn-custom")) { 
   $("." + category).removeClass("btn btn-warning btn-xs btn-custom")
   $("." + category).addClass("btn btn-success btn-xs btn-custom")
   $("." + category + "Status").val("success")
 }
};

The CSS classes set by the above code is standard Twitter Bootstrap CSS. The final btn-custom class only serves to add some margins and bold weight to the button text.  Nothing special there.

Interestingly enough, the above code was initially implemented in an Oracle APEX application.   Since it was all javascript and CSS I was able to easily port the code over to a sample xPages application without changing really anything.

Using Twitter Bootstrap to Create a Mobile Schedule for a Youth Sports League

This baseball season I am the coordinator for my son’s second grade league.  I decided to create an app for all the parents that would include field information, schedules and coaches contact information.  Using Twitter Bootstrap makes this really easy because most of the components are responsive and will work with any size screen.

If you take a look at the demo application I have setup you can see that while it works best on a mobile phone device, it can be viewed just as clearly in a large screen web browser.

The magic in the schedule and teams pages is that I used a repeat control bound to a Notes view and then wrote a SSJS function to return HTML with the bootstrap tags to achieve the desired look.   Simple and effective.

Repeat Control Code

<div class="panel-body">
<xp:repeat id="repeat1" value="#{view1}" var="row" rows="300">
<xp:text escape="false" id="computedField1"
value="#{javascript:return buildScheduleTable()}">
</xp:text>
</xp:repeat>
</div>

SSJS Code

function buildScheduleTable() {
var tHTML = "";
tHTML += "
<div class=\"panel panel-primary\">
<table class=\"table\">"
tHTML += "
<tbody>"
tHTML += "
<tr>"
tHTML += "
<div class=\"panel-heading\"><b>"
tHTML += row.getColumnValue("event_dt")
tHTML += "</b></div>
"
tHTML += "
<div clas=\"panel-body\">"
tHTML += "
<td>"
if (row.getColumnValue("event_status") == "Canceled") {
tHTML += "<span class=\"label label-danger\">CANCELED</span>
"
} else if (row.getColumnValue("event_type") == "Practice") {
tHTML += "<span class=\"label label-default\">Practices</span>
"
} else if (row.getColumnValue("event_type") == "Game") {
tHTML += "<span class=\"label label-warning\">Games</span>
"
} else {
tHTML += "<span class=\"label label-danger\">Other</span>
"
}
tHTML += "<b>Cherryview #1 </b>" + row.getColumnValue("location1")
tHTML += "
<b>Cherryview #2 </b>" + row.getColumnValue("location2")
tHTML += "
<b>Village Creek </b>" + row.getColumnValue("location3")
if (row.getColumnValue("location4").trim() != "" ){
tHTML += "
<b>Oak Shores </b>" + row.getColumnValue("location4")
}
tHTML += "</td>
"
tHTML += "</div>
"
tHTML += "</tr>
"
tHTML += "</tbody>
"
tHTML += "</table>
</div>
"
return tHTML
}

Message me if you’d like a copy of the demo application NSF.