Keeping an images ratio on resize in JavaScript

A little tricky task was given to the team I am working in, to keep an image at its aspect ratio. However the catch is that it needs to be able to go as large as it can to fill the screen at all times, while the user can resize the screen and keeping the images size ratio. Easy you may say, but once you get into it, not so much.

Three of us thought long and hard through many different situation of how we can calculate the size, detect the users actions and calculate the ratio, but with no luck. We also then hunted the internet as all great developers do, which produced many methods using some very long calculation, but none just seemed to work and we knew nothing about the maths.

Then like a shine of light I thought of how you can do it in a simple way. Instead of thinking of the image at 1980px by 1020px and then calculate out the pixel difference etc, you need to think about the image in percentages. When you reduce the images width by say 594 pixel, you would then think of how to calculate the relative pixel difference on the height of the image, but this would be going in the realms of mathematicians. Therefore you stop thinking of that in pixels and think that 1980px reduced by 594px, is a 30% reduction.

Now all you need to do is reduce the height also by 30%, relative to the heights pixels, so here we go…

First we need to find out what the width and heights relative 1% is, so we know where to work from. You need to provide the images original height and width to start this off, then divide each by 100, simple maths.

// Get the one percent of the image width and height
var widthOnePercent = imageOriginWidth / 100;
var heightOnePercent = imageOriginHeight / 100;

Next because we want the image to be as large as it can, we will set the width to be as large as the container to start off the calculations. Once this has been set we can use the images size to find out what percentage the width is at. We know the width of what the image is at in pixels and we know how many pixels are 1%, therefore we can divide the images width by 1%. For example 1386px divided by 19.8px equals 70%.

// Calculate the width percentage
var imageCurrentWidth = $(imageSelector).width();
var imageCurrentPercent = imageCurrentWidth / widthOnePercent;

Now we just need to set the height of the image to 70% of the images height, by multiplying the images 1% height by 70%.

// Calculate the height relative to the percentage of the images width
var imageNewHeight = heightOnePercent * imageCurrentPercent;

However this will work to put the image within the width boundary of the container and the images ratio kept in tact, but potentially due to the width the height could be off the screen. Therefore we need to make sure the image doesn’t go off the screen. This can be calculated out by checking if the height of the image, plus the distance from the top of the window, is not as large as the windows height.

($(window).height() < $(imageSelector).offset().top + imageNewHeight)

Once we have determined this, then we do the same as before but in the opposite direction. This time we start with what height we want the image to be, instead of what height it is. To do this we use the windows height, minus the images top offset, which gives use what space is left.

// Set the new height so it fit on the screen
imageNewHeight = $(window).height() - $(imageSelector).offset().top;

Then the same as before, it is divided by the images 1% height to calculate what the percentage the width is going to be.

// Calculate out what percentage is the height at
imageCurrentPercent = imageNewHeight / heightOnePercent;

// Calculate the width relative to the percentage of the images height
var imageNewWidth = widthOnePercent * imageCurrentPercent;

Below is the full JavaScript code, but to see a full example check out my PureRandom CodePen.

(function($) {

function calIamgeSize(imageSelector, imageOriginWidth, imageOriginHeight) {

// make image as big as it can to start
$(imageSelector).width(
$(imageSelector).parent().width()
);

// Get the one percent of the image width and height
var widthOnePercent = imageOriginWidth / 100;
var heightOnePercent = imageOriginHeight / 100;

// Calculate the width percentage
var imageCurrentWidth = $(imageSelector).width();
var imageCurrentPercent = imageCurrentWidth / widthOnePercent;

// Calculate the height relative to the percentage of the images width
var imageNewHeight = heightOnePercent * imageCurrentPercent;

// If the images height is off the page, then rescale
if ($(window).height() < $(imageSelector).offset().top + imageNewHeight) {

// Set the new height so it fit on the screen
imageNewHeight = $(window).height() - $(imageSelector).offset().top;

// Calculate out what percentage is the height at
imageCurrentPercent = imageNewHeight / heightOnePercent;

// Calculate the width relative to the percentage of the images height
var imageNewWidth = widthOnePercent * imageCurrentPercent;

// Set new width of image
$(imageSelector).width(imageNewWidth);

}

// set new height of image
$(imageSelector).height(imageNewHeight);

}

// Resize image
var imageW = 1000;
var imageH = 667;
var imageClass = ".js-render-image";

$(window).on("resize", function() {

calIamgeSize(imageClass, imageW, imageH);

});

calIamgeSize(imageClass, imageW, imageH);

})($);

JQuery Validate for 3 field date

A difficult and challenging task I had been given is to use the JQuery validate.js to validate the date fields, but this was then split out into 3 fields. One for day, month and of course year, which sounds easy, but it was not. The issue comes that validate.js is built to validate one field on its own and we wanted to validate not just each field, but also as a whole date.

I researched the web and most developers suggested amending the JavaScript library file. However the version we had was minified and of course we had other teams using that copy, so using this method would affect others code. I thought about putting the validation for the whole date in with each of the fields, but this then produced the same validation each message for the whole date 3 times because it was doing it for each of the fields. There was a suggestion from developer to use a group method, but I couldn’t seem to get that working.

So I persisted to make my own method. This may not be the best, but I found it worked well and passed testing so it can’t be that bad. You can read more about the library of JQuery Validate here and learn the basics, which will make this all seem a bit easier.

First we need to validate each field on their own for example make sure the month is between 1 and 12. Instead of making a validation made for each field I made a universal method that all of them could use.

In the example below you can see in the ‘params’ parameter I passed in a array that has the minimal number and the maximum number. You would pass for example of month [1,12]. This is then validated on exit to check the ‘value’ of the field, stored in ‘dateValue’, is between or equal to the range.

$.validator.addMethod("vailddatefield",

function (value, element, params) {

var dateValue = parseInt(value, 10);
var Min = parseInt(params[0], 10);
var Max = parseInt(params[1], 10);
return this.optional(element) || (dateValue >= Min && dateValue <= Max );

},"Please enter a valid age"

);

You can then attach this to the field like so:

//Month validation
$(".month").rules("add", {

vailddatefield: [1,12],
messages: {
vailddatefield:  'Please enter a month value between 1 and 12'
}

});

The issue then comes when you want to validate these all together. If you was to enter the validation for the whole date in the method above then when an error happened it would produce the message once for each field with the same text. This then means we need a single field to evaluate if the full date is valid and then validate against that.

If you add the below snippet into the previous method, you can then pass the class or IDs for each of the date fields. These can then be concatenated together to make a full date, then put into a hidden field. This now holds our full date string in good format ready for validating.

//validate full date
var day = $(params[2]);
var month = $(params[3]);
var year = $(params[4]);
var fullDate= $(params[5]);

$(fullDate).val($(day).val() + '/' + month.val() + '/' + year.val());

This however still doesn’t solve how we are going to validate it. To fire the validation by the library as normal, you need to give focus to the field then leave it, which is impossible because it is hidden. So we use some magic… If you put the below statement in, it forces the JavaScript to validate that field. This then runs its attached validation from the library. If you enter this into the previous method as well, so no matter which fields they start or finish in, it will validate the full date in the end. You will also only get only one message to appear if the field is invalid.

$(fullDate).valid()

Something I also did was stop it validating until all the fields are complete, as you don’t want to say their date is invalid before they even get chance to fill it out, so I changed the above to be the below. This then checks that each field has it minimal length of value before it validates the whole date.

if ((dayBirth.val().length >= 1) && (monthBirth.val().length >= 1) && (yearBirth.val().length > 3)) {

$(fullBirth).valid()          

 }

You can now see the whole code for the validation method. This will validate each field on its own, while also validating the whole date. You will need to add your own method for validating the full date, but if you read up on the library this should be easy.

/// Check Valid date
$.validator.addMethod("vailddatefield",

function (value, element, params) {

var dateValue = parseInt(value, 10);

//Get max and min dates
var Min = parseInt(params[0], 10);
var Max= parseInt(params[1], 10);

//validate full date
var day = $(params[2]);
var month = $(params[3]);
var year = $(params[4]);
var fullDate= $(params[5]);

//enter full date
$(fullDate).val($(day).val() + '/' + month.val() + '/' + year.val());

//Validate full date
if ((dayBirth.val().length >= 1) && (monthBirth.val().length >= 1) && (yearBirth.val().length > 3)) {

$(fullBirth).valid();

}

return this.optional(element) || (dateValue >= Min && dateValue <= Max);

},"Please enter a valid age"

);

Below here you can then see the example of calling the new methods for Month and for the full date. You can always also add more validation methods.

//Month required
$(".validate-month").rules("add", {

vailddatefield: [1,12,'.day','.month','.year','.fulldate'],
messages: {

vailddatefield:  'Please enter a month between 1 and 12'

}

});

//full date
$(".validate-date").rules("add", {

vailddate: '',
messages: {

vailddate:  'Please enter valid date'

}

});

Any thoughts and other methods people have seen can be shared in the comments section below.

ARIA Control JavaScript Library

This is a library to automatically inject the standard for screen readers mark-up ARIA. ARIA is Accessible Rich Internet Applications which is a way to make websites and applications more accessible to people with disabilities.

This libraries purpose is to automatically inject the necessary tags and commands to the users mark-up, so that it meets the standards as much as possible. There are also commands for the users to inject the methods on to specific parts if the library does not get them automatically. Also the automatic functionality can be turned off if only partly needed.

Download the library on GitHub 

View the examples on CodePen

Here are some helpful links about ARIA to help understand the tags and their values.

 

ARIA Control Library V1 Method Breakdown

Hidden (has auto)

This detects all the elements that are ‘display:none’ and/or ‘visibility:hidden’. It will then add the ARIA ‘aria-hidden’ and give it a role of ‘presentation’. This makes the element hidden from screen readers. There is also a method to make the element un hidden if the state changes.

Hidden Message (has auto)

This method uses the given class in the options to make the element display off the page. This means that the screen reader can see the message and read it out, but sighted users won’t be able to see the message. This is useful if you want to give extra information only to the users using a screen reader.

Notifications

This sets the attributes for ‘aria-atomic’, ‘role’ and ‘aria-live’. These are the attributes best served as a notification.

Alert (has auto)

This is for error messages and other alerts to the user. This will use the notification method above with the settings as ‘aria-atomic: true’, ‘role: alert’ and ‘aria-live: rude’. This can be automatically run with a set class.

Warning (has auto)

This is for warning messages and other none critical alerts to the user. This will use the notification method above with the settings as ‘aria-atomic: true’, ‘role: alert’ and ‘aria-live: assertive’. This can be automatically run with a set class.

Message (has auto)

This is for any message that is not critical like a success message. This will use the notification method above with the settings as ‘aria-atomic: true’, ‘role: alert’ and ‘aria-live: polite’. This can be automatically run with a set class.

Required (has auto)

Any element, usually inputs, that has the attribute ‘required’ on them will have the ARIA tag ‘aria-required’ added to the element. This can also be done manually by passing the class.

Popup (has auto)

With this method you need to set up the popup classes for the control and the popup. It is mainly aimed at the tooltip example, e.g. if you click in a field and then a tooltip shows. The method will give the control the aria tags to show it has a popup and the tooltip the tags to show what controls it. You can also pass the role type, but by default it is ‘tooltip’.

Show and Hide Popup

These are more helper methods to the above. When the popup shows or hides you can call these method to update the elements settings.

Checked Elements (has auto)

Both Checkboxes and Radio buttons will automatically have the check item injected with the ARIA ‘aria-checked’. This marks if the element is checked or not, for which the change event is also added to all of the elements so if it becomes checked or unchecked then it is updated.

Disabled (has auto)

Any disabled element will have the ‘aria-disabled’ tag added to it.

Selected Option (has auto)

This will make all the options in a select element ‘aria-selected: false’ then find the selected option to set it to true. It also adds the event change to detect when the selected has changed and updates them.

Max and Min (has auto)

This will mainly be for inputs with a max or min value. It finds if they have a value then puts them in their relative tags, either ‘aria-valuemin’ or ‘aria-valuemax’.

Navigation (only has auto)

A complex one, but easy to set up. With all the correct settings this can add the tags to show up to screen readers as a navigation. It will also tag the links as menu items and the sub menu as well as being a popup. It uses the hover event to detect when the sub menu is being shown.

Button (has auto)

This will detect the all button types and add its role as ‘button’

 

ARIA Control Library V1 Options Breakdown

autoDetect (default = true)

This determines if to automatically tag the elements or to only allow manual running.

alertClass (default = .acAlert)

Used for the auto detection for Alert Notifications.

warningClass (default = .acWarning)

Used for the auto detection for Warning Notifications.

msgClass (default = .acMsg)

Used for the auto detection for Message Notifications.

popupClass (default = .acTooltip)

The class of the popup or, as the example was, the tooltip.

popupCtrlClass (default = .acTooltipCtrl)

The element that triggers the popup or tooltip to show.

popupType (default = tooltip)

The role of the popup.

hiddenMsgClass (default = .acHiddenMsg)

The class of any hidden messages that don’t show to slighted users.

navClass (default = .acNav)

The navigation containers class.

navParentClass (default = .acSubNav)

The top level links class.

navSubNavClass (default = .acSubMenu)

The sub menu class.

What if the worst happens?

I see a lot of companies rush, rush, rush and push, push, push to get things in fast. They want to deliver new features every second if they could. However this has big risk and future problems they keep coming. I always say Prepare for the Worst and Hope for the Best!

This I find is the best saying to use, whenever you think ‘should I just do this little thing or not’. If you ever look at a problem and think something could go wrong here then put in a worst case solver.

Some of the trip ups I have see have been to do with logging, which can either be to little or even to much. You can always do with logging to save you in the live environment. When issues start happening you need to diagnose what the issue is straight away, but you can’t get the details without some logging as to what is going on.

However you should only log the things that could fail, but of course shouldn’t be. An example is, I have created a method that actions dynamic changes on multiple elements, but if one element is removed I don’t need to know about it. I just need the method to continue running, so I could put a try catch round it and then let it go with no logging. This prepares for the worst and then lets the best happen. However If I have built a method that the application needs all elements to be present, then I want to log the error for reporting so it can be fixed as soon as it is seen.

There has been sometimes that someone has added masses of logging at every section, then the server starts throwing warnings up or just falls over. So your best is that the program works as you need, but your worst preparation is you have logging to review any errors. You then also have the best prepared with as much useful logging for review as you need and also the worst is you don’t put in unnecessary logging. There is always a balance between too much and too little that needs to be looked at.

Something logging and other attributes can do is take up memory. This can then build up and take down servers, as I have recently also found. I deployed a nice new OSB service and added one new log that would be fired each time someone submitted the form. This to me didn’t seem much as it was a small file, but then once it was live there was a report about the logging from the support team. Although it was a lot of logging that could build up over time, it was also not helped that the server was full 95%. This means it could fill up at any point.

If you are going to have a well organised and dynamic server, then there should be scripts run daily or monthly that clear up the files. This could either delete the logs older than X number of days or even just package them and send to another locations. It is super risky to be running a server at 95% full and expect nothing to tip it over the edge. Regular maintenance and checks should be carried out so that this is caught at the first site.

One that all developers should be using is a good old Try Catch. If you are not sure what one is, without being too patronizing, it looks like this in JavaScript for example:

try {
//Code attempted
}
catch(ex){
//Exception caught
}

You should use these anywhere there could a potential break even if you want something to react to it or not. For example you would put one round a submission from a form. This would then catch if it failed and for what reason, then from this you could display a notice informing the user what to do next. You could also do it for example if you are dynamically loading content in a loop, you could put one round the code within the loop. If one of the calls to get content fails it could trip over the Try Catch, but then continue getting the rest of the content. The user then need not know that something went wrong and then you can either send an error message to support or just let it continue.

These can be really helpful to stop your application from breaking in unwanted ways like with server errors, but again you don’t want to over use them and you want to be sure they errors are being picked up.

Another method to get around the errors cropping up is putting default values. I stepped into this one when someone create a switch statement, but with no default case (C#) and the variable that was being set inside was set to null at the beginning. This was working fine for some time as the cases that were there met the demand, but further down the line the content editable variable that was being checked in the switch had changed. This meant that the variable being returned was null and breaking the application.

To get around this you should first try to make the check dynamic or dynamically changeable, so when a new value is added then the new result can also be generated. The other thing that should always be done with Switch statements or any other kind of statement that provides the option, is put a Default. Example:

Switch (Number){
Case 1:
//Do thing
Break;
Case 2:
//Do this
Break;
Default:
//Or else do this then
Break;
}

It just means that there is a result from the statement no matter what. At best, unlike this scenario, if you are setting a variable in the statement you should always return a valid result and then handle the result. For this instance an empty string could have been returned and all would have worked fine.

These are some of the ways developers can prepare for the worst and stop stupid mistakes. If you have seen any silly things happen that could be avoided then comment below with what, why and most importantly how to fix it.

 

Sticky Menu Bar code and example

This is a bit of a feature a lot of websites use and love, which is the Sticky Menu Bar. This could also be used for things like a cookie policy bar as well, but what ever the use it is a good feature. However I see some people add these with some terrible code and methods, because they don’t understand how it would work. I will explain my method and why I think it is better.

Although it is all done via JavaScript and JQuery, we will start with the HTML. This is sort of simple that you can have what ever you like, but you need the navigation wrapped in a container. For my example I have the HTML5 ‘nav’ element as my container and then the actual navigation as an Unordered List.

<nav >
<ul >


<!--ul>

This will be explained further on, but the reason for this is so your content does jump.

For the CSS it is pretty much just standard styling more for the benefit of the visual of the navigation, not for the functionality. The ‘nav’ has colour, the ‘ul’ removes the bullet points and then the final styling to bring the link containers inline.

nav {
background-color: #0073AA;
}

ul {
list-style: none;
}

li {
display: inline-block;
color: white;
padding: 10px 0;
}

Then is the JavaScript/JQuery that I have done in a prototype, so that it can be added once then used however many times. The basics to set this up is the initialisation method and the defaults object added to the prototype.

The ‘defaults’ contain 3 parameters that are the ‘itemClass’ the class given to the navigation controller for my example the ‘ul’, the ‘itemParent’ the class given to the parent of the navigation the ‘nav’ and finally the ‘stickyClass’ which is the class given to the item once it has stuck to the top of the screen. This is so if you want to style it different once it has stuck. The defaults, mean if you don’t want your own classes then there are back up classes instead.

StickyBar.prototype = {
defaults: {
itemClass: "sticky_item",
itemParent: "sticky_parent",
stickyClass: "sticky_class"
},
init: function() {
}
}

You also have a ‘init’ method attached. This is where we action the method to run the functionally of the navigation. This is below and starts off with putting the object into a scope called ‘base’. This give the ‘this’ the scope of the method.

We then have the on scroll attribute of JQuery to read each time the user scrolls, you can tell where they are. Next I have put in the ‘each’ attribute so if you have multiple sticky bars with the same class, they are all picked up and checked. You can have multiple sticky bars, as the position of these are done in the CSS, so as long as you have a different class on each for their direction then you can have more than one.

Within here is the real thinking. With the if command we check if the scroll position of the window is further down the page then the navigation. This is the reason we have the navigation container, as once you have stuck the navigation to the page it moves with the page and therefore the window will never scroll back over it. With the container you can stick the content inside and then use the container as the reference point.

If it is to be stuck then we add some inline CSS to position fix it to the window and add the sticky class to the navigation. If it is not to be stuck then it does the opposite. There is also a bit of functionality to give the parent element some height, because once you stick the navigation it is not relative on the page so all the content will shift up. Instead we add the height on as a placeholder. I have added it here, so that if your item change for instance a drop down menu or you also have multiple stick bars, then it works for all.

startScroll: function() {
var base = this;
$(window).scroll(function() {
$(base.itemClass).each(function() {
if ($(window).scrollTop() >= $(this).parent(base.itemParent).offset().top) {
$(this).css({
'position': 'fixed'
})
$(this).addClass("stickyClass");
$(this).parent(base.itemParent).css({
'height': $(this).height()
});
} else {
$(this).css({
'position': 'relative'
});
$(this).removeClass("stickyClass");
$(this).parent(base.itemParent).css({
'height':'auto'
});
}
})
})
}

Once last thing we need to do is add it to the ‘init’ method and it is complete. Please also notice the object StickyBar that sets the each sticky bar and the IFFY around everything to run this on load.

All you then need to do to initialize each instance is call the object and add in the optional parameters, which again I have put in an IFFY to have it load straight away.

//New Sticky
(function() {
var stickyBar = new StickyBar({
itemClass: '.nav_bar',
itemParent: '.nav_bar_parent',
stickyClass: 'nav_bar_stuck'
});
})();

Please see the full code and an example on CodePen