diff --git a/front/src/css/rule.css b/front/src/css/rule.css index 26b009b..0903840 100644 --- a/front/src/css/rule.css +++ b/front/src/css/rule.css @@ -82,7 +82,11 @@ font-size: 3em; margin-bottom: 1em; } -.offenders .offenderButton { +.rule .startTime { + display: none; +} +.offendersTable .offenderButton, +.value .offenderButton { display: inline-block; position: relative; background: #efe; @@ -91,16 +95,19 @@ border-radius: 0.4em; z-index: 1; } -.offenders .offenderButton.opens { +.offendersTable .offenderButton.opens, +.value .offenderButton.opens { padding-right: 0.75em; } -.offenders .offenderButton.opens:after { +.offendersTable .offenderButton.opens:after, +.value .offenderButton.opens:after { position: relative; left: 0.5em; content: '\25BC'; font-size: 0.8em; } -.offenders .offenderButton > div { +.offendersTable .offenderButton > div, +.value .offenderButton > div { display: none; position: absolute; right: 0; @@ -111,28 +118,35 @@ border-top: 1px solid #999; z-index: 2; } -.offenders .offenderButton .domTree { +.offendersTable .offenderButton .domTree, +.value .offenderButton .domTree { text-align: left; white-space: nowrap; } -.offenders .offenderButton .domTree > div { +.offendersTable .offenderButton .domTree > div, +.value .offenderButton .domTree > div { margin: 0.5em; } -.offenders .offenderButton .domTree > div div { +.offendersTable .offenderButton .domTree > div div, +.value .offenderButton .domTree > div div { margin-left: 1em; } -.offenders .offenderButton .backtrace, -.offenders .offenderButton .cssFileAndLine { +.offendersTable .offenderButton .backtrace, +.value .offenderButton .backtrace, +.offendersTable .offenderButton .cssFileAndLine, +.value .offenderButton .cssFileAndLine { white-space: nowrap; padding: 0.5em; } -.offenders .offenderButton.opens:hover { +.offendersTable .offenderButton.opens:hover, +.value .offenderButton.opens:hover { border-bottom-left-radius: 0; border-bottom-right-radius: 0; background: #ffe0cc; z-index: 2; } -.offenders .offenderButton.opens:hover > div { +.offendersTable .offenderButton.opens:hover > div, +.value .offenderButton.opens:hover > div { display: block; background: #ffe0cc; } diff --git a/front/src/js/directives/offendersDirectives.js b/front/src/js/directives/offendersDirectives.js index f226bb7..c22eed8 100644 --- a/front/src/js/directives/offendersDirectives.js +++ b/front/src/js/directives/offendersDirectives.js @@ -119,6 +119,22 @@ function getNonJQueryHTML(node, onASingleLine) { var type = node.data.type; + if (node.windowPerformance) { + switch (type) { + case 'documentScroll': + return '(triggering the scroll event on document)'; + + case 'windowScroll': + return '(triggering the scroll event on window)'; + + case 'window.onscroll': + return '(calling the window.onscroll function)'; + + default: + return ''; + } + } + if (!node.data.callDetails) { return ''; } @@ -153,6 +169,18 @@ case 'error': return args[0]; + case 'jQuery - onDOMReady': + return '(function)'; + + case 'documentScroll': + return 'The scroll event just triggered on document'; + + case 'windowScroll': + return 'The scroll event just triggered on window'; + + case 'window.onscroll': + return 'The window.onscroll function just got called'; + default: return ''; } @@ -518,9 +546,6 @@ } break; - case 'jQuery - onDOMReady': - return '(function)'; - default: return ''; } @@ -674,7 +699,7 @@ function getProfilerLineHTML(index, node) { return '
The table below shows the interactions between the JavaScript and the DOM on a scroll event.
+This is the colors palette, sized by total occurrences:
This graph gives a quick view of when the Javascript interactions with the DOM occur during the loading of the page.
+This graph gives a quick view of when the JavaScript interactions with the DOM occur during the loading of the page.
- The table below shows the interactions between Javascript and the DOM. It is useful to understand what happens while the page loads. + The table below shows the interactions between the JavaScript and the DOM. It is useful to understand what happens while the page loads.
TODO
TODO
", + "isOkThreshold": 50, + "isBadThreshold": 2000, + "isAbnormalThreshold": 3000, + "hasOffenders": false + }, "DOMinserts": { "tool": "phantomas", "label": "DOM inserts", @@ -195,6 +204,49 @@ var policies = { }; } }, + "eventsScrollBound": { + "tool": "phantomas", + "label": "Scroll events bound", + "message": "Number of 'scroll' event listeners binded to 'window' or 'document'.
Asking too much work to the browser on scroll hurts the smoothness of the scroll. Merging all your event listeners into an unique listener can help you factorize their code and reduce their footprint on scroll.
", + "isOkThreshold": 1, + "isBadThreshold": 7, + "isAbnormalThreshold": 12, + "hasOffenders": true, + "offendersTransformFn": function(offenders) { + return { + count: offenders.length, + list: offenders.map(function(offender) { + var parts = /^bound by (.*) on ([^ ]+)$/.exec(offender); + + if (!parts) { + debug('eventsScrollBound offenders transform function error with "%s"', offender); + return { + parseError: offender + }; + } + + var backtraceArray = offendersHelpers.backtraceToArray(parts[1]); + + return { + backtrace: backtraceArray || [], + target: parts[2] + }; + }) + }; + } + }, + "DOMaccessesOnScroll": { + "tool": "jsExecutionTransformer", + "label": "DOM access on scroll", + "message": "This rule counts the number of DOM-accessing functions calls, such as queries, readings, writings, bindings and jQuery functions.
Two scroll events are triggered quickly, one after the other, and only the second one is analyzed so throttled functions are ignored.
One of the main reasons of a poor scrolling experience is when too much JS is executed on each scroll event. Note that some devices such as smartphones and MacBooks send more scroll events than others.
Reduce the number of DOM accesses inside scroll listeners. Put DOM queries outside them when possible. Use throttling or deboucing.
", + "isOkThreshold": 1, + "isBadThreshold": 12, + "isAbnormalThreshold": 25, + "hasOffenders": true, + "offendersTransformFn": function(offenders) { + return offenders; + } + }, "jsErrors": { "tool": "phantomas", "label": "JavaScript errors", diff --git a/lib/metadata/scoreProfileGeneric.json b/lib/metadata/scoreProfileGeneric.json index 319c83c..aeda814 100644 --- a/lib/metadata/scoreProfileGeneric.json +++ b/lib/metadata/scoreProfileGeneric.json @@ -12,6 +12,7 @@ "domManipulations": { "label": "DOM manipulations", "policies": { + "DOMaccesses": 3, "DOMinserts": 2, "DOMqueries": 1, "DOMqueriesWithoutResults": 2, @@ -19,6 +20,13 @@ "eventsBound": 1 } }, + "scroll": { + "label": "Scroll bottlenecks", + "policies": { + "eventsScrollBound": 1, + "DOMaccessesOnScroll": 4 + } + }, "badJavascript": { "label": "Bad JavaScript", "policies": { @@ -99,6 +107,7 @@ "globalScore": { "domComplexity": 1, "domManipulations": 2, + "scroll": 1, "badJavascript": 1, "jQueryVersion": 1, "cssSyntaxError": 1, diff --git a/lib/offendersHelpers.js b/lib/offendersHelpers.js index 92405d9..a3630e0 100644 --- a/lib/offendersHelpers.js +++ b/lib/offendersHelpers.js @@ -115,7 +115,7 @@ var OffendersHelpers = function() { var parts = null; for (var i=0 ; i