John Main Logo

John Main

Code. Design. Hosting. Maintenance.

01
Nov '18

Ok many of you may already know this one, but it’s a relatively new one to me, and since non-HTML5 browsers are now mostly gone the way of the dinosaur, I’m much more comfortable relying on its elements for my core functionality. If you are still on IE9 or below then you deserve all you get!

This is as simple as I can make an AJAX form submission handler, that will also deal with files. It uses the HTML5 FormData object to serialise all the form data, including files, and allow us to pass it with the AJAX POST request.

$('.my-form').on('submit', function(e) {
var form_data = new FormData($(this)[0]);
$.ajax({
type: 'POST',
url: $(this).attr('action'),
data: form_data,
dataType: 'json',
cache: false,
contentType: false,
processData: false,
success: function(response) {
// Wahey!
},
error: function() {
// Oh dear
}
});
return false;
});

The ‘cache’, ‘contentType’ and ‘processData’ flags ARE necessary to make this work properly. The ‘dataType’ is optional, but if your server doesn’t send back JSON in response to an AJAX request as standard then you need to have a word with yourself!

Also notice that the FormData object needs the DOM version of the form, not the jQuery object, hence the need for “$(this)[0]” in the constructor.

Job. Done.

27
Mar '18

So this one had me puzzled for rather a while. We all know about the bootstrap event ‘shown.bs.modal’ that fires when a modal has been loaded. However, when the modal content is loaded via AJAX, I’ve found that can still be triggered a little bit in advance of contained elements loading in the DOM.

To give a concrete example, I encountered this when trying to activate datatables on a table that I was embedding in a modal. My script was set up as follows:

$('.modal').on('shown.bs.modal', function (e) {
$('.table-class').DataTable({
//Datatable options
});
});

This tended to work about 50% of the time, and after a lot of thought I realised that the times it didn’t run were due to the table element not being loaded into the DOM before the DataTable call is made. Even though the Bootstrap event had been fired, the DOM was yet to catch up.

Now there are various ways around this, most obviously by using ‘setTimeout’ to add a short delay before triggering subsequent logic, although this technique has always struck me as rather imprecise and risky. Fortunately, jQuery provides the ‘ready’ function which gives us all we need to make this logic work perfectly:

$('.modal').on('shown.bs.modal', function (e) {
$('.table-class').ready(function() {
$('.table-class').DataTable({
//Datatable options
});
});
});

All we are doing here is adding a subsequent observer to the element so that we can be sure the DOM is aware of it before we try to run any further logic. Simple as that!

21
Oct '17

Cloning simple markup is usually a fairly trivial task in jQuery, using the .clone() function, however it becomes a major headache when there are form elements involved. jQuery’s clone function effectively works based on the raw HTML of the page and as such any changes that have since taken place within the DOM are ignored. This doesn’t sound like such a big deal until you realise that includes changes to all form input elements; the clone will have these all reset to their initial values.

I’ve written a little function to get round this issue, which simply ‘fixes’ the current values fo all input fields (except files) in the markup, allowing them to be cloned correctly:

function fixValues(element) {
element.find('input[type=text]').each(function() {
$(this).attr('value', $(this).val());
});
element.find('input[type=checkbox], input[type=radio]').each(function() {
$(this).attr('checked', $(this).is(':checked') ? 'checked' : '');
});
element.find('select option:selected').each(function() {
$(this).attr('selected', 'selected');
});
element.find('textarea').each(function() {
$(this).html($(this).val());
});
}

Just trigger this function on the element you want to clone, immediately before calling the jQuery clone function (or, if you’re feeling really enthusiastic, you could even extend the clone function itself) and your values should all be copied over as expected.

29
May '17

Like me, you may find the jQuery $.getJSON function really useful for quick GET-based AJAX calls, and lament the lack of a corresponding POST function. Well here it is:

$.postJSON = function(url, data, func) { $.post(url, data, func, 'json'); }

You’re welcome!

13
Nov '16

Easy one this, just copy and paste code if you’re using Bootstrap.
Whenever a tab is changed, this code will take its ID from the trigger and put into the window location hash (AKA address anchor or, if you’re really posh, the fragment identifier).

//Remember tab state
$('ul.nav-tabs > li > a').on('shown.bs.tab', function(e) {
if($(e.target).attr('href')) {
var id = $(e.target).attr('href').substr(1);
window.location.hash = id;
}
});

The second section of code ensures that whenever the page is reloaded, or the page is linked to using a valid tab ID, the identified tab will be selected automatically.

//Switch tab state
if (window.location.hash) {
$("a[href='" + window.location.hash + "']").tab('show');
}

Done

28
Aug '13

You won’t often find me posting an IE only chunk of code here but today I’m making an exception as I have found myself writing a site specifically for use in a call-centre where I know for a fact that everyone will be using IE so I can make a few custom nice-to-haves with that in mind.

IE, for all its faults, is the only browser that will let you implement a copy to clipboard function without the need for Flash plugins. There are lots of tutorials out there that show you ways to do this, but I ended up building a very quick jQuery function that I thought I’d share.

This assumes that you have a container with a class of ‘copy_text’ and a button, link or other clickable element with the class of ‘copy_to_clipboard’:

$('.copy_to_clipboard').click(function(e) {
e.preventDefault();
if (!document.all)
return; // IE only
var copy_text = $(this).siblings('.copy_text').html();
var new_element = document.createElement('input');
new_element.setAttribute('type', 'text');
new_element.setAttribute('class', 'copy_text_dynamic');
new_element.setAttribute('value', copy_text);
document.body.insertBefore(new_element)
r = new_element.createTextRange();
r.execCommand('copy');
});

Now you’ll notice this has created an input element on the fly. This is because IE’s createTextRange call only works on input elements so we have to create one and fill it with the text that we want to copy to the clipboard.

The final part of this process is making sure that this newly created input element doesn’t show up on our page. You may have already noticed that you can’t hide an element (i.e. display: none) in IE if you want to do anything with it as it doesn’t bother putting hidden elements in the DOM. That being the case, as an alternative I prefer to fix the position of the element and send it way off the side of the screen like so:

.copy_text_dynamic {
display: block;
position: fixed;
left: -10000px;
}
14
Jun '12

I have no problems using jQuery plugins that are rather old, if they are well written and efficient for my purpose. However jQuery hasn’t maintained complete backwards-compatibility throughout it’s life, notably when in version 1.5 the ‘handleError’ function was removed.
It is very simple to put in a patch for older plugins that still use this function – just extend jQuery and add the function back in yourself. If you don’t care about the error messages you can just add an empty function block but I prefer to use something like this to give me a better clue about the errors I might be dealing with:

jQuery.extend({
handleError: function( s, xhr, status, e ) {
// If a local callback was specified, fire it
if ( s.error )
s.error( xhr, status, e );
// If we have some XML response text (e.g. from an AJAX call) then log it in the console
else if(xhr.responseText)
console.log(xhr.responseText);
}
});
15
May '12

In my opinion one of the big glaring fails in CSS is the lack of ability to make an element expand to fill the remaining space within its parent container. So often, particularly in mobile design, in order to build as flexible a layout as possible I want to let one element be the size it needs to be and have another fill the remaining space. That’s what this jQuery plugin does.
This version works for filling the remaining height, but I’m sure you can see how easy it is to change it to work for widths.

(function($) {
$.fn.dynamicElement = function() {
options = jQuery.extend({
spacing: 20
}, options);
this.each(function() {
var sibling_height = 0;
$(this).siblings().each(function() {
sibling_height += $(this).outerHeight(true);
});
var parent_height = $(this).parent().innerHeight();
$(this).height(parent_height - sibling_height - options.spacing);
});
}
})(jQuery);

Options
spacing – adds an extra gap. Do bear in mind though that the position of this gap will be dependent on how you have aligned your elements.

Calling the Function

$('.element').dynamicElement();
$('.element').dynamicElement({ spacing: 50 });
29
Apr '12

In the process of learning mobile application design with jQuery Mobile I struggled greatly with making my layouts work with different screen sizes. There are a hundred and one articles online about screen sizes, resolutions etc but no clear consensus on an easy way to make your applications scale from phone to tablet. So I thought I’d build a plugin to do just that.
The idea behind this is simply that you design you application on a certain device with a certain screen width and make sure all your fonts are in the correct proportion. If you set a base font size and then use em sizes on all the rest then it’s simply a case of using a little logic on the current screen width to calculate what that base font size should be in order keep your scale correct.

(function($) {
$.fn.dynamicText = function(options) {
options = jQuery.extend({
maxFontSize: 14,
minFontSize: 6,
baseFontSize: 10,
baseScreenWidth: 800
}, options);
var element = $(this);
doTextResize();
$(window).resize(function() {
doTextResize();
});
function doTextResize() {
var font_size = parseInt(options.baseFontSize * $(window).width() / options.baseScreenWidth);
font_size = (font_size = options.minFontSize) ? font_size : options.minFontSize;
element.css('font-size', font_size + 'pt');
}
}
})(jQuery);

Options

  • maxfontSize – maximum size it will adjust your base font to
  • minFontSize – minimum size it will adjust your base font to
  • baseFontSize – the base font size you used for your development
  • baseScreenWidth – the screen width you used for your development

Calling the Function

$('body').dynamicText({ baseFontSize: 14 });
$('.container').dynamicText({ maxFontSize: 8, minFontSize: 16 });

You’ll notice that, although you will generally want to call this function on the entire body of your page, you by no means have to. It will set the base font of any specified element and as such scale all those below it.

The window resize listener is not strictly necessary for a static page but it will trigger when you change the orientation of your device which could be useful.

20
Mar '12

Having recently upgraded my phone to a Samsung Galaxy SII, acquired an Asus Transformer Prime tablet and reached the end of a big work project I decided it was time to invest in my skill-set and learn the art of mobile application development.

After some research I decided that PhoneGap and jQuery Mobile were the tools I should be using. They bridge the gap between web development and mobile application development really well and, used correctly, produce and app as polished and powerful as one built by any other means.

The key thing to remember when developing is that your code will be rendered by a webkit engine so several of those tricks you might use for Firefox or IE won’t work. A key issue that had me stumped for a while was the fact that you can’t transform any elements that are hidden. However there is a flip side to this in that you are developing for webkit and only webkit so you don’t have to worry about any of that browser compatibility rubbish you normally have to account for in web development.

jQuery has some very good tutorials and demo code on their demo site. All their code is ready to drop straight into an application and is very much geared towards scalability and flexibility with device orientation.

PhoneGap is a full set of libraries to help you take control of your hardware though Javascript calls. I can’t even explain how useful these libraries are. They also have a great set of documentation.

Chances are that once you’ve got into the swing of setting up an app the main issue you will encounter is scaling your app to work on different screen sized and resolutions. This job gets even more difficult when you learn that a pixel is not always a pixel. I strongly recommend reading this article to shed a little light on the situation.

And finally some links and plugins that I have found incredibly useful:

PhoneGap Getting Started Guide

PhoneGap Plugins – The Video Player plugin is particularly useful

Photo Swipe – Great starting point for keeping views scalable and the code is easily modified to go into other apps