Handling common accessibility problems - Learn web development

Handling common accessibility problems
======================================

* Previous
* Overview: Cross browser testing
* Next

Next we turn our attention to accessibility, providing information on common problems, how to do simple testing, and how to make use of auditing/automation tools for finding accessibility issues.

|  |  |
| --- | --- |
| Prerequisites: |
 Familiarity with the core HTML,
 CSS, and
 JavaScript languages; an idea
 of the high level
 principles of cross browser testing.
  |
| Objective: |
 To be able to diagnose common Accessibility problems, and use
 appropriate tools and techniques to fix them.
  |

What is accessibility?
----------------------

When we say accessibility in the context of web technology, most people immediately think of making sure websites/apps are usable by people with disabilities, for example:

* Visually impaired people using screen readers or magnification/zoom to access text
* People with motor function impairments using the keyboard (or other non-mouse features) to activate website functionality.
* People with hearing impairments relying on captions/subtitles or other text alternatives for audio/video content.

However, it is wrong to say that accessibility is just about disabilities. Really, the aim of accessibility is to make your websites/apps usable by as many people in as many contexts as possible, not just those users using high-powered desktop computers. Some examples might include:

* Users on mobile devices.
* Users on alternative browsing devices such as TVs, watches, etc.
* Users of older devices that might not have the latest browsers.
* Users of lower spec devices that might have slow processors.

In a way, this whole module is about accessibility — cross browser testing makes sure that your sites can be used by as many people as possible. What is accessibility? defines accessibility more completely and thoroughly than this article does.

That said, this article will cover cross browser and testing issues surrounding people with disabilities, and how they use the Web. We've already talked about other spheres such as responsive design and performance in other places in the module.

**Note:** Like many things in web development, accessibility isn't about 100% success or not; 100% accessibility is pretty much impossible to achieve for all content, especially as sites get more complex. Instead, it is more about making a reasonable effort to make as much of your content accessible to as many people as possible via defensive coding and sticking to best practices.

Common accessibility issues
---------------------------

In this section we'll detail some of the main issues that arise around web accessibility, connected with specific technologies, along with best practices to follow, and some quick tests you can do to see if your sites are going in the right direction.

**Note:** Accessibility is morally the right thing to do, and good for business (numbers of disabled users, users on mobile devices, etc. present significant market segments), but it is also a legal requirement in many parts of the world to make web content accessible to people with disabilities. Read Accessibility guidelines and the law for more information.

### HTML

Semantic HTML (where the elements are used for their correct purpose) is accessible right out of the box — such content is readable by sighted viewers (provided you don't do anything silly like make the text way too small or hide it using CSS), but will also be usable by assistive technologies like screen readers (apps that literally read out a web page to their user), and confer other advantages too.

#### Semantic structure

The most important quick win in semantic HTML is to use a structure of headings and paragraphs for your content; this is because screen reader users tend to use the headings of a document as signposts to find the content they need more quickly. If your content has no headings, all they will get is a huge wall of text with no signposts to find anything. Examples of bad and good HTML:

```html
<font size="7">My heading</font>
<br /><br />
This is the first section of my document.
<br /><br />
I'll add another paragraph here too.
<br /><br />
<font size="5">My subheading</font>
<br /><br />
This is the first subsection of my document. I'd love people to be able to find
this content!
<br /><br />
<font size="5">My 2nd subheading</font>
<br /><br />
This is the second subsection of my content. I think it is more interesting than
the last one.
```

```html
<h1>My heading</h1>

<p>This is the first section of my document.</p>

<p>I'll add another paragraph here too.</p>

<h2>My subheading</h2>

<p>
  This is the first subsection of my document. I'd love people to be able to
  find this content!
</p>

<h2>My 2nd subheading</h2>

<p>
  This is the second subsection of my content. I think it is more interesting
  than the last one.
</p>
```

In addition, your content should make logical sense in its source order — you can always place it where you want using CSS later on, but you should get the source order right to start with.

As a test, you can turn off a site's CSS and see how understandable it is without it. You could do this manually by just removing the CSS from your code, but the easiest way is to use browser features, for example:

* Firefox: Select *View > Page Style > No Style* from the main menu.
* Safari: Select *Develop > Disable Styles* from the main menu (to enable the *Develop* menu, choose *Safari > Preferences > Advanced > Show Develop menu in menu bar*).
* Chrome: Install the Web Developer Toolbar extension, then restart the browser. Click the gear icon that will appear, then select *CSS > Disable All Styles*.
* Edge: Select *View > Style > No Style* from the main menu.

#### Using native keyboard accessibility

Certain HTML features can be selected using only the keyboard — this is default behavior, available since the early days of the web. The elements that have this capability are the common ones that allow user to interact with web pages, namely links, `<button>`s, and form elements like `<input>`.

You can try this out using our native-keyboard-accessibility.html example (see the source code) — open this in a new tab, and try pressing the tab key; after a few presses, you should see the tab focus start to move through the different focusable elements; the focused elements are given a highlighted default style in every browser (it differs slightly between different browsers) so that you can tell what element is focused.

![A screenshot of three buttons demonstrating sample of the default behavior of interactive native elements. The third button is highlighted by a blue border to indicate its focus state.](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/button-focused-unfocused.png)

**Note:** In Firefox, you can also enable an overlay that shows the page tabbing order. For more information see: Accessibility Inspector > Show web page tabbing order.

You can then press Enter/Return to follow a focused link or press a button (we've included some JavaScript to make the buttons alert a message), or start typing to enter text in a text input (other form elements have different controls, for example the `<select>` element can have its options displayed and cycled between using the up and down arrow keys).

Note that different browsers may have different keyboard control options available. Most modern browsers follow the tab pattern described above (you can also do Shift + Tab to move backwards through the focusable elements), but some browsers have their own idiosyncrasies:

* Firefox for the Mac doesn't do tabbing by default. To turn it on, you have to go to *Preferences > Advanced > General*, then uncheck "Always use the cursor keys to navigate within pages". Next, you have to open your Mac's System Preferences app, then go to *Keyboard > Shortcuts*, then select the *All Controls* radio button.
* Safari doesn't allow you to tab through links by default; to enable this, you need to open Safari's *Preferences*, go to Advanced, and check the *Press Tab to highlight each item on a webpage* checkbox.

**Warning:** You should perform this kind of test/review on any new page you write — make sure that functionality can be accessed by the keyboard, and that the tab order provides a sensible navigation path through the document.

This example highlights the importance of using the correct semantic element for the correct job. It is possible to style *any* element to look like a link or button with CSS, and to behave like a link or button with JavaScript, but they won't actually be links or buttons, and you'll lose a lot of the accessibility these elements give you for free. So don't do it if you can avoid it.

Another tip — as shown in our example, you can control how your focusable elements look when focused, using the :focus pseudo-class. It is a good idea to double up focus and hover styles, so your users get that visual clue that a control will do something when activated, whether they are using mouse or keyboard:

```css
a:hover,
input:hover,
button:hover,
select:hover,
a:focus,
input:focus,
button:focus,
select:focus {
  font-weight: bold;
}
```

**Note:** If you do decide to remove the default focus styling using CSS, make sure you replace it with something else that fits in with your design better — it is a very valuable accessibility tool, and should not be removed.

#### Building in keyboard accessibility

Sometimes it is not possible to avoid losing keyboard accessibility. You might have inherited a site where the semantics are not very good (perhaps you've ended up with a horrible CMS that generates buttons made with `<div>`s), or you are using a complex control that does not have keyboard accessibility built in, like the HTML `<video>` element (amazingly, Opera is the only browser that allows you to tab through the `<video>` element's default browser controls). You have a few options here:

1. Create custom controls using `<button>` elements (which we can tab to by default!) and JavaScript to wire up their functionality. See Creating a cross-browser video player for some good examples of this.
2. Create keyboard shortcuts via JavaScript, so functionality is activated when you press certain keys on the keyboard. See Desktop mouse and keyboard controls for some game-related examples that can be adapted for any purpose.
3. Use some interesting tactics to fake button behavior. Take for example our fake-div-buttons.html example (see source code). Here we've given our fake `<div>` buttons the ability to be focused (including via tab) by giving each one the attribute `tabindex="0"` (see WebAIM's tabindex article for more really useful details). This allows us to tab to the buttons, but not to activate them via the Enter/Return key. To do that, we had to add the following bit of JavaScript trickery:

```js
document.onkeydown = (e) => {
  if (e.keyCode === 13) {
    // The Enter/Return key
    document.activeElement.onclick(e);
  }
};
```

Here we add a listener to the `document` object to detect when a button has been pressed on the keyboard. We check what button was pressed via the event object's keyCode property; if it is the keycode that matches Return/Enter, we run the function stored in the button's `onclick` handler using `document.activeElement.onclick()`. `activeElement` gives us the element that is currently focused on the page.

**Note:** This technique will only work if you set your original event handlers via event handler properties (e.g. `onclick`). `addEventListener` won't work. This is a lot of extra hassle to build the functionality back in. And there's bound to be other problems with it. Better to just use the right element for the right job in the first place.

#### Text alternatives

Text alternatives are very important for accessibility — if a person has a visual or hearing impairment that stops them being able to see or hear some content, then this is a problem. The simplest text alternative available is the humble `alt` attribute, which we should include on all images that contain relevant content. This should contain a description of the image that successfully conveys its meaning and content on the page, to be picked up by a screen reader and read out to the user.

**Note:** For more information, read Text alternatives.

Missing alt text can be tested for in a number of ways, for example using accessibility Auditing tools.

Alt text is slightly more complex for video and audio content. There is a way to define text tracks (e.g. subtitles) and display them when video is being played, in the form of the `<track>` element, and the WebVTT format (see Adding captions and subtitles to HTML video for a detailed tutorial). Browser compatibility for these features is fairly good, but if you want to provide text alternatives for audio or support older browsers, a simple text transcript presented somewhere on the page or on a separate page might be a good idea.

#### Element relationships and context

There are certain features and best practices in HTML designed to provide context and relationships between elements where none otherwise exists. The three most common examples are links, form labels, and data tables.

The key to accessible link text is that people using screen readers will often use a common feature whereby they pull up a list of all the links on the page. In this case, the link text needs to make sense out of context. For example, a list of links labeled "click here", "click me", etc. is really bad for accessibility. It is better for link text to make sense in context and out of context.

Next on our list, the form `<label>` element is one of the central features that allows us to make forms accessible. The trouble with forms is that you need labels to say what data should be entered into each form input. Each label needs to be included inside a `<label>` to link it unambiguously to its partner form input (the `for` attribute value of each `<label>` needs to match the form element `id` value), and it will make sense even if the source order is not completely logical (which to be fair it should be).

**Note:** For more information about link text and form labels, read Meaningful text labels.

Finally, a quick word about data tables. A basic data table can be written with very simple markup (see `bad-table.html` live, and source), but this has problems — there is no way for a screen reader user to associate rows or columns together as groupings of data — to do this you need to know what the header rows are, and if they are heading up rows, columns, etc. This can only be done visually for such a table.

If you instead look at our `punk-bands-complete.html` example (live, source), you can see a few accessibility aids at work here, such as table headers (`<th>` and `scope` attributes), `<caption>` element, etc.

**Note:** For more information about accessible tables, read Accessible data tables.

### CSS

CSS tends to provide a lot fewer fundamental accessibility features than HTML, but it can still do just as much damage to accessibility if used incorrectly. We have already mentioned a couple of accessibility tips involving CSS:

* Use the correct semantic elements to mark up different content in HTML; if you want to create a different visual effect, use CSS — don't abuse an HTML element to get the look you want. For example, if you want bigger text, use `font-size`, not an h1 element.
* Make sure your source order makes sense without CSS; you can always use CSS to style the page any way you want afterward.
* You should make sure interactive elements like buttons and links have appropriate focus/hover/active states set, to give the user visual clues as to their function. If you remove the defaults for stylistic reasons, make sure you include some replacement styles.

There are a few other considerations you should take into account.

#### Color and color contrast

When choosing a color scheme for your website, you should make sure that the text (foreground) color contrasts well with the background color. Your design might look cool, but it is no good if people with visual impairments like color blindness can't read your content. Use a tool like WebAIM's Color Contrast Checker to check whether your scheme is contrasting enough.

Another tip is to not rely on color alone for signposts/information, as this will be no good for those who can't see the color. Instead of marking required form fields in red, for example, mark them with an asterisk and in red.

**Note:** A high contrast ratio will also allow anyone using a smartphone or tablet with a glossy screen to better read pages when in a bright environment, such as sunlight.

#### Hiding content

There are many instances where a visual design will require that not all content is shown at once. For example, in our Tabbed info box example (see source code) we have three panels of information, but we are positioning them on top of one another and providing tabs that can be clicked to show each one (it is also keyboard accessible — you can alternatively use Tab and Enter/Return to select them).

![A screenshot demonstrating an example of accessible hiding and showing content in tabs. The example has three tabs namely Tab 1, Tab 2 and Tab 3. Tab 1 is currently focused and activated to display content.](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/20191022144107.png)

Screen reader users don't care about any of this — they are happy with the content as long as the source order makes sense, and they can get to it all. Absolute positioning (as used in this example) is generally seen as one of the best mechanisms of hiding content for visual effect, because it doesn't stop screen readers from getting to it.

On the other hand, you shouldn't use `visibility``:hidden` or `display``:none`, because they do hide content from screen readers. Unless of course, there is a good reason why you want this content to be hidden from screen readers.

**Note:** Invisible Content Just for Screen Reader Users has a lot more useful detail surrounding this topic.

### JavaScript

JavaScript has the same kind of problems as CSS with respect to accessibility — it can be disastrous for accessibility if used badly, or overused. We've already hinted at some accessibility problems related to JavaScript, mainly in the area of semantic HTML — you should always use appropriate semantic HTML to implement functionality wherever it is available, for example use links and buttons as appropriate. Don't use `<div>` elements with JavaScript code to fake functionality if at all possible — it is error-prone, and more work than using the free functionality HTML gives you.

#### Simple functionality

Generally simple functionality should work with just the HTML in place — JavaScript should only be used to enhance functionality, not build it in entirely. Good uses of JavaScript include:

* Providing client-side form validation, which alerts users to problems with their form entries quickly, without having to wait for the server to check the data. If it isn't available, the form will still work, but validation might be slower.
* Providing custom controls for HTML `<video>`s that are accessible to keyboard-only users (as we said earlier, the default browser controls aren't keyboard-accessible in most browsers).

**Note:** WebAIM's Accessible JavaScript provides some useful further details about considerations for accessible JavaScript.

More complex JavaScript implementations can create issues with accessibility — you need to do what you can. For example, it would be unreasonable to expect you to make a complex 3D game written using WebGL 100% accessible to a blind person, but you could implement keyboard controls so it is usable by non-mouse users, and make the color scheme contrasting enough to be usable by those with color deficiencies.

#### Complex functionality

One of the main areas problematic for accessibility is complex apps that involve complicated form controls (such as date pickers) and dynamic content that is updated often and incrementally.

Non-native complicated form controls are problematic because they tend to involve a lot of nested `<div>`s, and the browser does not know what to do with them by default. If you are inventing them yourself, you need to make sure that they are keyboard accessible; if you are using some kind of third-party framework, carefully review the options available to see how accessible they are before diving in. Bootstrap looks to be fairly good for accessibility, for example, although Making Bootstrap a Little More Accessible by Rhiana Heath explores some of its issues (mainly related to color contrast), and looks at some solutions.

Regularly updated dynamic content can be a problem because screen reader users might miss it, especially if it updates unexpectedly. If you have a single-page app with a main content panel that is regularly updated using XMLHttpRequest or Fetch, a screen reader user might miss those updates.

#### WAI-ARIA

Do you need to use such complex functionality, or will plain old semantic HTML do instead? If you do need complexity, you should consider using WAI-ARIA (Accessible Rich Internet Applications), a specification that provides semantics (in the form of new HTML attributes) for items such as complex form controls and updating panels that can be understood by most browsers and screen readers.

To deal with complex form widgets, you need to use ARIA attributes like `roles` to state what role different elements have in a widget (for example, are they a tab, or a tab panel?), `aria-disabled` to say whether a control is disabled or not, etc.

To deal with regularly updating regions of content, you can use the `aria-live` attribute, which identifies an updating region. Its value indicates how urgently the screen reader should read it out:

* `off:` The default. Updates should not be announced.
* `polite`: Updates should be announced only if the user is idle.
* `assertive`: Updates should be announced to the user as soon as possible.
* `rude`: Updates should be announced straight away, even if this interrupts the user.

Here's an example:

```html
<p><span id="LiveRegion1" aria-live="polite" aria-atomic="false"></span></p>
```

You can see an example in action at Freedom Scientific's ARIA (Accessible Rich Internet Applications) Live Regions example — the highlighted paragraph should update its content every 10 seconds, and a screen reader should read this out to the user. ARIA Live Regions - Atomic provides another useful example.

We don't have space to cover WAI-ARIA in detail here, you can learn a lot more about it at WAI-ARIA basics.

Accessibility tools
-------------------

Now we've covered accessibility considerations for different web technologies, including a few testing techniques (like keyboard navigation and color contrast checkers), let's have a look at other tools you can make use of when doing accessibility testing.

### Auditing tools

There are a number of auditing tools available that you can feed your web pages into. They will look over them and return a list of accessibility issues present on the page. Examples include:

* Wave: A rather nice online accessibility testing tool that accepts a web address and returns a useful annotated view of that page with accessibility problems highlighted.
* Tenon: Another nice online tool that goes through the code at a provided URL and returns results on accessibility errors including metrics, specific errors along with the WCAG criteria they affect, and suggested fixes. It requires a free trial signup to view the results.

Let's look at an example, using Wave.

1. Go to the Wave homepage.
2. Enter the URL of our bad-semantics.html example into the text input box near the top of the page. Then press enter or click/tap the arrow at the far right edge of the input box.
3. The site should respond with a description of the accessibility problems. Click the icons displayed to see more information about each of the issues identified by Wave's evaluation.

**Note:** Such tools aren't good enough to solve all your accessibility problems on their own. You'll need a combination of these, knowledge and experience, user testing, etc. to get a full picture.

### Automation tools

Deque's aXe tool goes a bit further than the auditing tools we mentioned above. Like the others, it checks pages and returns accessibility errors. Its most immediately useful form is probably the browser extensions:

* aXe for Chrome
* aXe for Firefox

These add an accessibility tab to the browser developer tools. For example, we installed the Firefox version, then used it to audit our bad-table.html example. We got the following results:

![A screenshot of accessibility issues identified by the Axe tool.](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/axe-screenshot.png)

aXe is also installable using `npm`, and can be integrated with task runners like Grunt and Gulp, automation frameworks like Selenium and Cucumber, unit testing frameworks like Jasmine, and more besides (again, see the main aXe page for details).

### Screen readers

It is definitely worth testing with a screen reader to get used to how severely visually impaired people use the Web. There are a number of screen readers available:

* Some are paid-for commercial products, like JAWS (Windows) and Window Eyes (Windows).
* Some are free products, like NVDA (Windows), ChromeVox (Chrome, Windows, and macOS), and Orca (Linux).
* Some are built into the operating system, like VoiceOver (macOS and iOS), ChromeVox (on Chromebooks), and TalkBack (Android).

Generally, screen readers are separate apps that run on the host operating system and can read not only web pages, but text in other apps as well. This is not always the case (ChromeVox is a browser extension), but usually, screen readers tend to act in slightly different ways and have different controls, so you'll have to consult the documentation for your chosen screen reader to get all the details — saying that, they all work in basically the same sort of way.

Let's go through some tests with a couple of different screen readers to give you a general idea of how they work and how to test with them.

**Note:** WebAIM's Designing for Screen Reader Compatibility provides some useful information about screen reader usage and what works best for screen readers. Also see Screen Reader User Survey #9 Results for some interesting screen reader usage statistics.

#### VoiceOver

VoiceOver (VO) comes free with your Mac/iPhone/iPad, so it's useful for testing on desktop/mobile if you use Apple products. We'll be testing it on Mac OS X on a MacBook Pro.

To turn it on, press Cmd + F5. If you've not used VO before, you will be given a welcome screen where you can choose to start VO or not, and run through a rather useful tutorial to learn how to use it. To turn it off again, press Cmd + F5 again.

**Note:** You should go through the tutorial at least once — it is a really useful way to learn VO.

When VO is on, the display will look mostly the same, but you'll see a black box at the bottom left of the screen that contains information on what VO currently has selected. The current selection will also be highlighted, with a black border — this highlight is known as the **VO cursor**.

![A sample screenshot demonstrating accessibility testing using VoiceOver on the MDN homepage. The bottom left of the image is a highlight of the information selected on the webpage.](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/voiceover.png)

To use VO, you will make a lot of use of the "VO modifier" — this is a key or key combination that you need to press in addition to the actual VO keyboard shortcuts to get them to work. Using a modifier like this is common with screen readers, to enable them to keep their commands from clashing with other commands. In the case of VO, the modifier can either be CapsLock, or Ctrl + Option.

VO has many keyboard commands, and we won't list them all here. The basic ones you'll need for web page testing are in the following table. In the keyboard shortcuts, "VO" means "the VoiceOver modifier".

Most common VoiceOver keyboard shortcuts| Keyboard shortcut | Description |
| --- | --- |
| VO + Cursor keys | Move the VO cursor up, right, down, left. |
| VO + Spacebar |
 Select/activate items highlighted by the VO cursor. This includes items
 selected in the Rotor (see below).
  |
| VO + Shift + down cursor |
 Move into a group of items (such as an HTML table, or a form, etc.) Once
 inside a group you can move around and select items inside that group
 using the above commands as normal.
  |
| VO + Shift + up cursor | Move out of a group. |
| VO + C | (when inside a table) Read the header of the current column. |
| VO + R | (when inside a table) Read the header of the current row. |
| VO + C + C (two Cs in succession) | (when inside a table) Read the entire current column, including header. |
| VO + R + R (two Rs in succession) |
 (when inside a table) Read the entire current row, including the headers
 that correspond to each cell.
  |
| VO + left cursor, VO + right cursor |
 (when inside some horizontal options, such as a date or time picker)
 Move between options.
  |
| VO + up cursor, VO + down cursor |
 (when inside some horizontal options, such as a date or time picker)
 Change the current option.
  |
| VO + U |
 Use the Rotor, which displays lists of headings, links, form controls,
 etc. for easy navigation.
  |
| VO + left cursor, VO + right cursor | (when inside Rotor) Move between different lists available in the Rotor. |
| VO + up cursor, VO + down cursor |
 (when inside Rotor) Move between different items in the current Rotor
 list.
  |
| Esc | (when inside Rotor) Exit Rotor. |
| Ctrl | (when VO is speaking) Pause/Resume speech. |
| VO + Z | Restart the last bit of speech. |
| VO + D | Go into the Mac's Dock, so you can select apps to run inside it. |

This seems like a lot of commands, but it isn't so bad when you get used to it, and VO regularly gives you reminders of what commands to use in certain places. Have a play with VO now; you can then go on to play with some of our examples in the Screen reader testing section.

#### NVDA

NVDA is Windows-only, and you'll need to install it.

1. Download it from nvaccess.org. You can choose whether to make a donation or download it for free; you'll also need to give them your email address before you can download it.
2. Once downloaded, install it — you double-click the installer, accept the license and follow the prompts.
3. To start NVDA, double-click on the program file/shortcut, or use the keyboard shortcut Ctrl + Alt + N. You'll see the NVDA welcome dialog when you start it. Here you can choose from a couple of options, then press the *OK* button to get going.

NVDA will now be active on your computer.

To use NVDA, you will make a lot of use of the "NVDA modifier" — this is a key that you need to press in addition to the actual NVDA keyboard shortcuts to get them to work. Using a modifier like this is common with screen readers, to enable them to keep their commands from clashing with other commands. In the case of NVDA, the modifier can either be Insert (the default), or CapsLock (can be chosen by checking the first checkbox in the NVDA welcome dialog before pressing *OK*).

**Note:** NVDA is more subtle than VoiceOver in terms of how it highlights where it is and what it is doing. When you are scrolling through headings, lists, etc., items you are selected on will generally be highlighted with a subtle outline, but this is not always the case for all things. If you get completely lost, you can press Ctrl + F5 to refresh the current page and begin from the top again.

NVDA has many keyboard commands, and we won't list them all here. The basic ones you'll need for web page testing are in the following table. In the keyboard shortcuts, "NVDA" means "the NVDA modifier".

Most common NVDA keyboard shortcuts| Keyboard shortcut | Description |
| --- | --- |
| NVDA + Q | Turn NVDA off again after you've started it. |
| NVDA + up cursor | Read the current line. |
| NVDA + down cursor | Start reading at the current position. |
| Up cursor and down cursor, or Shift + Tab and Tab | Move to previous/next item on page and read it. |
| Left cursor and right cursor | Move to previous/next character in current item and read it. |
| Shift + H and H | Move to previous/next heading and read it. |
| Shift + K and K | Move to previous/next link and read it. |
| Shift + D and D |
 Move to previous/next document landmark (e.g. `<nav>`)
 and read it.
  |
| Shift + 1–6 and 1–6 | Move to previous/next heading (level 1–6) and read it. |
| Shift + F and F | Move to previous/next form input and focus on it. |
| Shift + T and T | Move to previous/next data table and focus on it. |
| Shift + B and B | Move to previous/next button and read its label. |
| Shift + L and L | Move to previous/next list and read its first list item. |
| Shift + I and I | Move to previous/next list item and read it. |
| Enter/Return | (when link/button or other activatable item is selected) Activate item. |
| NVDA + Space |
 (when form is selected) Enter form so individual items can be selected,
 or leave form if you are already in it.
  |
| Shift Tab and Tab | (when inside form) Move between form inputs. |
| Up cursor and down cursor |
 (when inside form) Change form input values (in the case of things like
 select boxes).
  |
| Spacebar | (when inside form) Select chosen value. |
| Ctrl + Alt + cursor keys | (when a table is selected) Move between table cells. |

#### Screen reader testing

Now you've gotten used to using a screen reader, we'd like you to use it to do some quick accessibility tests, to get an idea of how screen readers deal with good and bad webpage features:

* Look at good-semantics.html, and note how the headers are found by the screen reader and available to use for navigation. Now look at bad-semantics.html, and note how the screen reader gets none of this information. Imagine how annoying this would be when trying to navigate a really long page of text.
* Look at good-links.html, and note how they make sense when viewed out of context. This is not the case with bad-links.html — they are all just "click here".
* Look at good-form.html, and note how the form inputs are described using their labels because we've used `<label>` elements properly. In bad-form.html, they get an unhelpful label along the lines of "blank".
* Look at our punk-bands-complete.html example, and see how the screen reader is able to associate columns and rows of content and read them out all together because we've defined headers properly. In bad-table.html, none of the cells can be associated at all. Note that NVDA seems to behave slightly strangely when you've only got a single table on a page; you could try WebAIM's table test page instead.
* Have a look at the WAI-ARIA live regions example we saw earlier, and note how the screen reader will keep reading out the constantly updating section as it updates.
### User testing

As mentioned above, you can't rely on automated tools alone for determining accessibility problems on your site. It is recommended that when you draw up your testing plan, you should include some accessibility user groups if at all possible (see our User Testing section earlier on in the course for some more context). Try to get some screen reader users involved, some keyboard-only users, some non-hearing users, and perhaps other groups too, as suits your requirements.

Accessibility testing checklist
-------------------------------

The following list provides a checklist for you to follow to make sure you've carried out the recommended accessibility testing for your project:

1. Make sure your HTML is as semantically correct as possible. Validating it is a good start, as is using an Auditing tool.
2. Check that your content makes sense when the CSS is turned off.
3. Make sure your functionality is keyboard accessible. Test using Tab, Return/Enter, etc.
4. Make sure your non-text content has text alternatives. An Auditing tool is good for catching such problems.
5. Make sure your site's color contrast is acceptable, using a suitable checking tool.
6. Make sure hidden content is visible by screen readers.
7. Make sure that functionality is usable without JavaScript wherever possible.
8. Use ARIA to improve accessibility where appropriate.
9. Run your site through an Auditing tool.
10. Test it with a screen reader.
11. Include an accessibility policy/statement somewhere findable on your site to say what you did.
Finding help
------------

There are many other issues you'll encounter with accessibility; the most important thing to know really is how to find answers online. Consult the HTML and CSS article's Finding help section for some good pointers.

Summary
-------

Hopefully this article has given you a good grounding in the main accessibility problems you might encounter, and how to test and overcome them.

In the next article we'll look at feature detection in more detail.

* Previous
* Overview: Cross browser testing
* Next


Implementing feature detection - Learn web development

Implementing feature detection
==============================

* Previous
* Overview: Cross browser testing
* Next

Feature detection involves working out whether a browser supports a certain block of code, and running different code depending on whether it does (or doesn't), so that the browser can always provide a working experience rather than crashing/erroring in some browsers. This article details how to write your own simple feature detection, how to use a library to speed up implementation, and native features for feature detection such as `@supports`.

|  |  |
| --- | --- |
| Prerequisites: |
 Familiarity with the core HTML,
 CSS, and
 JavaScript languages; an idea
 of the high-level
 principles of cross-browser testing.
  |
| Objective: |
 To understand what the concept of feature detection is, and be able to
 implement suitable solutions in CSS and JavaScript.
  |

The concept of feature detection
--------------------------------

The idea behind feature detection is that you can run a test to determine whether a feature is supported in the current browser, and then conditionally run code to provide an acceptable experience both in browsers that *do* support the feature, and browsers that *don't*. If you don't do this, browsers that don't support the features you are using in your code may not display your sites properly or might fail altogether, creating a bad user experience.

Let's recap and look at the example we touched on in our Handling common JavaScript problems — the Geolocation API (which exposes available location data for the device the web browser is running on) has the main entry point for its use, a `geolocation` property available on the global Navigator object. Therefore, you can detect whether the browser supports geolocation or not by using something like the following:

```js
if ("geolocation" in navigator) {
  navigator.geolocation.getCurrentPosition(function (position) {
    // show the location on a map, such as the Google Maps API
  });
} else {
  // Give the user a choice of static maps
}
```

Before we move on, we'd like to say one thing upfront — don't confuse feature detection with **browser sniffing** (detecting what specific browser is accessing the site) — this is a terrible practice that should be discouraged at all costs. See Using bad browser sniffing code for more details.

Writing your own feature detection tests
----------------------------------------

In this section, we'll look at implementing your own feature detection tests, in both CSS and JavaScript.

### CSS

You can write tests for CSS features by testing for the existence of *element.style.property* (e.g. `paragraph.style.rotate`) in JavaScript.

A classic example might be to test for Subgrid support in a browser; for browsers that support the `subgrid` value for a subgrid value for `grid-template-columns` and `grid-template-rows`, we can use subgrid in our layout. For browsers that don't, we could use regular grid that works fine but is not as cool-looking.

Using this as an example, we could include a subgrid stylesheet if the value is supported and a regular grid stylesheet if not. To do so, we could include two stylesheets in the head of our HTML file: one for all the styling, and one that implements the default layout if subgrid is not supported:

```html
<link href="basic-styling.css" rel="stylesheet" />
<link class="conditional" href="grid-layout.css" rel="stylesheet" />
```

Here, `basic-styling.css` handles all the styling that we want to give to every browser. We have two additional CSS files, `grid-layout.css` and `subgrid-layout.css`, which contain the CSS we want to selectively apply to browsers depending on their support levels.

We use JavaScript to test the support for the subgrid value, then update the `href` of our conditional stylesheet based on browser support.

We can add a `<script></script>` to our document, filled with the following JavaScript

```js
const conditional = document.querySelector(".conditional");
if (CSS.supports("grid-template-columns", "subgrid")) {
  conditional.setAttribute("href", "subgrid-layout.css");
}
```

In our conditional statement, we test to see if the`grid-template-columns` property supports the `subgrid` value using `CSS.supports()`.

#### @supports

CSS has a native feature detection mechanism: the `@supports` at-rule. This works in a similar manner to media queries except that instead of selectively applying CSS depending on a media feature like a resolution, screen width or aspect ratio, it selectively applies CSS depending on whether a CSS feature is supported, similar to `CSS.supports()`.

For example, we could rewrite our previous example to use `@supports`:

```css
@supports (grid-template-columns: subgrid) {
  main {
    display: grid;
    grid-template-columns: repeat(9, 1fr);
    grid-template-rows: repeat(4, minmax(100px, auto));
  }

  .item {
    display: grid;
    grid-column: 2 / 7;
    grid-row: 2 / 4;
    grid-template-columns: subgrid;
    grid-template-rows: repeat(3, 80px);
  }

  .subitem {
    grid-column: 3 / 6;
    grid-row: 1 / 3;
  }
}
```

This at-rule block applies the CSS rule within only if the current browser supports the `grid-template-columns: subgrid;` declaration. For a condition with a value to work, you need to include a complete declaration (not just a property name) and NOT include the semicolon on the end.

`@supports` also has `AND`, `OR`, and `NOT` logic available — the other block applies the regular grid layout if the subgrid option is not available:

```css
@supports not (grid-template-columns: subgrid) {
  /\* rules in here \*/
}
```

This is more convenient than the previous example — we can do all of our feature detection in CSS, no JavaScript required, and we can handle all the logic in a single CSS file, cutting down on HTTP requests. For this reason it is the preferred method of determining browser support for CSS features.

### JavaScript

We already saw an example of a JavaScript feature detection test earlier on. Generally, such tests are done via one of a few common patterns.

Common patterns for detectable features include:

Members of an object

Check whether a particular method or property (typically an entry point into using the API or other feature you are detecting) exists in its parent `Object`.

Our earlier example used this pattern to detect Geolocation support by testing the `navigator` object for a `geolocation` member:

```js
if ("geolocation" in navigator) {
  // Access navigator.geolocation APIs
}
```

Properties of an element

Create an element in memory using `Document.createElement()` and then check if a property exists on it.

This example shows a way of detecting Canvas API support:

```js
function supports\_canvas() {
  return !!document.createElement("canvas").getContext;
}

if (supports\_canvas()) {
  // Create and draw on canvas elements
}
```

**Note:** The double `NOT` in the above example (`!!`) is a way to force a return value to become a "proper" boolean value, rather than a Truthy/Falsy value that may skew the results.

Specific return values of a method on an element

Create an element in memory using `Document.createElement()` and then check if a method exists on it. If it does, check what value it returns. See the feature test in Dive into HTML Video Format detection for an example of this pattern.

Retention of assigned property value by an element

Create an element in memory using `Document.createElement()`, set a property to a specific value, then check to see if the value is retained. See the feature test in Dive into HTML <input> type detection for an example of this pattern.

Bear in mind that some features are, however, known to be undetectable. In these cases, you'll need to use a different approach, such as using a polyfill.

#### matchMedia

We also wanted to mention the `Window.matchMedia` JavaScript feature at this point too. This is a property that allows you to run media query tests inside JavaScript. It looks like this:

```js
if (window.matchMedia("(max-width: 480px)").matches) {
  // run JavaScript in here.
}
```

As an example, our Snapshot demo makes use of it to selectively apply the Brick JavaScript library and use it to handle the UI layout, but only for the small screen layout (480px wide or less). We first use the `media` attribute to only apply the Brick CSS to the page if the page width is 480px or less:

```html
<link
 href="dist/brick.css"
 rel="stylesheet"
 media="all and (max-width: 480px)" />
```

We then use `matchMedia()` in the JavaScript several times, to only run Brick navigation functions if we are on the small screen layout (in wider screen layouts, everything can be seen at once, so we don't need to navigate between different views).

```js
if (window.matchMedia("(max-width: 480px)").matches) {
  deck.shuffleTo(1);
}
```

Summary
-------

This article covered feature detection in a reasonable amount of detail, going through the main concepts and showing you how to implement your own feature detection tests.

Next up, we'll start looking at automated testing.

* Previous
* Overview: Cross browser testing
* Next


Handling common JavaScript problems - Learn web development

Handling common JavaScript problems
===================================

* Previous
* Overview: Cross browser testing
* Next

 Now we'll look at common cross-browser JavaScript problems and how to fix them.
 This includes information on using browser dev tools to track down and fix problems, using Polyfills and libraries to work around problems, getting modern JavaScript features working in older browsers, and more.

|  |  |
| --- | --- |
| Prerequisites: |
 Familiarity with the core HTML,
 CSS, and
 JavaScript languages; an idea of the high-level principles of cross browser testing.
  |
| Objective: | To be able to diagnose common JavaScript cross-browser problems, and use appropriate tools and techniques to fix them. |

The trouble with JavaScript
---------------------------

Historically, JavaScript was plagued with cross-browser compatibility problems — back in the 1990s, the main browser choices back then (Internet Explorer and Netscape) had scripting implemented in different language flavors (Netscape had JavaScript, IE had JScript and also offered VBScript as an option), and while at least JavaScript and JScript were compatible to some degree (both based on the ECMAScript specification), things were often implemented in conflicting, incompatible ways, causing developers many nightmares.

Such incompatibility problems persisted well into the early 2000s, as old browsers were still being used and still needed supporting. For example, code to create `XMLHttpRequest` objects had to have special handling for Internet Explorer 6:

```js
if (window.XMLHttpRequest) {
  // Mozilla, Safari, IE7+ ...
  httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) {
  // IE 6 and older
  httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
```

This is one of the main reasons why libraries like jQuery came into existence — to abstract away differences in browser implementations, so a developer could just use, for example, `jQuery.ajax()`, which would then handle the differences in the background.

Things have improved significantly since then; modern browsers do a good job of supporting "classic JavaScript features", and the requirement to use such code has diminished as the requirement to support older browsers has lessened (although bear in mind that they have not gone away altogether).

These days, most cross-browser JavaScript problems are seen:

* When poor-quality browser-sniffing code, feature-detection code, and vendor prefix usage block browsers from running code they could otherwise use just fine.
* When developers make use of new/nascent JavaScript features, modern Web APIs, etc.) in their code, and find that such features don't work in older browsers.

We'll explore all these problems and more below.

Fixing general JavaScript problems
----------------------------------

As we said in the previous article on HTML/CSS, you should make sure your code is working generally, before going on to concentrate on the cross-browser issues. If you are not already familiar with the basics of Troubleshooting JavaScript, you should study that article before moving on. There are a number of common JavaScript problems that you will want to be mindful of, such as:

* Basic syntax and logic problems (again, check out Troubleshooting JavaScript).
* Making sure variables, etc. are defined in the correct scope, and you are not running into conflicts between items declared in different places (see Function scope and conflicts).
* Confusion about this, in terms of what scope it applies to, and therefore if its value is what you intended. You can read What is "this"? for a light introduction; you should also study examples like this one, which shows a typical pattern of saving a `this` scope to a separate variable, then using that variable in nested functions so you can be sure you are applying functionality to the correct `this` scope.
* Incorrectly using functions inside loops that iterate with a global variable (more generally "getting the scope wrong").

For example, in bad-for-loop.html (see source code), we loop through 10 iterations using a variable defined with `var`, each time creating a paragraph and adding an onclick event handler to it. When clicked, we want each one to display an alert message containing its number (the value of `i` at the time it was created). Instead they all report `i` as 11 — because the `for` loop does all its iterating before nested functions are invoked.

The easiest solution is to declare the iteration variable with `let` instead of `var`—the value of `i` associated with the function is then unique to each iteration. See good-for-loop.html (see the source code also) for a version that works.

* Making sure asynchronous operations have completed before trying to use the values they return. This usually means understanding how to use *promises*: using `await` appropriately or running the code to handle the result of an asynchronous call in the promise's `then()` handler. See How to use promises for an introduction to this topic.

**Note:** Buggy JavaScript Code: The 10 Most Common Mistakes JavaScript Developers Make has some nice discussions of these common mistakes and more.

### Linters

As with HTML and CSS, you can ensure better quality, less error-prone JavaScript code using a linter, which points out errors and can also flag up warnings about bad practices, etc., and be customized to be stricter or more relaxed in their error/warning reporting. The JavaScript/ECMAScript linters we'd recommend are JSHint and ESLint; these can be used in a variety of ways, some of which we'll detail below.

#### Online

The JSHint homepage provides an online linter, which allows you to enter your JavaScript code on the left and provides an output on the right, including metrics, warnings, and errors.

![JSHint screenshot. Left panel is a color-coded and line-numbered code editor. Right panel is divided into metrics on the number, size, and makeup of functions and warnings. The warnings include the issue and the line number.](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript/jshint-online.png)

#### Code editor plugins

It is not very convenient to have to copy and paste your code over to a web page to check its validity several times. What you really want is a linter that will fit into your standard workflow with the minimum of hassle. Many code editors have linter plugins. For example, see the "Plugins for text editors and IDEs" section of the JSHint install page.

#### Other uses

There are other ways to use such linters; you can read about them on the JSHint and ESLint install pages.

It is worth mentioning command line uses — you can install these tools as command line utilities (available via the CLI — command line interface) using npm (Node Package Manager — you'll have to install NodeJS first). For example, the following command installs JSHint:

```bash
npm install -g jshint
```

You can then point these tools at JavaScript files you want to lint, for example:

![jshint filename.js was entered at the command line. The response is a list of line numbers and a description of the error found.](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript/js-hint-commandline.png)

You can also use these tools with a task runner/build tool such as Gulp or Webpack to automatically lint your JavaScript during development. (see Using a task runner to automate testing tools in a later article.) See ESLint integrations for ESLint options; JSHint is supported out of the box by Grunt, and also has other integrations available, e.g. JSHint loader for Webpack.

**Note:** ESLint takes a bit more setup and configuration than JSHint, but it is more powerful too.

### Browser developer tools

Browser developer tools have many useful features for helping to debug JavaScript. For a start, the JavaScript console will report errors in your code.

Make a local copy of our fetch-broken example (see the source code also).

If you look at the console, you'll see an error message. The exact wording is browser-dependent, but it will be something like: "Uncaught TypeError: heroes is not iterable", and the referenced line number is 25. If we look at the source code, the relevant code section is this:

```js
function showHeroes(jsonObj) {
  const heroes = jsonObj["members"];

  for (const hero of heroes) {
    // ...
  }
}
```

So the code falls over as soon as we try to use `jsonObj` (which as you might expect, is supposed to be a JSON object). This is supposed to be fetched from an external `.json` file using the following `fetch()` call:

```js
const requestURL =
  "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";

const response = fetch(requestURL);
populateHeader(response);
showHeroes(response);
```

But this fails.

#### The Console API

You may already know what is wrong with this code, but let's explore it some more to show how you could investigate this. For a start, there is a Console API that allows JavaScript code to interact with the browser's JavaScript console. It has a number of features available, but the one you'll use most often is `console.log()`, which prints a custom message to the console.

Try adding a `console.log()` call to log the return value of `fetch()`, like this:

```js
const requestURL =
  "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";

const response = fetch(requestURL);
console.log(`Response value: ${response}`);
const superHeroes = response;
populateHeader(superHeroes);
showHeroes(superHeroes);
```

Refresh the page in the browser. This time, before the error message, you'll see a new message logged to the console:
```

Response value: [object Promise]
```

The `console.log()` output shows that the return value of `fetch()` is not the JSON data, it's a `Promise`. The `fetch()` function is asynchronous: it returns a `Promise` that is fulfilled only when the actual response has been received from the network. Before we can use the response, we have to wait for the `Promise` to be fulfilled.

We can do this by putting the code that uses the response inside the `then()` method of the returned `Promise`, like this:

```js
const response = fetch(requestURL);
fetch(requestURL).then((response) => {
  populateHeader(response);
  showHeroes(response);
});
```

To summarize, anytime something is not working and a value does not appear to be what it is meant to be at some point in your code, you can use `console.log()` to print it out and see what is happening.

#### Using the JavaScript debugger

Unfortunately, we still have the same error — the problem has not gone away. Let's investigate this now, using a more sophisticated feature of browser developer tools: the JavaScript debugger as it is called in Firefox.

**Note:** Similar tools are available in other browsers; the Sources tab in Chrome, Debugger in Safari (see Safari Web Development Tools), etc.

In Firefox, the Debugger tab looks like this:

![Firefox debugger](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript/debugger-tab.png)

* On the left, you can select the script you want to debug (in this case we have only one).
* The center panel shows the code in the selected script.
* The right-hand panel shows useful details pertaining to the current environment — *Breakpoints*, *Callstack* and currently active *Scopes*.

The main feature of such tools is the ability to add breakpoints to code — these are points where the execution of the code stops, and at that point you can examine the environment in its current state and see what is going on.

Let's get to work. The error is now being thrown at line 26. Click on line number 26 in the center panel to add a breakpoint to it (you'll see a blue arrow appear over the top of it). Now refresh the page (Cmd/Ctrl + R) — the browser will pause execution of the code at line 51. At this point, the right-hand side will update to show some very useful information.

![Firefox debugger with a breakpoint](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript/breakpoint.png)

* Under *Breakpoints*, you'll see the details of the break-point you have set.
* Under *Call Stack*, you'll see a few entries — this is basically a list of the series of functions that were invoked to cause the current function to be invoked. At the top, we have `showHeroes()` the function we are currently in, and second we have `onload`, which stores the event handler function containing the call to `showHeroes()`.
* Under *Scopes*, you'll see the currently active scope for the function we are looking at. We only have three — `showHeroes`, `block`, and `Window` (the global scope). Each scope can be expanded to show the values of variables inside the scope when execution of the code was stopped.

We can find out some very useful information in here.

1. Expand the `showHeroes` scope — you can see from this that the heroes variable is `undefined`, indicating that accessing the `members` property of `jsonObj` (first line of the function) didn't work.
2. You can also see that the `jsonObj` variable is storing a `Response` object, not a JSON object.

The argument to `showHeroes()` is the value the `fetch()` promise was fulfilled with. So this promise is not in the JSON format: it is a `Response` object. There's an extra step needed to retrieve the content of the response as a JSON object.

We'd like you to try fixing this problem yourself. To get you started, see the documentation for the `Response` object. If you get stuck, you can find the fixed source code at https://github.com/mdn/learning-area/blob/main/tools-testing/cross-browser-testing/javascript/fetch-fixed.

**Note:** The debugger tab has many other useful features that we've not discussed here, for example conditional breakpoints and watch expressions. For a lot more information, see the Debugger page.

### Performance issues

As your apps get more complex and you start to use more JavaScript, you may start to run into performance problems, especially when viewing apps on slower devices. Performance is a big topic, and we don't have time to cover it in detail here. Some quick tips are as follows:

* To avoid loading more JavaScript than you need, bundle your scripts into a single file using a solution like Browserify. In general, reducing the number of HTTP requests is very good for performance.
* Make your files even smaller by minifying them before you load them onto your production server. Minifying squashes all the code together onto a huge single line, making it take up far less file size. It is ugly, but you don't need to read it when it is finished! This is best done using a minification tool like Uglify (there's also an online version — see JSCompress.com)
* When using APIs, make sure you turn off the API features when they are not being used; some API calls can be really expensive on processing power. For example, when showing a video stream, make sure it is turned off when you can't see it. When tracking a device's location using repeated Geolocation calls, make sure you turn it off when the user stops using it.
* Animations can be really costly for performance. A lot of JavaScript libraries provide animation capabilities programmed by JavaScript, but it is much more cost effective to do the animations via native browser features like CSS Animations (or the nascent Web Animations API) than JavaScript. Read Brian Birtles' Animating like you just don't care with Element.animate for some really useful theory on why animation is expensive, tips on how to improve animation performance, and information on the Web Animations API.

**Note:** Addy Osmani's Writing Fast, Memory-Efficient JavaScript contains a lot of detail and some excellent tips for boosting JavaScript performance.

Cross-browser JavaScript problems
---------------------------------

In this section, we'll look at some of the more common cross-browser JavaScript problems. We'll break this down into:

* Using modern core JavaScript features
* Using modern Web API features
* Using bad browser sniffing code
* Performance problems
### Using modern JavaScript/API features

In the previous article we described some of the ways in which HTML and CSS errors and unrecognized features can be handled due to the nature of the languages. JavaScript is not as permissive as HTML and CSS however — if the JavaScript engine encounters mistakes or unrecognized syntax, such as when new, unsupported features are used, more often than not it will throw errors.

There are a few strategies for handling new feature support; let's explore the most common ones.

**Note:** These strategies do not exist in separate silos — you can, of course combine them as needed. For example, you could use feature detection to determine whether a feature is supported; if it isn't, you could then run code to load a polyfill or a library to handle the lack of support.

#### Feature detection

The idea behind feature detection is that you can run a test to determine whether a JavaScript feature is supported in the current browser, and then conditionally run code to provide an acceptable experience both in browsers that do and don't support the feature. As a quick example, the Geolocation API (which exposes available location data for the device the web browser is running on) has a main entry point for its use — a `geolocation` property available on the global Navigator object. Therefore, you can detect whether the browser supports geolocation or not by using something like the following:

```js
if ("geolocation" in navigator) {
  navigator.geolocation.getCurrentPosition((position) => {
    // show the location on a map, perhaps using the Google Maps API
  });
} else {
  // Give the user a choice of static maps instead perhaps
}
```

 You could also write such a test for a CSS feature, for example by testing for the existence of *element.style.property* (e.g. `paragraph.style.transform !== undefined`).
 If you're looking to apply styles if a CSS feature is supported, you can directly use the @supports at-rule (known as a feature query).
 For example, to check whether the browser supports CSS container queries, you could do something like this:

```css
@supports (container-type: inline-size) {
  /\* Use container queries if supported \*/
}
```

As a last point, don't confuse feature detection with **browser sniffing** (detecting what specific browser is accessing the site) — this is a terrible practice that should be discouraged at all costs. See Using bad browser sniffing code, later on, for more details.

**Note:** Feature detection will be covered in a lot more detail in its own dedicated article, later in the module.

#### Libraries

JavaScript libraries are essentially third party units of code that you can attach to your page, providing you with a wealth of ready-made functionality that can be used straight away, saving you a lot of time in the process. A lot of JavaScript libraries probably came into existence because their developer was writing a set of common utility functions to save them time when writing future projects, and decided to release them into the wild because other people might find them useful too.

JavaScript libraries tend to come in a few main varieties (some libraries will serve more than one of these purposes):

* Utility libraries: Provide a bunch of functions to make mundane tasks easier and less boring to manage. jQuery for example provides its own fully-featured selectors and DOM manipulation libraries, to allow CSS-selector type selecting of elements in JavaScript and easier DOM building. It is not so important now we have modern features like `Document.querySelector()`/`Document.querySelectorAll()`/`Node` methods available across browsers, but it can still be useful when older browsers need supporting.
* Convenience libraries: Make difficult things easier to do. For example, the WebGL API is really complex and challenging to use when you write it directly, so the Three.js library (and others) is built on top of WebGL and provides a much easier API for creating common 3D objects, lighting, textures, etc.
 The Service Worker API is also very complex to use, so code libraries have started appearing to make common Service Worker uses-cases much easier to implement (see the Service Worker Cookbook for several useful code samples).
* Effects libraries: These libraries are designed to allow you to easily add special effects to your websites. This was more useful back when "DHTML" was a popular buzzword, and implementing an effect involved a lot of complex JavaScript, but these days browsers have a lot of built in CSS features and APIs to implementing effects more easily.
* UI libraries: Provide methods for implementing complex UI features that would otherwise be challenging to implement and get working cross browser, for example Foundation, Bootstrap, and Material-UI (the latter is a set of components for use with the React framework). These tend to be used as the basis of an entire site layout; it is often difficult to drop them in just for one UI feature.
* Normalization libraries: Give you a simple syntax that allows you to easily complete a task without having to worry about cross browser differences. The library will manipulate appropriate APIs in the background so the functionality will work whatever the browser (in theory). For example, LocalForage is a library for client-side data storage, which provides a simple syntax for storing and retrieving data. In the background, it uses the best API the browser has available for storing the data, whether that is IndexedDB, Web Storage, or even Web SQL (which is now deprecated, but is still supported in Chromium-based browsers in secure contexts). As another example, jQuery

When choosing a library to use, make sure that it works across the set of browsers you want to support, and test your implementation thoroughly. Also make sure that the library is popular and well-supported, and isn't likely to just become obsolete next week. Talk to other developers to find out what they recommend, see how much activity and how many contributors the library has on GitHub (or wherever else it is stored), etc.

Library usage at a basic level tends to consist of downloading the library's files (JavaScript, possibly some CSS or other dependencies too) and attaching them to your page (e.g. via a `<script>` element), although there are normally many other usage options for such libraries, like installing them as Bower components, or including them as dependencies via the Webpack module bundler. You will have to read the libraries' individual install pages for more information.

**Note:** You will also come across JavaScript frameworks in your travels around the Web, like Ember and Angular. Whereas libraries are often usable for solving individual problems and dropping into existing sites, frameworks tend to be more along the lines of complete solutions for developing complex web applications.

#### Polyfills

Polyfills also consist of 3rd party JavaScript files that you can drop into your project, but they differ from libraries — whereas libraries tend to enhance existing functionality and make things easier, polyfills provide functionality that doesn't exist at all. Polyfills use JavaScript or other technologies entirely to build in support for a feature that a browser doesn't support natively. For example, you might use a polyfill like es6-promise to make promises work in browsers where they are not supported natively.

Let's work through an exercise — in this example used for demonstration purposes only, we use a Fetch polyfill and an es6-promise polyfill. While Fetch and promises are fully supported in modern browsers, if we were targeting a browser that did not support Fetch that browser would likely not support Fetch either, and Fetch makes heavy use of promises:

1. To get started, make a local copy of our fetch-polyfill.html example and our nice image of some flowers in a new directory. We are going to write code to fetch the flowers image and display it in the page.
2. Next, save a copy of the Fetch polyfill in the same directory as the HTML.
3. Apply the polyfill scripts to the page using the following code — place these above the existing `<script>` element so they will be available on the page already when we start trying to use Fetch (we are also loading a Promise polyfill from a CDN, as IE11 does support promises, which fetch requires):

```html
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>
<script src="fetch.js"></script>
```

4. Inside the original `<script>`, add the following code:

```js
const myImage = document.querySelector(".my-image");

fetch("flowers.jpg").then((response) => {
  response.blob().then((myBlob) => {
    const objectURL = URL.createObjectURL(myBlob);
    myImage.src = objectURL;
  });
});
```

5. If you load it in a browser that doesn't support Fetch, you should still see the flower image appear — cool!

 ![heading reading fetch basic example with a photo of purple flowers](/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript/fetch-image.jpg)

**Note:** You can find our finished version at fetch-polyfill-finished.html (see also the source code).

**Note:** Again, there are many different ways to make use of the different polyfills you will encounter — consult each polyfill's individual documentation.

One thing you might be thinking is "why should we always load the polyfill code, even if we don't need it?" This is a good point — as your sites get more complex and you start to use more libraries, polyfills, etc., you can start to load a lot of extra code, which can start to affect performance, especially on less-powerful devices. It makes sense to only load files as needed.

Doing this requires some extra setup in your JavaScript. You need some kind of a feature detection test that detects whether the browser supports the feature we are trying to use:

```js
if (browserSupportsAllFeatures()) {
  main();
} else {
  loadScript("polyfills.js", main);
}

function main(err) {
  // actual app code goes in here
}
```

So first we run a conditional that checks whether the function `browserSupportsAllFeatures()` returns true. If it does, we run the `main()` function, which will contain all our app's code. `browserSupportsAllFeatures()` looks like this:

```js
function browserSupportsAllFeatures() {
  return window.Promise && window.fetch;
}
```

Here we are testing whether the `Promise` object and `fetch()` function exist in the browser. If both do, the function returns true. If the function returns `false`, then we run the code inside the second part of the conditional — this runs a function called loadScript(), which loads the polyfills into the page, then runs `main()` after the loading has finished. `loadScript()` looks like this:

```js
function loadScript(src, done) {
  const js = document.createElement("script");
  js.src = src;
  js.onload = () => {
    done();
  };
  js.onerror = () => {
    done(new Error(`Failed to load script ${src}`));
  };
  document.head.appendChild(js);
}
```

This function creates a new `<script>` element, then sets its `src` attribute to the path we specified as the first argument (`'polyfills.js'` when we called it in the code above). When it has loaded, we run the function we specified as the second argument (`main()`). If an error occurs in the loading of the script, we still call the function, but with a custom error that we can retrieve to help debug a problem if it occurs.

Note that polyfills.js is basically the two polyfills we are using put together into one file. We did this manually, but there are cleverer solutions that will automatically generate bundles for you — see Browserify (see Getting started with Browserify for a basic tutorial). It is a good idea to bundle JS files into one like this — reducing the number of HTTP requests you need to make improves the performance of your site.

You can see this code in action in fetch-polyfill-only-when-needed.html (see the source code also). We'd like to make it clear that we can't take credit for this code — it was originally written by Philip Walton. Check out his article Loading Polyfills Only When Needed for the original code, plus a lot of useful explanation around the wider subject).

**Note:** There are some 3rd party options to consider, for example Polyfill.io — this is a meta-polyfill library that will look at each browser's capabilities and apply polyfills as needed, depending on what APIs and JS features you are using in your code.

#### JavaScript transpiling

Another option that is becoming popular for people who want to use modern JavaScript features now is converting code that uses recent ECMAScript features to a version that will work in older browsers.

**Note:** This is called "transpiling" — you are not compiling code into a lower level to be run on a computer (like you would say with C code); instead, you are changing it into a syntax that exists at a similar level of abstraction so it can be used in the same way, but in slightly different circumstances (in this case, transforming one flavor of JavaScript into another).

A common transpiler is Babel.js but there are others.

### Don't browser sniff

Historically developers used *browser sniffing code* to detect which browser the user was using, and give them appropriate code to work on that browser.

All browsers have a **user-agent** string, which identifies what the browser is (version, name, OS, etc.). Many developers implemented bad browser sniffing code and didn't maintain it. This lead to supporting browsers getting locked out of using websites they could easily render. This became so common that browsers started to lie about what browser they were in their user-agent strings (or claim they were all browsers), to get around sniffing code. Browsers also implemented facilities to allow users to change what user-agent string the browser reported when queried with JavaScript. This all made browser sniffing even more error prone, and ultimately pointless.

History of the browser user-agent string by Aaron Andersen provides a useful and amusing take on the history of browser sniffing.
 Use feature detection (and CSS @supports for CSS feature detection) to reliably detect whether a feature is supported. But doing so, you won't need to change your code when new browser versions come out.

### Handling JavaScript prefixes

In the previous article, we included quite a lot of discussion about handling CSS prefixes. Well, new JavaScript implementations used to use prefixes as well, with JavaScript using camel case rather than hyphenation like CSS. For example, if a prefix was being used on a new jshint API object called `Object`:

* Mozilla would use `mozObject`
* Chrome/Opera/Safari would use `webkitObject`
* Microsoft would use `msObject`

Here's an example, taken from our violent-theremin demo (see source code), which uses a combination of the Canvas API and the Web Audio API to create a fun (and noisy) drawing tool:

```js
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioCtx = new AudioContext();
```

In the case of the Web Audio API, the key entry points to using the API were supported in Chrome/Opera via `webkit` prefixed versions (they now support the unprefixed versions). The easy way to get around this situation is to create a new version of the objects that are prefixed in some browsers, and make it equal to the non-prefixed version, OR the prefixed version (OR any other prefixed versions that need consideration) — whichever one is supported by the browser currently viewing the site will be used.

Then we use that object to manipulate the API, rather than the original one. In this case we are creating a modified AudioContext constructor, then creating a new audio context instance to use for our Web Audio coding.

This pattern can be applied to just about any prefixed JavaScript feature. JavaScript libraries/polyfills also make use of this kind of code, to abstract browser differences away from the developer as much as possible.

Again, prefixed features were never supposed to be used in production websites — they are subject to change or removal without warning, and cause cross browser issues. If you insist on using prefixed features, make sure you use the right ones. You can look up what browsers require prefixes for different JavaScript/API features on MDN reference pages, and sites like caniuse.com. If you are unsure, you can also find out by doing some testing directly in browsers.

For example, try going into your browser's developer console and start typing

```js
window.AudioContext;
```

If this feature is supported in your browser, it will autocomplete.

Finding help
------------

There are many other issues you'll encounter with JavaScript; the most important thing to know really is how to find answers online. Consult the HTML and CSS article's Finding help section for our best advice.

Summary
-------

So that's JavaScript. Simple huh? Maybe not so simple, but this article should at least give you a start, and some ideas on how to tackle the JavaScript-related problems you will come across.

* Previous
* Overview: Cross browser testing
* Next


Introduction to cross-browser testing - Learn web development

Introduction to cross-browser testing
=====================================

* Overview: Cross browser testing
* Next

This article gives an overview of cross-browser testing: what cross-browser testing is, some common problems, and some approaches for debugging/troubleshooting.

|  |  |
| --- | --- |
| Prerequisites: |
 Familiarity with the core HTML,
 CSS, and
 JavaScript languages.
  |
| Objective: | To gain an understanding of the high-level concepts involved in cross-browser testing. |

What is cross-browser testing?
------------------------------

Cross-browser testing is the practice of ensuring that a website works across various browsers and devices. Web developers should consider:

* Different browsers, including slightly older ones that don't support all the latest JS/CSS features.
* Different devices, from desktops and laptops to tablets and smartphones, to smart TVs, with varying hardware capabilities.
* People with disabilities, who may rely on assistive technologies like screen readers, or use only a keyboard.

Remember that you are not your users — just because your site works on your MacBook Pro or high-end Galaxy Nexus, doesn't mean it will work for all your users!

**Note:** Make the web work for everyone discusses the different browsers, their market share, and related cross-browser compatibility issues.

Websites should be accessible across different browsers and devices, and to people with disabilities (e.g. screen-reader-friendly). A site doesn't need to deliver the exact same experience on all browsers and devices, as long as the core functionality is accessible in some way. For example, a modern browser might have something animated, 3D and shiny, while older browsers might just show a flat graphic with the same information.

Also, it's just about impossible for a website to work on ALL browsers and devices, so a web developer should come to an agreement with the site owner on the range of browsers and devices where the code will work.

Why do cross-browser issues occur?
----------------------------------

There are many different reasons why cross-browser issues occur, and note that here we are talking about issues where things behave differently across different browsers/devices/browsing preferences. Before you even get to cross-browser issues, you should have already fixed bugs in your code (see Debugging HTML, Debugging CSS, and What went wrong? Troubleshooting JavaScript from previous topics to refresh your memory if needed).

Cross-browser issues commonly occur because:

* sometimes browsers have bugs, or implement features differently. This situation is a lot less bad than it used to be; back when IE4 and Netscape 4 were competing to be the dominant browser in the 1990s, browser companies deliberately implemented things differently from each other to try to gain a competitive advantage, which made life hell for developers. Browsers are much better at following standards these days, but differences and bugs still creep through sometimes.
* some browsers may have different levels of support for technology features than others. This is inevitable when you are dealing with bleeding edge features that browsers are just getting around to implementing, or if you have to support very old browsers that are no longer being developed, which may have been frozen (i.e. no more new work done on them) a long time before a new feature was even invented. As an example, if you want to use cutting-edge JavaScript features in your site, they might not work in older browsers. If you need to support older browsers, you might have to not use those, or convert your code to old-fashioned syntax using some kind of cross-compiler where needed.
* some devices may have constraints that cause a website to run slowly, or display badly. For example, if a site has been designed to look nice on a desktop PC, it will probably look tiny and be hard to read on a mobile device. If your site includes a load of big animations, it might be OK on a high-spec tablet but might be sluggish or jerky on a low-end device.

…and more reasons besides.

In later articles, we'll explore common cross-browser problems, and look at solutions to those.

Workflows for cross-browser testing
-----------------------------------

All of this cross-browser testing business may sound time-consuming and scary, but it needn't be — you just need to plan carefully for it, and make sure you do enough testing in the right places to make sure you don't run into unexpected problems. If you are working on a large project, you should be testing it regularly, to make sure that new features work for your target audience, and that new additions to the code don't break old features that were previously working.

If you leave all the testing to the end of a project, any bugs you uncover will be a lot more expensive and time-consuming to fix than if you uncover them and fix them as you go along.

The workflow for testing and bug fixes on a project can be broken down into roughly the following four phases (this is only very rough — different people may do things quite differently to this):

**Initial planning** > **Development** > **Testing/discovery** > **Fixes/iteration**

Steps 2–4 will tend to be repeated as many times as necessary to get all of the implementation done. We will look at the different parts of the testing process in much greater detail in subsequent articles, but for now, let's just summarize what may occur in each step.

### Initial planning

In the initial planning phase, you will probably have several planning meetings with the site owner/client (this might be your boss, or someone from an external company you are building a website for), in which you determine exactly what the website should be — what content and functionality should it have, what should it look like, etc. At this point, you'll also want to know how much time you have to develop the site — what is their deadline, and how much are they going to pay you for your work? We won't go into much detail about this, but cross-browser issues can have a serious effect on such planning.

Once you've got an idea of the required feature set, and what technologies you will likely build these features with, you should start exploring the target audience — what browsers, devices, etc. will the target audience for this site be using? The client might already have data about this from previous research they've done, e.g. from other websites they own, or from previous versions of the website you are now working on. If not, you will be able to get a good idea by looking at other sources, such as usage stats for competitors, or countries the site will be serving. You can also use a bit of intuition.

 So for example, you might be building an e-commerce site that serves customers in North America. The site should work entirely in the last few versions of the most popular desktop and mobile (iOS, Android, Windows phone) browsers — this should include Chrome (and Opera as it is based on the same rendering engine as Chrome), Firefox, Edge, and Safari.
 It should also be accessible with WCAG AA compliance.

 Now you know your target testing platforms, you should go back and review the required feature set and what technologies you are going to use.
 For example, if the e-commerce site owner wants a WebGL-powered 3D tour of each product built into the product pages, they will need to accept that this just won't work all legacy browser version.

You should compile a list of the potential problem areas.

**Note:** You can find browser support information for technologies by looking up the different features on MDN — the site you're on! You should also consult caniuse.com, for some further useful details.

Once you've agreed on these details, you can go ahead and start developing the site.

### Development

Now on to the development of the site. You should split the different parts of the development into modules, for example you might split the different site areas up — home page, product page, shopping cart, payment workflow, etc. You might then further subdivide these — implement a common site header and footer, implement product page detail view, implement persistent shopping cart widget, etc.

There are multiple general strategies to cross-browser development, for example:

* Get all the functionality working as closely as possible in all target browsers. This may involve writing different code paths that reproduce functionality in different ways aimed at different browsers, or using a Polyfill to mimic any missing support using JavaScript or other technologies, or using a library that allows you to write a single bit of code and then does different things in the background depending on what the browser supports.
* Accept that some things aren't going to work the same on all browsers, and provide different (acceptable) solutions in browsers that don't support the full functionality. Sometimes this is inevitable due to device constraints — a cinema widescreen isn't going to give the same visual experience as a 4" mobile screen, regardless of how you program your site.
* Accept that your site just isn't going to work in some older browsers, and move on. This is OK, provided your client/userbase is OK with it.

Normally your development will involve a combination of the above three approaches. The most important thing is that you test each small part before committing it — don't leave all the testing till the end!

### Testing/discovery

After each implementation phase, you will need to test the new functionality. To start with, you should make sure there are no general issues with your code that are stopping your feature from working:

1. Test it in a couple of stable browsers on your system, like Firefox, Safari, Chrome, or Edge.
2. Do some lo-fi accessibility testing, such as trying to use your site with only the keyboard, or using your site via a screen reader to see if it is navigable.
3. Test on a mobile platform, such as Android or iOS.

At this point, fix any problems you find with your new code.

Next, you should try expanding your list of test browsers to a full list of target audience browsers and start concentrating on weeding out cross-browser issues (see the next article for more information on determining your target browsers). For example:

* Try to test the latest change on all the modern desktop browsers you can — including Firefox, Chrome, Opera, Edge, and Safari on desktop (Mac, Windows, and Linux, ideally).
* Test it in common phone and tablet browsers (e.g. iOS Safari on iPhone/iPad, Chrome and Firefox on iPhone/iPad/Android),
* Also do tests in any other browsers you have included inside your target list.

The most lo-fi option is to just do all the testing you can by yourself (pulling in teammates to help out if you are working in a team). You should try to test it on real physical devices where possible.

If you haven't got the means to test all those different browsers, operating systems, and device combinations on physical hardware, you can also make use of emulators (emulate a device using software on your desktop computer) and virtual machines (software that allows you to emulate multiple operating system/software combinations on your desktop computer). This is a very popular choice, especially in some circumstances — for example, Windows doesn't let you have multiple versions of Windows installed simultaneously on the same machine, so using multiple virtual machines is often the only option here.

Another option is user groups — using a group of people outside your development team to test your site. This could be a group of friends or family, a group of other employees, a class at a local university, or a professional user testing setup, where people are paid to test out your site and provide results.

Finally, you can get smarter with your testing using auditing or automation tools; this is a sensible choice as your projects get bigger, as doing all this testing by hand can start to take a really long time. You can set up your own testing automation system (Selenium being the popular app of choice) that could for example load your site in a number of different browsers, and:

* see if a button click causes something to happen successfully (like for example, a map displaying), displaying the results once the tests are completed
* take a screenshot of each, allowing you to see if a layout is consistent across the different browsers.

If you wish to invest money in testing, there are also commercial tools that can automate much of the setup and testing for you (such as Sauce Labs and Browser Stack). These kinds of tools usually enable a continuous integration workflow, where code changes are automatically tested before they are allowed to be submitted into your code repository.

#### Testing on prerelease browsers

It is often a good idea to test on prerelease versions of browsers; see the following links:

* Firefox Developer Edition
* Microsoft Edge Insider
* Safari Technology Preview
* Chrome Canary
* Opera Developer

This is especially prevalent if you are using very new technologies in your site, and you want to test against the latest implementations, or if you are coming across a bug in the latest release version of a browser, and you want to see if the browser's developers have fixed the bug in a newer version.

### Fixes/iteration

Once you've discovered a bug, you need to try to fix it.

The first thing to do is to narrow down where the bug occurs as much as possible. Get as much information as you can from the person reporting the bug — what platform(s), device(s), browser version(s), etc. Try it on similar configurations (e.g. the same browser version on different desktop platforms, or a few different versions of the same browser on the same platform) to see how widely the bug persists.

It might not be your fault — if a bug exists in a browser, then hopefully the vendor will rapidly fix it. It might have already been fixed — for example if a bug is present in Firefox release 49, but it is no longer there in Firefox Nightly (version 52), then they have fixed it. If it is not fixed, then you may want to file a bug (see Reporting bugs, below).

If it is your fault, you need to fix it! Finding out the cause of the bug involves the same strategy as any web development bug (again, see Debugging HTML, Debugging CSS, and What went wrong? Troubleshooting JavaScript). Once you've discovered what is causing your bug, you need to decide how to work around it in the particular browser it is causing problems in — you can't just change the problem code outright, as this may break the code in other browsers. The general approach is usually to fork the code in some way, for example use JavaScript feature detection code to detect situations in which a problem feature doesn't work, and run some different code in those cases that does work.

Once a fix has been made, you'll want to repeat your testing process to make sure your fix is working OK, and hasn't caused the site to break in other places or other browsers.

Reporting bugs
--------------

Just to reiterate on what was said above, if you discover bugs in browsers, you should report them:

* Firefox Bugzilla
* EdgeHTML issue tracker
* Safari
* Chrome
* Opera
Summary
-------

This article should have given you a high-level understanding of the most important concepts you need to know about cross browser testing. Armed with this knowledge, you are now ready to move on and start learning about Cross-browser testing strategies.

* Overview: Cross browser testing
* Next