{{announcement.body}}
{{announcement.title}}

Fixing JavaScript Cross-Browser Compatibility Issues

DZone 's Guide to

Fixing JavaScript Cross-Browser Compatibility Issues

If you're a web developer and you suffer from migraines, it's probably because cross-browser compatibility. Hopefully this post can serve as a panacea.

· Web Dev Zone ·
Free Resource

Out of all major web technologies, there is no other technology as vilified for cross-browser compatibility as JavaScript. Despite the advancements in pure HTML and CSS, it’s true that you cannot easily build web apps or websites without it.

In this article, we look into cross-browser compatibility issues with JavaScript and methodologies to fix the issues. Before we get started with the actual topic of discussion, it is important that we look into the basics and evolution of JavaScript.

Diving Into JavaScript

There have been rapid changes in web development iver the last decade, along with the emergence of different kinds of devices – desktop, smart phones, tablets, etc. There has also been an increase in the number of web browsers used for surfing the internet. This poses a different set of challenges for designers and developers since different browsers could interpret CSS and HTML code in a different manner. The reason behind is that every browser has a unique rendering engine, responsible for rendering web elements in a way that is different than other browsers. CSS, HTML, and JavaScript are three layers of progressive enhancement. Progressive enhancement is a technique for creating cross-browser compatible web apps wherein the highest priority is the core web page, while the other complex add-ons and features remain secondary. When JavaScript was introduced in the 1990s, there were major cross-browser compatibility issues since every browser development company had their own script implementation and this was primarily done to gain market dominance. Though such issues do not occur now, handling cross-browser compatibility issues with JavaScript can still cause nightmares for developers.

Problems with JavaScript code majorly occur when developers use features in web pages that do not support old browsers, use incorrect DOCTYPEs, or use an incomplete/incorrect implementation of browser sniffing code. Unless a standard mechanism for processing JavaScript (or other scripting languages) is implemented, cross-browser compatible issues with JavaScript will continue to persist. Let’s look into these cross-browser compatibility issues with JavaScript and learn a bit about the mechanisms for fixing them.

Common JavaScript Problems

Before we look into cross-browser compatibility issues with JavaScript, it is important that we look at some of the common JavaScript problems. It is assumed that you are already aware about JavaScript and have prior implementation experience with JavaScript.

  • Memory Leaks are one of the common problems faced by developers. Memory leak simply means that memory that was previously being used by the application is no longer being used. However, for some reason (e.g. incorrect handling of global variables, out of DOM references, etc.); the allocated memory is not returned back to the ‘free’ pool of memory. Some of the common reasons for memory leaks are incorrect handling of global variables and out of DOM references. ‘Profiling Tools for Chrome’ can be used for memory profiling as well as identifying memory leaks. Below is a sample snapshot of Chrome Memory Profiling in action.
  • Cross Browser Testing

  • JavaScript executes the code in the order in which it appears in the document. Hence it becomes important to reference code only when it is loaded. If you are referencing code before it is loaded, the code would result in an error.
  • Unlike other languages, no error is thrown if you pass an ‘incorrect number of parameters’ to a function in JavaScript. If those parameters are optional, your code will be executed without any problems. It might result in problems when those parameters are used in the function and not using them can alter the functionality. It is recommended to have uniform naming conventions so that identifying such problems becomes easy.
  • Equality operators are basic in JavaScript, but they have to be used with precision. There is a difference between ‘assignment/equals operator’ (==) and ‘strict equals operator’ (===). These are mainly used in conditional statements and accidentally using '==' instead of '===' can cause functionality issues. A thorough code walkthrough needs to be carried out in order to look into such silly, yet costly mistakes.
  • Variables are used as per their scope (local and global). Ensure that you use consistent naming conventions for different types of variables so that it is easier to maintain the code. Ensure that your source code does not have any syntax issues.
  • Adding DOM elements in JavaScript is considered to be a costly operation. In some cases, you would need to add DOM elements consecutively, but doing so is not a good practice. In such a scenario, you could instead use document fragments as it has superior efficiency and performance.
  • The starting index in JavaScript arrays is 0 and not 1. If you intend to create an array of 10 elements, you should declare an array with an index of 9 and not 10. Referencing out of bound array elements will result in errors.
  • Implementing a time-consuming task in a synchronous operation could slow down the performance of your web page/web application. Ensure that you move that logic to an asynchronous operation so it does not hog the CPU. Since the operation is asynchronous in nature, you have to be careful while using variables used in that operation since they might not reflect the latest value (as the async operation execution might still be in progress). Developers are advised to use the promise object that returns the status (success/failure) of the completion of an async operation. Sample code with promise is shown below:
  • Promise

  • Incorrect use of ‘functions inside loops’ results in the breaking of functionality.

Common Cross-Browser JavaScript Problems

So far we have looked into some of the basic JavaScript problems; let’s have a look at some of the mechanisms to solve those problems:

Library Use

There are many libraries (native and third-party) that might not be supported by all browsers. Before using a library, it is recommended that you do a thorough analysis in terms of browser support, feature support, etc. You should also check the ‘development history’ of the library as it shouldn’t happen that there are very few updates to the library and once you use the library, there are no updates to it!

Using User Agents and Browser Sniffing

Every browser has a user-agent string which identifies the browser that the user has used in order to access your web site/web application. Developers use browser sniffing code in order to tweak the UI/UX functionalities based on the browser in used. Some of the common user-agent strings are mentioned below.

Table

Source: https://mzl.la/2TBPY9a

Developers can use navigator.userAgent.indexOf(‘user-agent’) where the user-agent is the user-agent string (mentioned in the table above). Below is a snapshot of code where developers can come up with functionalities based on the type of browser.

code

Feature Detection for Modern JavaScript Features

JavaScript is not considered as permissive as HTML and CSS when it comes to handling errors and unrecognized features. JavaScript will definitely signal an error when it comes across improperly used syntax, a missing brace or semicolon, etc.

There are many new features that are implemented under ECMAScript 6 (ES6)/ECMAScript Next standards and many old browsers do not support these features. For example, the promise object that we discussed earlier will not work on the old version of the browsers. ‘Typed Arrays’ is another example. ‘Arrow Functions’ are a very useful feature that was introduced in ES6 and provide a concise manner for writing functions in JavaScript. The context inside the Arrow Function is statically defined. Modern JavaScript developers use this feature heavily, but it is also not supported on old browsers/old versions of browsers like IE, Firefox, Chrome, etc. The Safari browser does not support ‘Arrow Functions.’

So, how do you ensure JavaScript functionality is seamless on older browsers as well? The solution is to verify whether the feature being used is supported on old browsers. You can verify the same using an online resource like caniuse; simply key in the feature name and it indicates the version of browsers where the feature is supported. For example, below is the case for ‘Arrow Functions.’ Entries in red implies that the feature is not supported.

can i use

Based on the target audience, you should provide support for all the latest browsers and some older versions of browsers (depending on your initial market study). You can check out these web analytics tool that can help you understand your customers in a better way. You can also opt for conditional execution so that there is always a fallback mechanism in place in case the user is using an old browser. There are many old versions of browsers that do not support WebRTC (video conferencing), Maps API, etc. In the below example, we are using the Geolocation API; the geolocation property of the navigator object is used for that purpose. If the browser does not support the Maps API, the user is given an option to use Static Maps (as a fallback option).

geolocation

There are many JavaScript libraries that a developer has to simply import in order to use its functionalities. The good part about the usage is that the developer no longer has to code everything from scratch since the library already supports those functionalities.

JavaScript Transpiling

In case you want to provide support for old browsers, but do not want to use browser sniffing, feature detection, etc., a handy option that is available is called transpiling. In simple terms, transpiling helps in converting JavaScript code that might be using the latest ES6/ECMAScript features to code that can work on older browsers.

You can use a popular JavaScript transpiling tools like Babel where you simply enter the latest JavaScript code on the left and it outputs the transpiled code on the right.

Polyfills

Similar to third-party libraries that enhance the functionalities and reduce development time, polyfills also consist of third-party JavaScript files that you can use in your project. However, what makes polyfills different from libraries is that polyfills are capable of providing the functionalities that does not exist at all. For example, you can use a polyfill to support WebRTC/promise/other ES6-based features by simply using the equivalent polyfill for that feature.

You can have a look at this list which has details about the polyfill equivalent for JavaScript features. Let’s have a look at an example. Shown below is a code snippet where we have used a polyfill to support the startsWith feature that was introduced in ES6.

pollyfills

Solving Common JavaScript Problems

JavaScript Debugger

Breakpoints are commonly used for debugging purposes and when a breakpoint is hit, the execution is halted and the developer can have a look at various details like call stack, watch variables, memory information, etc. JavaScript has a keyword called debugger and when the keyword is encountered, the execution of JavaScript code is halted. This is similar to inserting a breakpoint in the code.

var x = 6 * 5; 
debugger;
/* Logic here */

Alternately, you could also use the traditional debugging method of using the JavaScript Console in Chrome to debug the code.

Browser Developer Tools

Browser developer tools can be used for removal warnings and errors in JavaScript code. It is also useful for debugging the code since developers can insert breakpoints at specific locations in the code.

In case you are using Chrome or Firefox, just right-click in the window after loading the code and click on ‘Inspect Element.’ Browser Developer Tools also have the ‘Debugger tab’ where the developer can insert breakpoints, check the callstack, add variables to watch a window, etc.

Below is the snapshot of the Developer Tools built into the Firefox browser.

firefox

Developers can also use the Console API for printing logs during the development phase. It is recommended that different kinds of console logs are used for different purposes. For example, console.log() can be used for debugging, console.assert() can be used if you want to issue an assert, and console.error() can be used in error scenarios.

Code Editor Plugins

There are many editors that have built-in as well as downloadable linter plugins that can be used to correct the warnings and errors in your JavaScript code. Atom is a popular open source IDE that has plugins for linting code. Developers can install lint, jslint, and linter-jshint plugins to lint their source code. These plugins issue warnings and errors that are present in the code in a separate panel at the bottom of the development window. Below is the snapshot of Atom IDE where it is displaying the warnings in the source code. Atom IDE can be downloaded from here.

Code Editor Plugins

Linters

Linters are used to ensure that code is of better quality, properly aligned, and there are no errors in the code. Llinters for JavaScript are also instrumental in maintaining code quality, irrespective of the size of your JavaScript file. Linters can be customized to different levels of error/warning reporting. Some of the widely-used linters for JavaScript are JSHint and ESLint.

linters

Solving General JavaScript Issues

Apart from the JavaScript issues that we have discussed so far, there are many general issues that developers need to address. Some of the common problems are:

  • Incorrect casing/spelling being used for variables, function names, etc. Many experienced developers accidentally make use of built-in functions with the wrong casing/spelling. For example, you might use getElementByClassName() instead of getElementsByClassName().
  • While performing a code review, the reviewer should make sure that there is no code after the return statement since that code is redundant (or unreachable).
  • Objectnotation is different from normal assignments and you need to check whether the member names of the object are separated by a comma (,) and member names are separated from their values by colon (:).
  • Though this is a very basic practice, check whether the semicolon (;) is being used at the right place.

Best Practices for JavaScript Development

Some best practices for JavaScript development are given below:

  • Always have declarations at the top.
  • Follow proper naming conventions for variables, functions, etc.
  • Use comments consistently throughout the code.
  • Declare the local variables using the var keyword.
  • Always initialize variables.
  • Do not declare String, Number, or Boolean objects.
  • Always have a default case in switch... case statements.
  • Have a close look at usage of '==' and '===' operators. Ensure that they are being used in the right place.
  • Place scripts at the bottom of the page.

JavaScript Frameworks for Overcoming Cross-Browser Compatibility Issues

It is a known fact that there will be cross-browser compatibility issues for your web app or website, irrespective of its size or complexity. As we have seen from the points mentioned above, cross-browser compatibility issue can be magnified when JavaScript is improperly used. But that doesn’t mean that you should avoid using JavaScript!

There exist multiple JS frameworks that facilitate development of cross-browser compatible websites. Some of the most renowned ones are:

  1. React.js
  2. Angular
  3. Vue.js
  4. Ionic
  5. Ember.js

These frameworks help to solve the problem of cross-browser compatibility for JavaScript. They also help developers to create a single-page application that is compatible across different browsers (Google Chrome, Mozilla Firefox, Safari, etc.).

Topics:
browser compatibility ,memory leaks ,web dev ,web application development

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}