{"id":26221753,"url":"https://github.com/push-based/awesome-web-performance-snippets","last_synced_at":"2026-04-08T14:32:26.019Z","repository":{"id":40484476,"uuid":"351859894","full_name":"push-based/awesome-web-performance-snippets","owner":"push-based","description":"🤓 A comprehensive collection of web performance snippets","archived":false,"fork":false,"pushed_at":"2023-09-21T12:12:42.000Z","size":8233,"stargazers_count":75,"open_issues_count":2,"forks_count":17,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-10-30T19:03:32.117Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/push-based.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-03-26T17:23:32.000Z","updated_at":"2025-10-30T11:42:47.000Z","dependencies_parsed_at":"2024-01-16T00:33:27.759Z","dependency_job_id":"f6e564b5-aff2-4a02-b621-d09d7603f948","html_url":"https://github.com/push-based/awesome-web-performance-snippets","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/push-based/awesome-web-performance-snippets","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/push-based%2Fawesome-web-performance-snippets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/push-based%2Fawesome-web-performance-snippets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/push-based%2Fawesome-web-performance-snippets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/push-based%2Fawesome-web-performance-snippets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/push-based","download_url":"https://codeload.github.com/push-based/awesome-web-performance-snippets/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/push-based%2Fawesome-web-performance-snippets/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31559797,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-12T16:29:25.036Z","updated_at":"2026-04-08T14:32:25.993Z","avatar_url":"https://github.com/push-based.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Awesome Web Performance Snippets ![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)\r\n\r\n## Third Party Sources\r\n\r\nIncluded scripts from other open source authors and repositories:\r\n- directly copied or adopted from [webperf-snippets](https://github.com/nucliweb/webperf-snippets) by [Joan León](https://joanleon.dev/)\r\n- directly copied from [header-check](https://github.com/csswizardry/ct) by [Harry Roberts](https://csswizardry.com/)\r\n## How to use a snippet?\r\n\r\n- [how-to-use-it-with-console-tab](https://github.com/push-based/web-performance-tools/blob/master/docs/how-to-use-it-with-console-tab.md)\r\n- [how-to-use-it-with-source-tab](https://github.com/push-based/web-performance-tools/blob/master/docs/how-to-use-it-with-sources-tab.md)\r\n- [how-to-use-it-with-bookmarks](https://github.com/push-based/web-performance-tools/blob/master/docs/how-to-use-it-with-bookmarks.md)\r\n- [how-to-use-it-with-chromium](https://github.com/push-based/web-performance-tools/blob/master/docs/how-to-use-it-with-chromium.md)\r\n\r\n\u003c!-- START-SNIPPETS --\u003e\r\n## [Check first and third party script](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\n// ex: katespade.com - list firsty party subdomains in HOSTS array\nconst HOSTS = [\"assets.katespade.com\"];\nfunction getScriptInfo() {\n    const resourceListEntries = performance.getEntriesByType(\"resource\");\n    // set for first party scripts\n    const first = [];\n    // set for third party scripts\n    const third = [];\n    resourceListEntries.forEach((resource) =\u003e {\n        // check for initiator type\n        const value = \"initiatorType\" in resource;\n        if (value) {\n            if (resource.initiatorType === \"script\") {\n                const { host } = new URL(resource.name);\n                // check if resource url host matches location.host = first party script\n                if (host === location.host || HOSTS.includes(host)) {\n                    first.push({ ...resource.toJSON(), type: \"First Party\" });\n                }\n                else {\n                    // add to third party script\n                    third.push({ ...resource.toJSON(), type: \"Third Party\" });\n                }\n            }\n        }\n    });\n    const scripts = {\n        firstParty: [{ name: \"no data\" }],\n        thirdParty: [{ name: \"no data\" }],\n    };\n    if (first.length) {\n        scripts.firstParty = first;\n    }\n    if (third.length) {\n        scripts.thirdParty = third;\n    }\n    return scripts;\n}\nconst { firstParty, thirdParty } = getScriptInfo();\nconsole.groupCollapsed(\"FIRST PARTY SCRIPTS\");\nconsole.table(firstParty);\nconsole.groupEnd();\nconsole.groupCollapsed(\"THIRD PARTY SCRIPTS\");\nconsole.table(thirdParty);\nconsole.groupEnd();\n\n/*\nChoose which properties to display\nhttps://developer.mozilla.org/en-US/docs/Web/API/console/table\n\nconsole.groupCollapsed(\"FIRST PARTY SCRIPTS\");\nconsole.table(firstParty, [\"name\", \"nextHopProtocol\"]);\nconsole.groupEnd();\nconsole.groupCollapsed(\"THIRD PARTY SCRIPTS\", [\"name\", \"nextHopProtocol\"]);\nconsole.table(thirdParty);\nconsole.groupEnd();\n*/\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Check first and third party script timings](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction createUniqueLists(firstParty, thirdParty) {\n    function getUniqueListBy(arr, key) {\n        return [...new Map(arr.map((item) =\u003e [item[key], item])).values()];\n    }\n    const firstPartyList = getUniqueListBy(firstParty, [\"name\"]);\n    const thirdPartyList = getUniqueListBy(thirdParty, [\"name\"]);\n    return { firstPartyList, thirdPartyList };\n}\nconst { firstPartyList, thirdPartyList } = createUniqueLists(firstParty, thirdParty);\nfunction calculateTimings(party, type) {\n    const partyChoice = party === \"first\" ? firstParty : thirdParty;\n    const timingChoices = {\n        DNS_TIME: [\"domainLookupEnd\", \"domainLookupStart\"],\n        TCP_HANDSHAKE: [\"connectEnd\", \"connectStart\"],\n        RESPONSE_TIME: [\"responseEnd\", \"responseStart\"],\n        SECURE_CONNECTION_TIME: [\"connectEnd\", \"secureConnectionStart\", 0],\n        FETCH_UNTIL_RESPONSE: [\"responseEnd\", \"fetchStart\", 0],\n        REQ_START_UNTIL_RES_END: [\"responseEnd\", \"requestStart\", 0],\n        START_UNTIL_RES_END: [\"responseEnd\", \"startTime\", 0],\n        REDIRECT_TIME: [\"redirectEnd\", \"redirectStart\"],\n    };\n    function handleChoices(timingEnd, timingStart, num) {\n        if (!num) {\n            return timingEnd - timingStart;\n        }\n        if (timingStart \u003e 0) {\n            return timingEnd - timingStart;\n        }\n        return 0;\n    }\n    const timings = partyChoice.map((script) =\u003e {\n        const [timingEnd, timingStart, num] = timingChoices[type];\n        const endValue = script[timingEnd];\n        const startValue = script[timingStart];\n        return {\n            name: script.name,\n            [type]: handleChoices(endValue, startValue, num),\n        };\n    });\n    return timings;\n}\n// Available Options\nconst timingOptions = [\n    \"DNS_TIME\",\n    \"TCP_HANDSHAKE\",\n    \"RESPONSE_TIME\",\n    \"SECURE_CONNECTION_TIME\",\n    \"FETCH_UNTIL_RESPONSE\",\n    \"REQ_START_UNTIL_RES_END\",\n    \"START_UNTIL_RES_END\",\n    \"REDIRECT_TIME\",\n];\n// run em all!\n// https://developer.mozilla.org/en-US/docs/Web/API/Resource_Timing_API/Using_the_Resource_Timing_API#timing_resource_loading_phases\ntimingOptions.forEach((timing) =\u003e {\n    console.groupCollapsed(`FIRST PARTY: ${timing}`);\n    console.table(calculateTimings(\"first\", timing));\n    console.groupEnd();\n    console.groupCollapsed(`THIRD PARTY: ${timing}`);\n    console.table(calculateTimings(\"third\", timing));\n    console.groupEnd();\n});\n// choose your battle - arg1 is string either \"first\" or \"third\", arg2 is string timing option listed above.\nconsole.table(calculateTimings(\"first\", \"REQ_START_UNTIL_RES_END\"));\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Check header](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\n(function () {\n    var ct = document.createElement('style');\n    ct.innerText = `\n    /*!==========================================================================\n   #CT.CSS\n   ========================================================================== */\n\n/*!\n * ct.css – Let’s take a look inside your \u003chead\u003e…\n *\n * © Harry Roberts 2021 – twitter.com/csswizardry\n */\n\n\n\n\n\n/**\n * It’s slightly easier to remember topics than it is colours. Set up some\n * custom properties for use later on.\n */\n\nhead {\n  --ct-is-problematic: solid;\n  --ct-is-affected: dashed;\n  --ct-notify: #0bce6b;\n  --ct-warn: #ffa400;\n  --ct-error: #ff4e42;\n}/**\n * Show the \u003chead\u003e and set up the items we might be interested in.\n */\n\nhead,\nhead script,\nhead script:not([src])[async],\nhead script:not([src])[defer],\nhead style, head [rel=\"stylesheet\"],\nhead script ~ meta[http-equiv=\"content-security-policy\"],\nhead \u003e meta[charset]:not(:nth-child(-n+5)) {\n  display: block;\n}\n\nhead script,\nhead style, head [rel=\"stylesheet\"],\nhead title,\nhead script ~ meta[http-equiv=\"content-security-policy\"],\nhead \u003e meta[charset]:not(:nth-child(-n+5)) {\n  margin: 5px;\n  padding: 5px;\n  border-width: 5px;\n  background-color: white;\n  color: #333;\n}\n\nhead ::before,\nhead script, head style {\n  font: 16px/1.5 monospace, monospace;\n  display: block;\n}\n\nhead ::before {\n  font-weight: bold;\n}/**\n * External Script and Style\n */\n\nhead script[src],\nhead link[rel=\"stylesheet\"] {\n  border-style: var(--ct-is-problematic);\n  border-color: var(--ct-warn);\n}\n\n  head script[src]::before {\n    content: \"[Blocking Script – \" attr(src) \"]\"\n  }\n\n  head link[rel=\"stylesheet\"]::before {\n    content: \"[Blocking Stylesheet – \" attr(href) \"]\"\n  }/**\n * Inline Script and Style.\n */\n\nhead style:not(:empty),\nhead script:not(:empty) {\n  max-height: 5em;\n  overflow: auto;\n  background-color: #ffd;\n  white-space: pre;\n  border-color: var(--ct-notify);\n  border-style: var(--ct-is-problematic);\n}\n\n  head script:not(:empty)::before {\n    content: \"[Inline Script] \";\n  }\n\n  head style:not(:empty)::before {\n    content: \"[Inline Style] \";\n  }/**\n * Blocked Title.\n *\n * These selectors are generally more complex because the Key Selector (\\`title\\`)\n * depends on the specific conditions of preceding JS--we can’t cast a wide net\n * and narrow it down later as we can when targeting elements directly.\n */\n\nhead script[src]:not([async]):not([defer]):not([type=module]) ~ title,\nhead script:not(:empty) ~ title {\n  display: block;\n  border-style: var(--ct-is-affected);\n  border-color: var(--ct-error);\n}\n\n  head script[src]:not([async]):not([defer]):not([type=module]) ~ title::before,\n  head script:not(:empty) ~ title::before {\n    content: \"[\u003ctitle\u003e blocked by JS] \";\n  }/**\n * Blocked Scripts.\n *\n * These selectors are generally more complex because the Key Selector\n * (\\`script\\`) depends on the specific conditions of preceding CSS--we can’t cast\n * a wide net and narrow it down later as we can when targeting elements\n * directly.\n */\n\nhead [rel=\"stylesheet\"]:not([media=\"print\"]):not(.ct) ~ script,\nhead style:not(:empty) ~ script {\n  border-style: var(--ct-is-affected);\n  border-color: var(--ct-warn);\n}\n\n  head [rel=\"stylesheet\"]:not([media=\"print\"]):not(.ct) ~ script::before,\n  head style:not(:empty) ~ script::before {\n    content: \"[JS blocked by CSS – \" attr(src) \"]\";\n  }/**\n * Using both \\`async\\` and \\`defer\\` is redundant (an anti-pattern, even). Let’s\n * flag that.\n */\n\nhead script[src][src][async][defer] {\n  display: block;\n  border-style: var(--ct-is-problematic);\n  border-color: var(--ct-warn);\n}\n\n  head script[src][src][async][defer]::before {\n    content: \"[async and defer is redundant: prefer defer – \" attr(src) \"]\";\n  }/**\n * Async and defer simply do not work on inline scripts. It won’t do any harm,\n * but it’s useful to know about.\n */\nhead script:not([src])[async],\nhead script:not([src])[defer] {\n  border-style: var(--ct-is-problematic);\n  border-color: var(--ct-warn);\n}\n\n  head script:not([src])[async]::before {\n    content: \"The async attribute is redundant on inline scripts\"\n  }\n\n  head script:not([src])[defer]::before {\n    content: \"The defer attribute is redundant on inline scripts\"\n  }/**\n * Third Party blocking resources.\n *\n * Expect false-positives here… it’s a crude proxy at best.\n *\n * Selector-chaining (e.g. \\`[src][src]\\`) is used to bump up specificity.\n */\n\nhead script[src][src][src^=\"//\"],\nhead script[src][src][src^=\"http\"],\nhead [rel=\"stylesheet\"][href^=\"//\"],\nhead [rel=\"stylesheet\"][href^=\"http\"] {\n  border-style: var(--ct-is-problematic);\n  border-color: var(--ct-error);\n}\n\n  head script[src][src][src^=\"//\"]::before,\n  head script[src][src][src^=\"http\"]::before {\n    content: \"[Third Party Blocking Script – \" attr(src) \"]\";\n  }\n\n  head [rel=\"stylesheet\"][href^=\"//\"]::before,\n  head [rel=\"stylesheet\"][href^=\"http\"]::before {\n    content: \"[Third Party Blocking Stylesheet – \" attr(href) \"]\";\n  }/**\n * Mid-HEAD CSP disables the Preload Scanner\n */\n\nhead script ~ meta[http-equiv=\"content-security-policy\"] {\n  border-style: var(--ct-is-problematic);\n  border-color: var(--ct-error);\n}\n\n  head script ~ meta[http-equiv=\"content-security-policy\"]::before {\n    content: \"[Meta CSP defined after JS]\"\n  }/**\n * Charset should appear as early as possible\n */\nhead \u003e meta[charset]:not(:nth-child(-n+5)) {\n  border-style: var(--ct-is-problematic);\n  border-color: var(--ct-warn);\n}\n\nhead \u003e meta[charset]:not(:nth-child(-n+5))::before {\n  content: \"[Charset should appear as early as possible]\";\n}/**\n * Hide all irrelevant or non-matching scripts and styles (including ct.css).\n *\n * We’re done!\n */\n\nlink[rel=\"stylesheet\"][media=\"print\"],\nlink[rel=\"stylesheet\"].ct, style.ct,\nscript[async], script[defer], script[type=module] {\n  display: none;\n}\n    `;\n    ct.classList.add('ct');\n    document.head.appendChild(ct);\n}());\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Check image sizes](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction getImgs(sortBy) {\n    const imgs = [];\n    const resourceListEntries = performance.getEntriesByType(\"resource\");\n    resourceListEntries.forEach(({ name, transferSize, encodedBodySize, decodedBodySize, initiatorType, }) =\u003e {\n        if (initiatorType == \"img\") {\n            imgs.push({\n                name,\n                transferSize,\n                decodedBodySize,\n                encodedBodySize,\n            });\n        }\n    });\n    const imgList = imgs.sort((a, b) =\u003e {\n        return b[sortBy] - a[sortBy];\n    });\n    return imgList;\n}\nconsole.table(getImgs(\"encodedBodySize\"));\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Check image srcset](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction checkImgSrcset(selector) {\n    selector = selector || prompt('Img selector (e.g. div.test \u003e img)');\n    let lastSrc = '';\n    const switches = [];\n    const el = document.querySelector(selector);\n    if (!el) {\n        throw (`Could not fnd any element with selector ${selector}`);\n    }\n    const resizeObserver = new ResizeObserver((entries) =\u003e {\n        const clientWidth = document.body.clientWidth;\n        for (const entry of entries) {\n            const img = entry.target;\n            if (lastSrc !== img.currentSrc) {\n                lastSrc = img.currentSrc;\n                lastSrc \u0026\u0026 loadImg(lastSrc).then(i =\u003e {\n                    switches.push({\n                        clientWidth,\n                        element: el,\n                        src: lastSrc,\n                        intrinsicWith: i.width,\n                        intrinsicHeight: i.height,\n                        renderedWith: el.clientWidth,\n                        renderedHeight: el.clientHeight,\n                        sizeDiff: ((i.width * i.height) / (el.clientWidth * el.clientHeight))\n                    });\n                    highlightElement(switches);\n                    logData(switches);\n                });\n                highlightElement(switches);\n                logData(switches);\n            }\n        }\n    });\n    resizeObserver.observe(el);\n}\nfunction logData(data) {\n    console.clear();\n    console.table(prepareTable(data));\n}\nfunction highlightElement(arr) {\n    arr.forEach(o =\u003e {\n        const { element, intrinsicWith, intrinsicHeight } = o;\n        if (element \u0026\u0026 intrinsicWith \u0026\u0026 intrinsicHeight) {\n            const d = ((intrinsicWith * intrinsicHeight) / (element.clientWidth * element.clientHeight));\n            // for over-size border for under-size opacity?\n            element.style.border = 1 + 'px solid red';\n            element.style.opacity = 0.5 * d;\n        }\n    });\n}\nfunction prepareTable(arr) {\n    return arr\n        .map(({ element, ...inTable }) =\u003e ({\n        dpr: window.devicePixelRatio,\n        clientWidth: inTable.clientWidth + 'px',\n        src: inTable.src,\n        intrinsicSize: inTable.intrinsicWith + 'x' + inTable.intrinsicHeight + 'px',\n        renderedSize: inTable.renderedWith + 'x' + inTable.renderedHeight + 'px',\n        sizeDiff: inTable.sizeDiff.toFixed(2)\n    }));\n}\nfunction loadImg(url) {\n    return new Promise((resolve, reject) =\u003e {\n        const img = new Image;\n        img.onload = function () {\n            resolve(img);\n        };\n        img.onerror = (e) =\u003e reject(e);\n        img.src = url;\n    });\n}\n;\ncheckImgSrcset();\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Check image usage](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconst bgUrlChecker = /(url\\([\"'])([A-Za-z0-9$.:/_\\-~]*)([\"']\\))(?!data:$)/g;\nconst base64UrlChecker = /(url\\([\"'])(data:)([A-Za-z0-9$.:/_\\-~]*)/g;\nconst srcChecker = /(src=[\"'])([A-Za-z0-9$.:/_\\-~]*)([\"'])(?!data:$)/g;\nconst bgSRule = 'background';\nconst bgImgSRule = 'background-image';\nconst msgNotLazyLoaded = \"❌ not lazy loaded\";\nconst msgNotEagerLoaded = \"❌ not eager loaded\";\nconst msgDontUseBgImage = \"❌ don't use bg image\";\nconst msgDontUseBgDataImage = \"❌ don't use data:\u003cformat\u003e\";\nconst msgNotDisplayed = \"⚠ fetched but not displayed\";\nconst msgUnknown = \"⚠ Case not implemented\";\nconst msgOk = \"🆗\";\nfunction fixUsage(imgs) {\n    let l = '';\n    imgs.forEach(i =\u003e {\n        switch (i.error) {\n            case msgNotEagerLoaded:\n                l = \"eager\";\n                break;\n            case msgNotLazyLoaded:\n                l = \"lazy\";\n                break;\n        }\n        l \u0026\u0026 i.tag.setAttribute('loading', l);\n    });\n}\nfunction highlightElements(imgs) {\n    let s = '';\n    imgs.forEach(i =\u003e {\n        switch (i.error) {\n            case msgNotEagerLoaded:\n                s = 'outline: 3px red solid;';\n                break;\n            case msgNotLazyLoaded:\n                s = 'outline: 3px red dotted;';\n                break;\n            case msgDontUseBgDataImage:\n                s = 'outline: 3px red dashed;';\n                break;\n            case msgDontUseBgImage:\n                s = 'outline: 3px red dashed;';\n                break;\n        }\n        s \u0026\u0026 i.tag.setAttribute('style', s);\n    });\n}\nfunction isInViewPort(tag) {\n    return tag.offsetTop \u003c window.innerHeight \u0026\u0026\n        tag.offsetTop \u003e -tag.offsetHeight \u0026\u0026\n        tag.offsetLeft \u003e -tag.offsetWidth \u0026\u0026\n        tag.offsetLeft \u003c window.innerWidth;\n}\nfunction styles(tag, pseudoElt) {\n    return window.getComputedStyle(tag, pseudoElt || null);\n}\nfunction getImgRelevantRules(tag) {\n    const res = {\n        withBgImgNodes: new Map(),\n        withBgDataImgNodes: new Map()\n    };\n    let matchBgB64 = base64UrlChecker.exec(tag.attributes.src);\n    if (matchBgB64) {\n        res.withBgImgNodes.set(matchBgB64[3], tag);\n    }\n    [null, '::before', '::after']\n        .map((pseudoElt) =\u003e {\n        const backgroundVal = styles(tag, pseudoElt).getPropertyValue(bgSRule);\n        const backgroundImageVal = styles(tag, pseudoElt).getPropertyValue(bgImgSRule);\n        let matchBg = bgUrlChecker.exec(backgroundVal) || bgUrlChecker.exec(backgroundImageVal);\n        let matchBgB64 = base64UrlChecker.exec(backgroundVal) || base64UrlChecker.exec(backgroundImageVal);\n        if (matchBg) {\n            res.withBgImgNodes.set(matchBg[2], tag);\n        }\n        else if (matchBgB64) {\n            res.withBgDataImgNodes.set(matchBgB64[3], tag);\n        }\n    });\n    return res;\n}\nfunction getNetworkImgs() {\n    const imgs = new Map();\n    const resourceListEntries = performance.getEntriesByType(\"resource\");\n    resourceListEntries.forEach(({ name, transferSize, initiatorType, }) =\u003e {\n        if (initiatorType == \"img\") {\n            imgs.set(name, {\n                name,\n                transferSize\n            });\n        }\n    });\n    return imgs;\n}\nfunction getImgsWithBackground(doc) {\n    const withBgImgNames = new Set();\n    const withBgImgNodes = new Map();\n    const withBgDataImgNames = new Set();\n    const withBgDataImgNodes = new Map();\n    Array.from(doc.querySelectorAll('body *'))\n        .forEach((tag) =\u003e {\n        const badRules = getImgRelevantRules(tag);\n        Array.from(badRules.withBgImgNodes.entries()).forEach(([url, _]) =\u003e {\n            withBgImgNodes.set(url, tag);\n            withBgImgNames.add(url);\n        });\n        Array.from(badRules.withBgDataImgNodes.entries()).forEach(([url, _]) =\u003e {\n            withBgDataImgNodes.set(url, tag);\n            withBgDataImgNames.add(url);\n        });\n    });\n    return { withBgImgNodes, withBgImgNames, withBgDataImgNodes, withBgDataImgNames };\n}\nfunction findImagesAndLoadingAttribute(doc) {\n    const imgs = doc.querySelectorAll('img');\n    const lazyLoadedAboveTheFoldNodes = new Map();\n    const lazyLoadedAboveTheFoldNames = new Set();\n    const eagerLoadedBelowTheFoldNodes = new Map();\n    const eagerLoadedBelowTheFoldNames = new Set();\n    imgs.forEach((tag) =\u003e {\n        const inViewPort = isInViewPort(tag);\n        const url = tag.attributes.src ? tag.attributes.src.value : null;\n        // Ignore images without URL since they might be handled by custom javaScript lazy loading technique.\n        if (!url)\n            return;\n        const isLazy = tag.attributes.loading === 'lazy';\n        if (isLazy \u0026\u0026 inViewPort) {\n            lazyLoadedAboveTheFoldNodes.set(url, tag);\n            lazyLoadedAboveTheFoldNames.add(url);\n        }\n        else if (!isLazy \u0026\u0026 !inViewPort) {\n            eagerLoadedBelowTheFoldNodes.set(url, tag);\n            eagerLoadedBelowTheFoldNames.add(url);\n        }\n    });\n    return {\n        lazyLoadedAboveTheFoldNames,\n        lazyLoadedAboveTheFoldNodes,\n        eagerLoadedBelowTheFoldNames,\n        eagerLoadedBelowTheFoldNodes\n    };\n}\nconst { lazyLoadedAboveTheFoldNodes, lazyLoadedAboveTheFoldNames, eagerLoadedBelowTheFoldNodes, eagerLoadedBelowTheFoldNames } = findImagesAndLoadingAttribute(document);\nconst { withBgDataImgNames, withBgDataImgNodes, withBgImgNames, withBgImgNodes } = getImgsWithBackground(document);\nconst networkImgs = getNetworkImgs();\nconst allNames = Array.from(new Set([\n    ...lazyLoadedAboveTheFoldNames,\n    ...eagerLoadedBelowTheFoldNames,\n    ...withBgImgNames,\n    ...withBgDataImgNames\n]));\nfunction enrichSizeUsage(imgData) {\n    return Promise.all(imgData.map((i, idx) =\u003e {\n        return new Promise((r) =\u003e {\n            const img = new Image;\n            const wRetain = i.tag.width;\n            const hRetain = i.tag.height;\n            img.onload = function () {\n                // mutation!\n                imgData[idx].imgDisplayDiff = `${wRetain}/${hRetain} to ${img.width}/${img.height}`;\n                r();\n            };\n            img.onerror = r;\n            img.src = i.url;\n        });\n    })).then(() =\u003e imgData);\n}\nfunction enrichData() {\n    return Array.from(allNames).map((url) =\u003e {\n        let imgData = {\n            tag: 'n/a',\n            url,\n            error: '',\n            transferSize: '?'\n        };\n        let errorDetected = true;\n        switch (true) {\n            case eagerLoadedBelowTheFoldNames.has(url):\n                imgData.tag = eagerLoadedBelowTheFoldNodes.get(url);\n                imgData.error = msgNotLazyLoaded;\n                break;\n            case lazyLoadedAboveTheFoldNames.has(url):\n                imgData.tag = lazyLoadedAboveTheFoldNodes.get(url);\n                imgData.error = msgNotEagerLoaded;\n                break;\n            case withBgImgNames.has(url):\n                imgData.tag = withBgImgNodes.get(url);\n                imgData.error = msgDontUseBgImage;\n                break;\n            case withBgDataImgNames.has(url):\n                imgData.tag = withBgDataImgNodes.get(url);\n                imgData.error = msgDontUseBgDataImage;\n                imgData.transferSize = url.length * 1.02;\n                break;\n            default:\n                errorDetected = false;\n        }\n        if (networkImgs.has(url)) {\n            const { transferSize, decodedBodySize, encodedBodySize } = networkImgs.get(url);\n            imgData = { ...imgData, transferSize, decodedBodySize };\n            if (!errorDetected) {\n                imgData.error = msgOk;\n            }\n        }\n        return imgData;\n    });\n}\nconst d = enrichData();\nhighlightElements(d);\nfixUsage(d);\nenrichSizeUsage(d).then(console.table);\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Cls](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction genColor() {\n    let n = (Math.random() * 0xfffff * 1000000).toString(16);\n    return \"#\" + n.slice(0, 6);\n}\n// console.log(shifts) to see full list of shifts above threshold\nconst shifts = [];\n// threshold ex: 0.05\n// Layout Shifts will be grouped by color.\n// All nodes attributed to the shift will have a border with the corresponding color\n// Shift value will be added above parent node.\n// Will have all details related to that shift in dropdown\n// Useful for single page applications and finding shifts after initial load\nfunction findShifts(threshold) {\n    return new PerformanceObserver((list) =\u003e {\n        list.getEntries().forEach((entry) =\u003e {\n            if (entry.value \u003e threshold \u0026\u0026 !entry.hadRecentInput) {\n                const color = genColor();\n                shifts.push(entry);\n                console.log(shifts);\n                const valueNode = document.createElement(\"details\");\n                valueNode.innerHTML = `\n\u003csummary\u003eLayout Shift: ${entry.value}\u003c/summary\u003e\n\u003cpre\u003e${JSON.stringify(entry, null, 2)}\u003c/pre\u003e\n`;\n                valueNode.style = `color: ${color};`;\n                entry.sources.forEach((source) =\u003e {\n                    source.node.parentNode.insertBefore(valueNode, source.node);\n                    source.node.style = `border: 2px ${color} solid`;\n                });\n            }\n        });\n    });\n}\nfindShifts(0.05).observe({ entryTypes: [\"layout-shift\"] });\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Cumulative layout shift](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\ntry {\n    let cumulativeLayoutShiftScore = 0;\n    const observer = new PerformanceObserver((list) =\u003e {\n        for (const entry of list.getEntries()) {\n            if (!entry.hadRecentInput) {\n                cumulativeLayoutShiftScore += entry.value;\n            }\n        }\n    });\n    observer.observe({ type: \"layout-shift\", buffered: true });\n    document.addEventListener(\"visibilitychange\", () =\u003e {\n        if (document.visibilityState === \"hidden\") {\n            observer.takeRecords();\n            observer.disconnect();\n            console.log(`CLS: ${cumulativeLayoutShiftScore}`);\n        }\n    });\n}\ncatch (e) {\n    console.log(`Browser doesn't support this API`);\n}\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Full relayout](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconst b = document.body;\nb.style.zoom === '1' ? b.style.zoom = '1.01' : b.style.zoom = '1';\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Gathering styles](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconsole.log(Array.from(document.querySelectorAll('style'))\n    .map(a =\u003e a.innerText)\n    .reduce((a, b) =\u003e a + b));\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Get Attribute Directives](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction getAttributeDirectives() {\n    const { name, showSummaryInDOM, appPrefixes, mode } = initializeFlow();\n    /**\n     * Filter out nodes that don't have an attribute\n     */\n    function filterAttribute(attribute, prefixes) {\n        return Array.isArray(prefixes)\n            ? prefixes.some((p) =\u003e attribute.name ? attribute.name.startsWith(p.toLowerCase()) : false)\n            : attribute.name\n                ? attribute.name.startsWith(prefixes.toLowerCase())\n                : false;\n    }\n    function initializeFlow() {\n        /**\n         * Clearing summary items from DOM.\n         */\n        const summaries = document.querySelectorAll(\".customSummaryItem\");\n        summaries.forEach((i) =\u003e i.remove());\n        const mode = prompt(\"Mode: summary or directive\");\n        switch (mode) {\n            case \"directive\":\n                return {\n                    mode,\n                    name: prompt(\"Directive name\"),\n                    showSummaryInDOM: prompt(\"Apply summary info to elements? (yes/no)\", \"no\") === \"yes\"\n                        ? true\n                        : false,\n                };\n            case \"summary\":\n                return {\n                    mode,\n                    appPrefixes: prompt(\"Directives prefixes, comma separated. (ex: app)\")\n                        .split(\",\")\n                        .map((p) =\u003e p.trim()) || \"app\",\n                    showSummaryInDOM: prompt(\"Apply summary info to elements? (yes/no)\", \"no\") === \"yes\"\n                        ? true\n                        : false,\n                };\n        }\n    }\n    /**\n     * Set of checks to determine if element is hidden.\n     */\n    function isHidden(element) {\n        return !(element.offsetWidth ||\n            element.offsetHeight ||\n            element.getClientRects().length);\n    }\n    // Checks if element is in viewport\n    function isInViewport(element) {\n        return (element.offsetTop \u003c window.innerHeight \u0026\u0026\n            element.offsetTop \u003e -element.offsetHeight \u0026\u0026\n            element.offsetLeft \u003e -element.offsetWidth \u0026\u0026\n            element.offsetLeft \u003c window.innerWidth);\n    }\n    /**\n     * Adds summary div to references\n     */\n    function addSummary(nodes, prefixes) {\n        nodes.forEach((n) =\u003e {\n            n.style.position = \"relative\";\n            const node = document.createElement(\"DIV\");\n            Object.assign(node.style, {\n                position: \"absolute\",\n                top: \"0\",\n                left: \"0\",\n                \"z-index\": \"999999\",\n                \"font-size\": \"14px\",\n                display: \"flex\",\n                background: \"green\",\n                color: \"#fff\",\n                padding: \"4px\",\n            });\n            node.classList.add(\"customSummaryItem\");\n            const text = document.createTextNode(`${[...n.attributes]\n                .filter((a) =\u003e filterAttribute(a, prefixes))\n                .map((a) =\u003e a.name).length}`);\n            node.appendChild(text);\n            n.appendChild(node);\n        });\n    }\n    /**\n     * Finds references of the nodes that contain directive with given name\n     */\n    function findReferences(name) {\n        const directives = Array.from(document.querySelectorAll(`[${name}]`)).map((r) =\u003e {\n            return {\n                name,\n                hidden: isHidden(r),\n                visible: !isHidden(r),\n                inViewport: isInViewport(r),\n                outOfViewport: !isInViewport(r),\n                reference: r,\n            };\n        });\n        return {\n            all: directives,\n            visible: directives.filter((c) =\u003e !c.hidden),\n            hidden: directives.filter((c) =\u003e c.hidden),\n            inViewport: directives.filter((c) =\u003e c.inViewport),\n            outOfViewport: directives.filter((c) =\u003e !c.inViewport),\n        };\n    }\n    /**\n     * Get summary data for all directives\n     */\n    function getAllDirectivesSummary(prefixes) {\n        const nodesWithDirectives = Array.from(document.body.querySelectorAll(\"*\")).filter((e) =\u003e Array.from(e.attributes).some((a) =\u003e filterAttribute(a, prefixes)));\n        const directives = \n        // Find unique components names in page\n        [\n            ...new Set(nodesWithDirectives\n                .map((e) =\u003e [...e.attributes]\n                .filter((a) =\u003e filterAttribute(a, prefixes))\n                .map((a) =\u003e a.name))\n                .reduce((acc, val) =\u003e [...acc, ...val], [])),\n        ]\n            .map((name) =\u003e getSpecificDirectiveSummary(name))\n            .reduce((acc, val) =\u003e [...acc, val[0]], []);\n        if (showSummaryInDOM) {\n            addSummary(nodesWithDirectives, prefixes);\n        }\n        return [\n            {\n                name: \"📝TOTAL\",\n                visible: directives\n                    .map((c) =\u003e c.visible)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                hidden: directives\n                    .map((c) =\u003e c.hidden)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                inViewport: directives\n                    .map((c) =\u003e c.inViewport)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                outOfViewport: directives\n                    .map((c) =\u003e c.outOfViewport)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                reference: \"----\",\n            },\n            ...directives,\n        ];\n    }\n    /**\n     * Get summary data for specific directive\n     */\n    function getSpecificDirectiveSummary(name, showSummary) {\n        const { all, visible, hidden, inViewport, outOfViewport } = findReferences(name);\n        if (showSummary) {\n            addSummary(all.map((e) =\u003e e.reference), name);\n        }\n        return [\n            {\n                name: `👉 ${name}`,\n                visible: visible.length,\n                hidden: hidden.length,\n                inViewport: inViewport.length,\n                outOfViewport: outOfViewport.length,\n                reference: {\n                    visible,\n                    hidden,\n                    inViewport,\n                    outOfViewport,\n                },\n            },\n            ...all,\n        ];\n    }\n    switch (mode) {\n        case \"directive\":\n            return console.table(getSpecificDirectiveSummary(name, showSummaryInDOM));\n        case \"summary\":\n            return console.table(getAllDirectivesSummary(appPrefixes));\n    }\n}\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Get Components Nodes](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction index() {\n    const { name, showSummaryInDOM, appPrefixes, mode, allNodes, visibleNodes, hiddenNodes, inViewportNodes, outOfViewportNodes, } = initializeFlow();\n    /**\n     * Flow init\n     */\n    function initializeFlow() {\n        /**\n         * Clearing summary items from DOM.\n         */\n        const summaries = document.querySelectorAll(\".customSummaryItem\");\n        summaries.forEach((i) =\u003e i.remove());\n        const mode = prompt(\"Mode: summary or component\");\n        const allNodes = Array.from(document.body.querySelectorAll(\"*\"));\n        const visibleNodes = [];\n        const hiddenNodes = [];\n        const inViewportNodes = [];\n        const outOfViewportNodes = [];\n        allNodes.forEach((n) =\u003e {\n            if (isHidden(n)) {\n                hiddenNodes.push(n);\n            }\n            else {\n                visibleNodes.push(n);\n            }\n            if (isInViewport(n)) {\n                inViewportNodes.push(n);\n            }\n            else {\n                outOfViewportNodes.push(n);\n            }\n        });\n        switch (mode) {\n            case \"component\":\n                return {\n                    mode,\n                    name: prompt(\"Component name\"),\n                    showSummaryInDOM: prompt(\"Apply summary info to elements? (yes/no)\", \"no\") === \"yes\"\n                        ? true\n                        : false,\n                    allNodes,\n                    visibleNodes,\n                    hiddenNodes,\n                    inViewportNodes,\n                    outOfViewportNodes,\n                };\n            case \"summary\":\n                return {\n                    mode,\n                    appPrefixes: prompt(\"Components prefixes, comma separated. (ex: app)\")\n                        .split(\",\")\n                        .map((p) =\u003e p.trim()) || \"app\",\n                    showSummaryInDOM: prompt(\"Apply summary info to elements? (yes/no)\", \"no\") === \"yes\"\n                        ? true\n                        : false,\n                    allNodes,\n                    visibleNodes,\n                    hiddenNodes,\n                    inViewportNodes,\n                    outOfViewportNodes,\n                };\n        }\n    }\n    /**\n     * Set of checks to determine if element is hidden.\n     */\n    function isHidden(element) {\n        return !(element.offsetWidth ||\n            element.offsetHeight ||\n            element.getClientRects().length);\n    }\n    /**\n     * Checks if element is in viewport.\n     */\n    function isInViewport(element) {\n        return (element.offsetTop \u003c window.innerHeight \u0026\u0026\n            element.offsetTop \u003e -element.offsetHeight \u0026\u0026\n            element.offsetLeft \u003e -element.offsetWidth \u0026\u0026\n            element.offsetLeft \u003c window.innerWidth);\n    }\n    /**\n     * Adds summary div to references\n     */\n    function addSummary(nodes) {\n        nodes.forEach((n) =\u003e {\n            n.references.self.style.position = \"relative\";\n            const node = document.createElement(\"DIV\");\n            const totalNode = document.createElement(\"SPAN\");\n            const visibleNode = document.createElement(\"SPAN\");\n            const hiddenNode = document.createElement(\"SPAN\");\n            const totalText = document.createTextNode(` Total: ${n.visibleNodes + n.hiddenNodes} `);\n            const visibleText = document.createTextNode(` Visible: ${n.visibleNodes} `);\n            const hiddenText = document.createTextNode(` Hidden: ${n.hiddenNodes} `);\n            /**\n             * Appending styles\n             */\n            Object.assign(node.style, {\n                position: \"absolute\",\n                top: \"0\",\n                left: \"0\",\n                \"z-index\": \"999999\",\n                \"font-size\": \"14px\",\n                display: \"flex\",\n            });\n            Object.assign(totalNode.style, { background: \"black\", color: \"#fff\" });\n            Object.assign(visibleNode.style, { background: \"green\", color: \"#fff\" });\n            Object.assign(hiddenNode.style, { background: \"red\", color: \"#fff\" });\n            totalNode.appendChild(totalText);\n            visibleNode.appendChild(visibleText);\n            hiddenNode.appendChild(hiddenText);\n            node.appendChild(totalNode);\n            node.appendChild(visibleNode);\n            node.appendChild(hiddenNode);\n            node.classList.add(\"customSummaryItem\");\n            n.references.self.appendChild(node);\n        });\n    }\n    /**\n     * Finds references of the component with given name\n     */\n    function findReferences(name, showSummary) {\n        const components = Array.from(document.querySelectorAll(name)).map((r) =\u003e {\n            const childNodes = [r, ...r.querySelectorAll(\"*\")];\n            const hiddenNodes = [];\n            const visibleNodes = [];\n            const inViewportNodes = [];\n            const outOfViewportNodes = [];\n            childNodes.forEach((c) =\u003e {\n                if (isHidden(c)) {\n                    hiddenNodes.push(c);\n                }\n                else {\n                    visibleNodes.push(c);\n                }\n                if (isInViewport(c)) {\n                    inViewportNodes.push(c);\n                }\n                else {\n                    outOfViewportNodes.push(c);\n                }\n            });\n            return {\n                name: r.nodeName,\n                nodes: childNodes.length,\n                visibleNodes: visibleNodes.length,\n                hiddenNodes: hiddenNodes.length,\n                inViewportNodes: inViewportNodes.length,\n                outOfViewportNodes: outOfViewportNodes.length,\n                hidden: isHidden(r),\n                visible: !isHidden(r),\n                inViewport: isInViewport(r),\n                outOfViewport: !isInViewport(r),\n                references: {\n                    self: r,\n                    visibleNodes,\n                    hiddenNodes,\n                    inViewportNodes,\n                    outOfViewportNodes,\n                },\n            };\n        });\n        if (showSummary) {\n            addSummary(components);\n        }\n        return {\n            all: components,\n            visible: components.filter((c) =\u003e !c.hidden),\n            hidden: components.filter((c) =\u003e c.hidden),\n            inViewport: components.filter((c) =\u003e c.inViewport),\n            outOfViewport: components.filter((c) =\u003e !c.inViewport),\n        };\n    }\n    /**\n     * Get summary data for all components\n     */\n    function getAllComponentsSummary(prefixes) {\n        const components = [\n            ...new Set(allNodes\n                .filter((e) =\u003e Array.isArray(prefixes)\n                ? prefixes.some((p) =\u003e e.nodeName.startsWith(p.toUpperCase()))\n                : e.nodeName.startsWith(prefix.toUpperCase()))\n                .map((e) =\u003e e.nodeName)),\n        ]\n            .map((name) =\u003e getSpecificComponentSummary(name))\n            .reduce((acc, val) =\u003e [...acc, val[0]], []);\n        return [\n            {\n                name: \"📝TOTAL\",\n                visible: components\n                    .map((c) =\u003e c.visible)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                hidden: components\n                    .map((c) =\u003e c.hidden)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                inViewport: components\n                    .map((c) =\u003e c.inViewport)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                outOfViewport: components\n                    .map((c) =\u003e c.outOfViewport)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                nodes: allNodes.length,\n                visibleNodes: visibleNodes.length,\n                hiddenNodes: hiddenNodes.length,\n                inViewportNodes: inViewportNodes.length,\n                outOfViewportNodes: outOfViewportNodes.length,\n                references: \"----\",\n            },\n            ...components,\n        ];\n    }\n    /**\n     * Get summary data for provided component name\n     */\n    function getSpecificComponentSummary(name) {\n        const { all, visible, hidden, inViewport, outOfViewport } = findReferences(name.toUpperCase(), showSummaryInDOM);\n        return [\n            {\n                name: `👉 ${name.toUpperCase()}`,\n                // Components counters\n                visible: visible.length,\n                hidden: hidden.length,\n                inViewport: inViewport.length,\n                outOfViewport: outOfViewport.length,\n                // Nodes counters\n                nodes: all.map((r) =\u003e r.nodes).reduce((acc, val) =\u003e acc + val, 0),\n                visibleNodes: all\n                    .map((r) =\u003e (!r.hidden ? r.visibleNodes : 0))\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                hiddenNodes: all\n                    .map((r) =\u003e (r.hidden ? r.nodes : r.hiddenNodes))\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                inViewportNodes: all\n                    .map((r) =\u003e r.inViewportNodes)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                outOfViewportNodes: all\n                    .map((r) =\u003e r.outOfViewportNodes)\n                    .reduce((acc, val) =\u003e acc + val, 0),\n                // References\n                references: {\n                    visible,\n                    hidden,\n                    inViewport,\n                    outOfViewport,\n                },\n            },\n            ...all,\n        ];\n    }\n    switch (mode) {\n        case \"component\":\n            return console.table(getSpecificComponentSummary(name));\n        case \"summary\":\n            return console.table(getAllComponentsSummary(appPrefixes));\n    }\n}\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Get DOMEvent Listeners](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction getDOMEventListeners() {\n    // Get all elements with event listeners\n    const elements = [document, ...document.querySelectorAll(\"*\")]\n        .map((e) =\u003e {\n        const elementListeners = window.getEventListeners(e);\n        return {\n            element: e,\n            listeners: Object.keys(elementListeners)\n                .map((key) =\u003e ({\n                [key]: elementListeners[key],\n            }))\n                .reduce((acc, listener) =\u003e ({\n                ...acc,\n                ...listener,\n            }), {}),\n        };\n    })\n        .filter((el) =\u003e Object.keys(el.listeners).length);\n    // Find unique listeners names\n    const names = new Set(elements\n        .map((e) =\u003e Object.keys(e.listeners))\n        .reduce((acc, listener) =\u003e [...acc, ...listener], []));\n    // Form output table\n    const table = [...names].reduce((acc, n) =\u003e {\n        const withListener = elements.filter((e) =\u003e e.listeners[n]);\n        const total = withListener.reduce((acc, e) =\u003e acc + e.listeners[n].length, 0);\n        const activeListeners = withListener.reduce((acc, e) =\u003e acc + e.listeners[n].filter((l) =\u003e !l.passive).length, 0);\n        const activeReferences = withListener.reduce((acc, e) =\u003e e.listeners[n].filter((l) =\u003e !l.passive).length ? [...acc, e] : acc, []);\n        const passiveListeners = withListener.reduce((acc, e) =\u003e acc + e.listeners[n].filter((l) =\u003e l.passive).length, 0);\n        const passiveReferences = withListener.reduce((acc, e) =\u003e e.listeners[n].filter((l) =\u003e l.passive).length ? [...acc, e] : acc, []);\n        const onceListeners = withListener.reduce((acc, e) =\u003e acc + e.listeners[n].filter((l) =\u003e l.once).length, 0);\n        const onceReferences = withListener.reduce((acc, e) =\u003e e.listeners[n].filter((l) =\u003e l.once).length ? [...acc, e] : acc, []);\n        return [\n            ...acc,\n            {\n                name: n,\n                total,\n                activeListeners,\n                activeListeners,\n                passiveListeners,\n                onceListeners,\n                references: {\n                    active: activeReferences,\n                    passive: passiveReferences,\n                    once: onceReferences,\n                },\n            },\n        ];\n    }, []);\n    console.table([\n        {\n            name: \"📝TOTAL\",\n            total: table.reduce((acc, val) =\u003e acc + val.total, 0),\n            activeListeners: table.reduce((acc, val) =\u003e acc + val.activeListeners, 0),\n            passiveListeners: table.reduce((acc, val) =\u003e acc + val.passiveListeners, 0),\n            onceListeners: table.reduce((acc, val) =\u003e acc + val.onceListeners, 0),\n            references: \"----\",\n        },\n        ...table,\n    ]);\n}\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Get Nodes Info](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction index(root = document.body) {\n    const allNodes = [...root.querySelectorAll(\"*\")];\n    const notProcessed = allNodes.filter((n) =\u003e isHidden(n));\n    const processed = allNodes.filter((n) =\u003e !isHidden(n));\n    const visibility = processed.filter((n) =\u003e isVisibilityHidden(n));\n    const opacity = processed.filter((n) =\u003e isOpacity0(n));\n    const dimensions = processed.filter((n) =\u003e isHeightWidthOverflow(n));\n    const transform = processed.filter((n) =\u003e isTransformHidden(n));\n    const opacityFilter = processed.filter((n) =\u003e isFilterOpacity(n));\n    /**\n     * Finds elements that are not affecting layout of the page and will not be included in styles recalculation\n     */\n    function isHidden(element) {\n        return !(element.offsetWidth ||\n            element.offsetHeight ||\n            element.getClientRects().length);\n    }\n    /**\n     * This elements are still processed during style recalculation\n     */\n    function isVisibilityHidden(element) {\n        return window.getComputedStyle(element).visibility === \"hidden\";\n    }\n    /**\n     * This elements are still processed during style recalculation\n     */\n    function isOpacity0(element) {\n        return window.getComputedStyle(element).opacity === \"0\";\n    }\n    /**\n     * This elements are still processed during style recalculation\n     */\n    function isHeightWidthOverflow(element) {\n        const styles = window.getComputedStyle(element);\n        return (((styles.height === \"0\" || styles.height === \"0px\") \u0026\u0026\n            styles.overflow === \"hidden\") ||\n            ((styles.width === \"0\" || styles.width === \"0px\") \u0026\u0026\n                styles.overflow === \"hidden\") ||\n            ((styles.height === \"0\" ||\n                (styles.height === \"0px\" \u0026\u0026 styles.width === \"0\") ||\n                styles.width === \"0px\") \u0026\u0026\n                styles.overflow === \"hidden\"));\n    }\n    /**\n     * This elements are still processed during style recalculation\n     */\n    function isTransformHidden(element) {\n        return element.style.tranform === \"scale(0)\";\n    }\n    /**\n     * This elements are still processed during style recalculation\n     */\n    function isFilterOpacity(element) {\n        return element.style.filter === \"opacity(0)\";\n    }\n    /**\n     * This elements are still processed during style recalculation\n     */\n    function getReferences(nodes) {\n        return nodes.map((n) =\u003e ({\n            self: n,\n            children: n.querySelectorAll(\"*\"),\n        }));\n    }\n    function getSummary(name, nodes) {\n        const children = nodes\n            .map((n) =\u003e n.querySelectorAll(\"*\").length + 1)\n            .reduce((acc, val) =\u003e acc + val, 0);\n        return {\n            \"hiding method\": name,\n            nodes: nodes.length,\n            children,\n            \"potential savings (%)\": Number(parseFloat((children / processed.length) * 100).toFixed(2)),\n            references: getReferences(nodes),\n        };\n    }\n    console.table([\n        {\n            name: `📝TOTAL`,\n            nodes: allNodes.length,\n            processed: processed.length,\n            notProcessed: notProcessed.length,\n        },\n    ]);\n    const summary = [\n        getSummary(\"visibility: none\", visibility),\n        getSummary(\"opacity: 0\", opacity),\n        getSummary(\"height: 0 || width: 0 \u0026\u0026 overflow: hidden\", dimensions),\n        getSummary(\"transform: scale(0)\", transform),\n        getSummary(\"filter: opacity(0)\", opacityFilter),\n    ];\n    return console.table([\n        {\n            \"hiding method\": \"👉SUMMARY\",\n            nodes: summary.reduce((acc, val) =\u003e acc + val.nodes, 0),\n            children: summary.reduce((acc, val) =\u003e acc + val.children, 0),\n            \"potential savings (%)\": Number(summary\n                .reduce((acc, val) =\u003e acc + val[\"potential savings (%)\"], 0)\n                .toFixed(2)),\n            references: \"----\",\n        },\n        ...summary,\n    ]);\n}\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Lcp](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\n/**\n * PerformanceObserver\n */\nconst po = new PerformanceObserver((list) =\u003e {\n    let entries = list.getEntries();\n    entries = dedupe(entries, \"startTime\");\n    /**\n     * Print all entries of LCP\n     */\n    entries.forEach((item, i) =\u003e {\n        console.dir(item);\n        console.log(`${i + 1} current LCP item : ${item.element}: ${item.startTime}`);\n        /**\n         * Highlight LCP elements on the page\n         */\n        item.element ? (item.element.style = \"border: 5px dotted blue;\") : console.warn('LCP not highlighted');\n    });\n    /**\n     * LCP is the lastEntry in getEntries Array\n     */\n    const lastEntry = entries[entries.length - 1];\n    /**\n     * Print final LCP\n     */\n    console.log(`LCP is: ${lastEntry.startTime}`);\n});\n/**\n * Start observing for largest-contentful-paint\n * buffered true getEntries prior to this script execution\n */\npo.observe({ type: \"largest-contentful-paint\", buffered: true });\nfunction dedupe(arr, key) {\n    return [...new Map(arr.map((item) =\u003e [item[key], item])).values()];\n}\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Long task](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\ntry {\n    // Create the performance observer.\n    const po = new PerformanceObserver((list) =\u003e {\n        for (const entry of list.getEntries()) {\n            // Log the entry and all associated details.\n            console.table(entry.toJSON());\n        }\n    });\n    // Start listening for `longtask` entries to be dispatched.\n    po.observe({ type: 'longtask', buffered: true });\n}\ncatch (e) {\n    console.log(`The browser doesn't support this API`);\n}\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Make lazy img](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconst imgs = document.querySelectorAll('img');\nArray.from(imgs)\n    .forEach(i =\u003e i.setAttribute('loading', 'lazy'));\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Optimize svg usage](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nfunction createElementFromHTMLString(htmlString) {\n    var div = document.createElement('div');\n    div.innerHTML = htmlString.trim();\n    return div.firstChild;\n}\nfunction cacheInDom(svgElem, svgId) {\n    const node = svgElem.cloneNode(svgElem);\n    node?.setAttribute \u0026\u0026 node.setAttribute('id', svgId);\n    svgDomCache.appendChild(node);\n}\nfunction modifySvgToUseCache(svgElem, svgId) {\n    //svgElem.replaceWith(createElementFromHTMLString(`\u003csvg\u003e\u003cuse href=\"#${svgId}\"\u003e\u003c/use\u003e\u003c/svg\u003e`));\n    svgElem.innerHTML = `\u003cuse href=\"#${svgId}\"\u003e\u003c/use\u003e`;\n}\nlet nextCachedSvgId = Math.random();\nconst svgDomCacheHtml = `\u003cdiv id=\"svg-cache\" style=\"\n    overflow: hidden;\n    width: 0;\n    height: 0;\n    position: fixed;\n    bottom: -2000px;\n    contain: content;\n    content-visibility: auto;\n  \"\u003e\u003c/div\u003e`;\nconst svgDomCache = createElementFromHTMLString(svgDomCacheHtml);\ndocument.body.appendChild(svgDomCache);\nlet reusedDomNodes = 0;\nconst cachedSvgContent = new Set();\ndocument.querySelectorAll('svg').forEach(svg =\u003e {\n    if (svg.children[0].tagName !== 'use') {\n        if (!cachedSvgContent.has(svg.innerHTML)) {\n            nextCachedSvgId++;\n            cachedSvgContent.add(svg.innerHTML);\n            cacheInDom(svg, nextCachedSvgId);\n        }\n        else {\n            reusedDomNodes += svg.querySelectorAll('*').length;\n        }\n        modifySvgToUseCache(svg, nextCachedSvgId);\n    }\n    else {\n        console.info('already optimized');\n    }\n});\nconsole.log('Reused DOM nodes: ', reusedDomNodes);\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Re apply dom](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconst bi = document.body.innerHTML;\ndocument.body.innerHTML = '';\nsetTimeout(() =\u003e document.body.innerHTML = bi, 350);\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Resources hints](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconst rels = [\n    \"preload\",\n    \"prefetch\",\n    \"preconnect\",\n    \"dns-prefetch\",\n    \"preconnect dns-prefetch\",\n    \"prerender\",\n    \"modulepreload\",\n];\nrels.forEach((element) =\u003e {\n    const linkElements = document.querySelectorAll(`link[rel=\"${element}\"]`);\n    const dot = linkElements.length \u003e 0 ? \"🟩\" : \"🟥\";\n    console.log(`${dot} ${element}`);\n    linkElements.forEach((el) =\u003e console.log(el));\n});\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Scripts loading](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconst scripts = document.querySelectorAll('script[src]');\nconst scriptsLoading = [...scripts].map((obj) =\u003e {\n    let newObj = {};\n    newObj = {\n        src: obj.src,\n        async: obj.async,\n        defer: obj.defer,\n        'render blocking': obj.async || obj.defer ? '' : '🟥'\n    };\n    return newObj;\n});\nconsole.table(scriptsLoading);\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Scroll up down](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nconst scrollHeight = document.documentElement.scrollHeight;\nwindow.scroll({\n    top: scrollHeight,\n    behavior: 'smooth'\n});\n// wait for a second, then scroll back up\nsetTimeout(() =\u003e window.scroll({\n    top: 0,\n    behavior: 'smooth'\n}), 3000);\nconsole.log('scroll done!');\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Time to first byte all](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nnew PerformanceObserver((entryList) =\u003e {\n    const entries = entryList.getEntries();\n    const resourcesLoaded = [...entries].map((entry) =\u003e {\n        let obj = {};\n        // Some resources may have a responseStart value of 0, due\n        // to the resource being cached, or a cross-origin resource\n        // being served without a Timing-Allow-Origin header set.\n        if (entry.responseStart \u003e 0) {\n            obj = {\n                'TTFB (ms)': entry.responseStart,\n                Resource: entry.name\n            };\n        }\n        return obj;\n    });\n    console.table(resourcesLoaded);\n}).observe({\n    type: 'resource',\n    buffered: true\n});\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n## [Time to first byte document](https://github.com/push-based/web-performance-tools/tree/main/)\r\n\n\u003cdetails\u003e\r\n\n\u003csummary\u003eCopy this code snippet into the DevTools console Tab to use it\u003c/summary\u003e\r\n\n\n```javascript\r\n\nnew PerformanceObserver((entryList) =\u003e {\n    const [pageNav] = entryList.getEntriesByType('navigation');\n    console.log(`TTFB (ms): ${pageNav.responseStart}`);\n}).observe({\n    type: 'navigation',\n    buffered: true\n});\n\n``` \r\n\n\r\n\r\n\n\u003c/details\u003e\r\n\n\r\n\n\u003c!-- END-SNIPPETS --\u003e\r\n\r\n\r\n\r\n\r\n---\r\n\r\nmade with ❤ by [push-based.io](https://www.push-based.io)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpush-based%2Fawesome-web-performance-snippets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpush-based%2Fawesome-web-performance-snippets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpush-based%2Fawesome-web-performance-snippets/lists"}