Chapter 14: Chapter 14. Developing cross-browser strategies
The introduction frames cross-browser JavaScript development as a broad challenge that requires prioritizing limited project resources.
After this chapter you will be able to
- Cross-browser development priorities. Explain why cross-browser JavaScript work requires choices about where to spend effort, rather than trying to support every browser perfectly.
- Graded browser-support matrix. Describe how graded browser support uses a matrix of browsers and target platforms, with each combination assigned a project-specific grade indicating the importance of supporting it.
- Support costs and coverage. Explain how to weigh audience needs, browser market share, and support effort when selecting browsers, while keeping coverage feasible and not sacrificing quality.
- Cross-browser effort tradeoffs. Explain how to decide how much time to spend on cross-browser concerns for reusable JavaScript.
- Popular browser support priorities. Describe the recommended priority for browser support and what graceful degradation means in the section's example.
- Cross-browser test coverage. Explain why reusable JavaScript needs comprehensive tests across the browsers it supports, including both common and fringe use cases.
- Risks of Permanent Bug Assumptions. Explain why reusable JavaScript should not assume a browser bug will persist: a workaround may break when the bug is fixed, and widespread reliance on it may discourage vendors from fixing it.
- Bugs Versus Unspecified API Changes. Distinguish a browser bug from an inconsistency in an unspecified API by checking the specification; describe why expected output should be tested for unspecified APIs and why their changes are harder to foresee than bug fixes.
- Two-way code coexistence. Explain why reusable code must both avoid disrupting surrounding code and withstand interference from it, especially on sites the developer cannot control.
- Small global footprint. Describe how encapsulation limits interference by grouping functions and properties under a small number of global names, ideally one, rather than adding each name globally.
- Form property DOM clobbering. Trace how a form input’s id or name can replace access to a standard form property, and describe how to recognize and avoid this problem.
- Browser regression meaning. Explain that a browser regression is a feature that used to work but no longer behaves as expected, whether because of a bug or a breaking API change.
- Anticipated API changes. Describe how feature detection can accommodate a predictable API change, using the section’s event-handler example.
- Testing new browser releases. Justify maintaining tests and running them on new browser releases to find and address regressions promptly.
- Safe fix criteria. Explain why the safest cross-browser fixes avoid harming other browsers and require neither browser nor feature detection.
- Uniform attribute restriction tradeoff. Explain why jQuery forbids changing the type attribute of an input already in the DOM across all browsers, and identify the cost of that choice.
- Feature detection principle. Explain how feature detection checks for an object or property before relying on its implied functionality, and why it is a useful first line of defense in cross-browser code.
- Conditional polyfill for array find. Describe how the Array.prototype.find example supplies a polyfill only when the built-in method is missing, while leaving the standard implementation in place when it exists. Explain the basic behavior of the supplied method.
- Application-level fallback choices. Describe how checks for browser facilities can determine whether an application offers full functionality or a fallback, and give examples of fallbacks described here.
- Event firing uncertainty. Distinguish support for binding an event from assurance that it will fire, and describe the problems this creates for late-loaded scripts and browser-specific events.
- Crash-inducing feature tests. Explain why a browser-crashing bug cannot safely be detected by running a test for it, using the older Safari example.
- Consistent cross-browser APIs. Justify working around a severe browser bug rather than exposing an API that behaves differently by browser.
- Runtime performance test costs. Explain why choosing the fastest API for each browser usually cannot depend on a thorough performance test every time a page loads, and state the section’s general response to hard-to-test issues.
- Problem testing over browser detection. Explain why testing for a browser problem is safer than inferring behavior from navigator.userAgent or a browser name.
- Practical limits of assumption reduction. Justify why cross-browser code should reduce assumptions without trying to eliminate every one.
What it builds on
- Using DOM attributes and properties (chapter 12) · Assumes familiarity with accessing properties on DOM elements.
- Storing functions (chapter 3) · Functions can have other functions as properties.
- Registering event handlers (chapter 2) · The earlier discussion introduces registering event handlers.
- Common operations on arrays (chapter 9) · Earlier array operations provide context for a method that searches array items.
- Working with events (chapter 13) · Working with browser events and their handlers
- Styling attribute headaches (chapter 12) · CSS styling and style properties
- Matching Unicode (chapter 10) · Regular expressions involving Unicode
Section by section
Introduction
The introduction frames cross-browser JavaScript development as a broad challenge that requires prioritizing limited project resources.
- Cross-browser development priorities: Explain why cross-browser JavaScript work requires choices about where to spend effort, rather than trying to support every browser perfectly.
Why it matters: Prioritizing support helps a team make effective use of its development effort.
Key terms: cross-browser JavaScript code, supported browsers, finite development resources
14.1 Cross-browser considerations
This section explains why cross-browser support requires deliberate choices and how to choose browser–platform combinations without sacrificing quality.
- Need for cross-browser strategy: Explain why JavaScript proficiency alone is insufficient for browser-based applications: browsers can have bugs, missing APIs, and browser-specific quirks.
Why it matters: It explains why code that is sound JavaScript may still need cross-browser attention. - Graded browser-support matrix: Describe how graded browser support uses a matrix of browsers and target platforms, with each combination assigned a project-specific grade indicating the importance of supporting it.
Why it matters: The matrix makes support priorities explicit for each browser and platform pairing. - Support costs and coverage: Explain how to weigh audience needs, browser market share, and support effort when selecting browsers, while keeping coverage feasible and not sacrificing quality.
Why it matters: A realistic support target directs limited effort toward the users who need it most. - Supported browser commitments: State what choosing to support a browser typically commits a team to do regarding testing, bugs and regressions, and performance.
Why it matters: These commitments make clear why adding a browser to the support list has a continuing cost.
Key terms: browser-specific quirks, missing APIs, cross-browser support, graded browser support, browser-support matrix, grade, target audience, market share, support effort, coverage
14.1 Do you know?
This section poses self-check questions about browser inconsistencies, using code on other people’s pages, and polyfills without explaining the answers.
14.2 The five major development concerns
The section identifies the main concerns for reusable JavaScript and explains how to prioritize cross-browser work while allowing less popular browsers to degrade gracefully.
- Cross-browser effort tradeoffs: Explain how to decide how much time to spend on cross-browser concerns for reusable JavaScript.
Why it matters: Cross-browser work is a tradeoff, not a requirement to spend equal effort on every concern. - Popular browser support priorities: Describe the recommended priority for browser support and what graceful degradation means in the section's example.
Why it matters: Prioritizing likely users need not mean letting the application break for everyone else.
Key terms: intended audience, development resources, schedule, popular browsers, targeted audience, degrades gracefully
14.2.1 Browser bugs and differences
This section explains how to verify reusable JavaScript across supported browsers and why fixes for browser bugs must remain safe when browsers change.
- Cross-browser test coverage: Explain why reusable JavaScript needs comprehensive tests across the browsers it supports, including both common and fringe use cases.
Why it matters: Broad tests provide evidence that reusable code works across the browsers you intend to support. - Future-resistant browser fixes: State why a fix for a current browser bug should remain safe if a future browser version fixes that bug.
Why it matters: A workaround should not become a new source of problems after a browser update.
Key terms: browser bugs, API differences, supported set of browsers, comprehensive suite of tests, fixes, future versions of the browser
14.2.2 Browser bug fixes
This section explains why cross-browser workarounds must account for eventual bug fixes, using a scrolling bug to show the risks of browser-specific hacks and distinguishing bugs from changes to unspecified APIs.
- Risks of Permanent Bug Assumptions: Explain why reusable JavaScript should not assume a browser bug will persist: a workaround may break when the bug is fixed, and widespread reliance on it may discourage vendors from fixing it.
Why it matters: A workaround should remain safe when a browser improves, rather than depend on the browser staying broken. - Root Element Scrolling Workaround: Trace the scrollTop and scrollLeft example: the specification calls for these properties on the root html element to control viewport scrolling, but some browsers required using body; browser-name-based workarounds could fail if those browsers corrected their behavior.
Why it matters: The scrolling case makes the danger of a browser-name-based workaround concrete. - Bugs Versus Unspecified API Changes: Distinguish a browser bug from an inconsistency in an unspecified API by checking the specification; describe why expected output should be tested for unspecified APIs and why their changes are harder to foresee than bug fixes.
Why it matters: Checking what is specified helps you distinguish a correctable bug from behavior that may change without warning.
Key terms: browser bugs, bug work-arounds, bug fixes, scrollTop, scrollLeft, html element, body element, user agent string, specification, browser bug
14.2.3 External code and markup
This section explains how reusable JavaScript can coexist with unfamiliar page code and markup by limiting its own interference and anticipating integration hazards.
- Two-way code coexistence: Explain why reusable code must both avoid disrupting surrounding code and withstand interference from it, especially on sites the developer cannot control.
Why it matters: Thinking in both directions helps you anticipate integration problems that do not appear when code runs alone. - Small global footprint: Describe how encapsulation limits interference by grouping functions and properties under a small number of global names, ideally one, rather than adding each name globally.
Why it matters: A small, self-contained footprint makes reusable code less likely to collide with its host page. - Risks from external modifications: Explain how other on-page code can alter prototypes, properties, or DOM methods and thereby break otherwise ordinary assumptions in reusable code.
Why it matters: Failures may come from changes made elsewhere on the page, even when your own operation looks harmless. - Form property DOM clobbering: Trace how a form input’s id or name can replace access to a standard form property, and describe how to recognize and avoid this problem.
Why it matters: This explains why a familiar form property might unexpectedly refer to an element or fail when called. - Stylesheet and script ordering: Explain why external style sheets should be included before external scripts when JavaScript expects their CSS rules, and how the section recommends addressing this integration requirement.
Why it matters: Documenting the required loading order helps others integrate code that relies on CSS being available.
Key terms: reusable code, external code, robust, encapsulation, global footprint, namespace, global namespace, function prototypes, object properties, DOM element methods
14.2.4 Regressions
The section explains what browser regressions are, how some API changes can be anticipated, and why testing new browser releases remains essential.
- Browser regression meaning: Explain that a browser regression is a feature that used to work but no longer behaves as expected, whether because of a bug or a breaking API change.
Why it matters: Recognizing regressions helps you distinguish newly broken behavior from code that never worked. - Anticipated API changes: Describe how feature detection can accommodate a predictable API change, using the section’s event-handler example.
Why it matters: Checking for available APIs lets code handle some browser changes without assuming a particular browser version. - Testing new browser releases: Justify maintaining tests and running them on new browser releases to find and address regressions promptly.
Why it matters: Tests on new releases can reveal breakages that no amount of advance API planning could foresee. - Vendor regression safeguards: Describe how browser vendors use JavaScript library test suites to prevent some regressions, while recognizing the limits of that protection.
Why it matters: Vendor testing helps, but it does not replace testing your own code on new releases.
Key terms: regression, non-backward-compatible API changes, feature detection, addEventListener, attachEvent, suite of tests, browser releases, regressions, browser vendors, JavaScript libraries
14.3 Implementation strategies
Introduces the need for effective, situation-appropriate strategies to build robust cross-browser code, without explaining those strategies yet.
14.3.1 Safe cross-browser fixes
This section explains how browser-independent fixes can make an API behave consistently across browsers, using two jQuery workarounds to show both a painless fix and a deliberate tradeoff.
- Safe fix criteria: Explain why the safest cross-browser fixes avoid harming other browsers and require neither browser nor feature detection.
Why it matters: This gives you a first option to look for when fixing a browser difference. - Negative dimension workaround: Describe how ignoring negative height and width values in every browser avoids an Internet Explorer exception without changing how other browsers handle those values.
Why it matters: This is a concrete example of a fix that makes behavior uniform without a browser check. - Uniform attribute restriction tradeoff: Explain why jQuery forbids changing the type attribute of an input already in the DOM across all browsers, and identify the cost of that choice.
Why it matters: Consistency can be worth a restriction, but the section asks you to weigh that restriction for your own users.
Key terms: cross-browser fixes, browser or feature detection, height or width style properties, negative values, unified API, type attribute, injected input elements, uniform informational exception
14.3.2 Feature detection and polyfills
The section explains how to use feature detection to choose supported browser functionality, supply polyfills for missing APIs, and fall back when an application needs unavailable browser facilities.
- Feature detection principle: Explain how feature detection checks for an object or property before relying on its implied functionality, and why it is a useful first line of defense in cross-browser code.
Why it matters: It lets cross-browser code adapt to available features with a simple, inexpensive check. - Conditional polyfill for array find: Describe how the Array.prototype.find example supplies a polyfill only when the built-in method is missing, while leaving the standard implementation in place when it exists. Explain the basic behavior of the supplied method.
Why it matters: Using the built-in method where available preserves the standard behavior while supporting browsers that lack it. - Application-level fallback choices: Describe how checks for browser facilities can determine whether an application offers full functionality or a fallback, and give examples of fallbacks described here.
Why it matters: The same checks that guide API use can also help an application offer an appropriate experience when essential browser facilities are absent. - Unsigned array length conversion: Explain why the find polyfill example uses the zero-fill right-shift operator on the array length.
Why it matters: It explains a less obvious operation in the polyfill example.
Key terms: feature detection, object property, fallback, polyfill, Array.prototype.find, predicate function, full application functionality, reduced-experience fallback, >>>, zero-fill right-shift operator
14.3.3 Untestable browser issues
The section explains why some browser differences cannot be tested reliably at runtime and how those limits shape cross-browser code.
- Event handler binding visibility: Explain why code cannot discover all handlers bound to an element or remove them all unless it kept references when binding them.
Why it matters: Keeping handler references is necessary if your code will need to remove those handlers later. - Event firing uncertainty: Distinguish support for binding an event from assurance that it will fire, and describe the problems this creates for late-loaded scripts and browser-specific events.
Why it matters: Detecting an event-binding API alone cannot guarantee that code waiting on an event will run. - Visual CSS effect limits: Explain why changing certain CSS properties cannot be tested for its intended visual effect through ordinary JavaScript checks.
Why it matters: A script cannot use every visible result as an automatic cross-browser test. - Crash-inducing feature tests: Explain why a browser-crashing bug cannot safely be detected by running a test for it, using the older Safari example.
Why it matters: A detection test is not useful when running it can break the user’s browser. - Consistent cross-browser APIs: Justify working around a severe browser bug rather than exposing an API that behaves differently by browser.
Why it matters: A uniform API can be more useful than one whose capabilities change unexpectedly between browsers. - Runtime performance test costs: Explain why choosing the fastest API for each browser usually cannot depend on a thorough performance test every time a page loads, and state the section’s general response to hard-to-test issues.
Why it matters: Cross-browser code needs practical fallbacks when reliable runtime testing costs too much or is impossible.
Key terms: event handler, event listener, bound handlers, event firing, window load, mouseenter, mouseleave, CSS property effects, color, backgroundColor
14.4 Reducing assumptions
This section shows how to make cross-browser code more reliable by questioning its assumptions while accepting that some assumptions are unavoidable.
- Problem testing over browser detection: Explain why testing for a browser problem is safer than inferring behavior from navigator.userAgent or a browser name.
Why it matters: Testing the problem makes cross-browser code less vulnerable to changes in browsers. - Hidden event attachment assumptions: Describe the assumptions identified in the event-attaching example and distinguish the easy one to check from the harder ones.
Why it matters: Even code that checks for a property can rely on untested assumptions about what that property does. - Practical limits of assumption reduction: Justify why cross-browser code should reduce assumptions without trying to eliminate every one.
Why it matters: A practical balance avoids needless complexity without pretending cross-browser code can be risk-free.
Key terms: user-agent detection, navigator.userAgent, browser detection, event-attaching code, callable functions, binding an event, assumptions, regressions, code base
14.5 Summary
14.6 Exercises
Concepts explained in this chapter
- feature detectionChecking whether a runtime environment supports a capability by testing for the feature itself.
- DOM clobberingBrowser behavior in which form controls named by their id or name create or replace properties on their containing form element.
- cross-browser developmentCross-browser development is the practice of writing web code that works acceptably across multiple browsers despite differences in support and behavior.
- browser bugAn error in a browser implementation that causes behavior to differ from what is expected.
- polyfillA polyfill is code that implements a feature in environments that do not natively support it, emulating the missing behavior.
- regressionA failure of previously working software behavior following a change.
- safe cross-browser fixA cross-browser fix that resolves a browser-specific problem without adverse effects in other browsers or reliance on detection logic.
- cross-browser development concernAn issue that affects code across browsers, such as implementation bugs, regressions, interference, or absent features.
- external codeCode outside a component or library that coexists in the same page or environment and may affect or be affected by it.
- custom eventA programmer-defined event that represents an application-specific occurrence and can be triggered and handled like a built-in event.
- event-handler registrationThe act of associating a handler with an event target or event type so it runs when the event occurs.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
