{"id":28767,"date":"2026-11-04T18:06:23","date_gmt":"2026-11-04T17:06:23","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/"},"modified":"2026-11-04T19:17:22","modified_gmt":"2026-11-04T18:17:22","slug":"gtm-container-orphan-checker-unused-tags-triggers-variables","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/","title":{"rendered":"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables"},"content":{"rendered":"<div class=\"lw-go\" id=\"go-wurzel\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<style>\n.lw-go label.go-feld { display: flex; flex-direction: column; gap: 6px; font-size: 0.84rem; color: var(--text-secondary, #a0a0b0); margin-bottom: 12px; }\n.lw-go textarea { width: 100%; box-sizing: border-box; padding: 9px 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; font-size: 0.84rem; min-height: 220px; resize: vertical; line-height: 1.45; }\n.lw-go .go-reihe { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 4px 0 6px; }\n.lw-go button { padding: 9px 16px; border-radius: 6px; border: 1px solid var(--border, #2a2a35); background: var(--bg-body, #14141a); color: var(--text-primary, #e8e8ee); font-size: 0.92rem; cursor: pointer; }\n.lw-go button.go-haupt { background: var(--accent, #38bdf8); border-color: var(--accent, #38bdf8); color: var(--on-accent, #0a0e14); font-weight: 700; }\n.lw-go button:focus-visible, .lw-go input:focus-visible, .lw-go textarea:focus-visible, .lw-go summary:focus-visible { outline: 2px solid var(--accent, #38bdf8); outline-offset: 2px; }\n.lw-go .go-datei { font-size: 0.86rem; color: var(--text-secondary, #a0a0b0); max-width: 100%; }\n.lw-go .go-status { min-height: 1.3em; margin: 10px 0 0; font-size: 0.92rem; color: var(--text-secondary, #a0a0b0); }\n.lw-go .go-status.go-schlecht { color: var(--fail, #ff7b72); }\n.lw-go .go-h { font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.98rem; margin: 26px 0 8px; }\n.lw-go .go-tabelle { overflow-x: auto; }\n.lw-go table { border-collapse: collapse; width: 100%; font-size: 0.88rem; }\n.lw-go th, .lw-go td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border, #2a2a35); vertical-align: top; color: var(--text-primary, #e8e8ee); }\n.lw-go th { color: var(--text-secondary, #a0a0b0); font-weight: 600; font-size: 0.8rem; }\n.lw-go td.go-zahl, .lw-go th.go-zahl { text-align: right; font-variant-numeric: tabular-nums; }\n.lw-go .go-behaelter { margin: 0 0 8px; font-size: 0.9rem; color: var(--text-secondary, #a0a0b0); }\n.lw-go ul.go-liste { list-style: none; margin: 0; padding: 0; }\n.lw-go ul.go-liste > li { display: flex; gap: 10px; align-items: flex-start; padding: 10px 0; border-bottom: 1px solid var(--border, #2a2a35); font-size: 0.9rem; line-height: 1.5; color: var(--text-primary, #e8e8ee); }\n.lw-go .go-marke { flex: 0 0 auto; min-width: 74px; text-align: center; font-size: 0.74rem; font-weight: 700; padding: 2px 6px; border-radius: 4px; border: 1px solid currentColor; }\n.lw-go .go-fehler { color: var(--fail, #ff7b72); }\n.lw-go .go-warnung { color: var(--warn, #f0b429); }\n.lw-go .go-hinweis { color: var(--text-secondary, #a0a0b0); }\n.lw-go .go-gut { color: var(--pass, #3fb950); }\n.lw-go .go-inhalt { flex: 1 1 auto; min-width: 0; }\n.lw-go .go-titel { font-weight: 600; }\n.lw-go .go-hilfe { display: block; font-size: 0.84rem; color: var(--text-secondary, #a0a0b0); margin-top: 2px; line-height: 1.55; }\n.lw-go details { margin-top: 6px; }\n.lw-go summary { cursor: pointer; font-size: 0.84rem; color: var(--accent, #38bdf8); }\n.lw-go ul.go-elemente { list-style: none; margin: 6px 0 0; padding: 0; }\n.lw-go ul.go-elemente li { font-size: 0.84rem; padding: 3px 0; border: 0; display: block; word-break: break-word; }\n.lw-go .go-art { color: var(--text-secondary, #a0a0b0); font-size: 0.76rem; margin-right: 6px; }\n.lw-go .go-nr { font-family: monospace; color: var(--text-secondary, #a0a0b0); font-size: 0.78rem; }\n.lw-go .go-mehr { font-size: 0.82rem; color: var(--text-secondary, #a0a0b0); }\n.lw-go .go-klein { font-size: 0.84rem; color: var(--text-secondary, #a0a0b0); line-height: 1.55; margin: 0 0 6px; }\n.lw-go .go-klein a, .lw-go ul.go-links a { color: var(--accent, #38bdf8); }\n.lw-go ul.go-links { margin: 0 0 10px; padding-left: 1.2em; font-size: 0.86rem; line-height: 1.6; color: var(--text-secondary, #a0a0b0); }\n.lw-go .go-gruppe { margin: 0 0 18px; }\n.lw-go .go-gruppe-kopf { font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.92rem; margin: 0 0 4px; padding: 6px 0; border-bottom: 2px solid var(--accent, #38bdf8); }\n.lw-go .go-gruppe-kopf .go-anzahl { color: var(--text-secondary, #a0a0b0); font-weight: 400; }\n.lw-go table.go-loesch td { word-break: break-word; }\n.lw-go table.go-loesch td.go-n { font-weight: 600; width: 45%; }\n.lw-go table.go-loesch td.go-o { color: var(--text-secondary, #a0a0b0); width: 18%; }\n.lw-go table.go-loesch td.go-g { color: var(--text-secondary, #a0a0b0); font-size: 0.84rem; }\n.lw-go .go-nichts { font-size: 0.9rem; color: var(--pass, #3fb950); margin: 0 0 12px; }\n.lw-go details.go-ueberblick { margin-top: 22px; }\n.lw-go details.go-ueberblick summary { font-weight: 700; color: var(--text-primary, #e8e8ee); font-size: 0.95rem; }\n@media (max-width: 480px) {\n    .lw-go table.go-loesch td.go-o { display: none; }\n    .lw-go table.go-loesch th.go-o { display: none; }\n    .lw-go { padding: 16px !important; }\n    .lw-go .go-marke { min-width: 62px; }\n}\n<\/style>\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 18px;\">A container grows by adding and rarely by removing. This check reads a Google Tag Manager export in the browser and lists what is no longer connected to anything: tags without a working trigger, triggers and variables that nothing references, leftover templates and folders, and elements kept alive only by paused tags. Broken references (a trigger ID or a {{variable}} that does not exist) and duplicate IDs are listed as well, because they are the faults that make a tag silently send nothing.<\/p>\n<p>    <label class=\"go-feld\">Container export (paste the JSON or select the file)<br \/>\n        <textarea id=\"go-json\" spellcheck=\"false\" autocomplete=\"off\" data-mp-mask placeholder=\"{&quot;exportFormatVersion&quot;: 2, &quot;containerVersion&quot;: {&hellip;}}\"><\/textarea><br \/>\n    <\/label><\/p>\n<p class=\"go-klein\">In Tag Manager: Admin \u2192 Export Container, for a version or the workspace. The API format of a container version works too.<\/p>\n<div class=\"go-reihe\">\n        <input id=\"go-datei\" class=\"go-datei\" type=\"file\" accept=\".json,.txt,application\/json,text\/plain\">\n    <\/div>\n<div class=\"go-reihe\">\n        <button type=\"button\" id=\"go-los\" class=\"go-haupt\">Check container<\/button><br \/>\n        <button type=\"button\" id=\"go-beispiel\">Example container<\/button><br \/>\n        <button type=\"button\" id=\"go-leeren\">Clear<\/button>\n    <\/div>\n<p id=\"go-status\" class=\"go-status\" data-mp-mask aria-live=\"polite\">\n<div id=\"go-ergebnis\" data-mp-mask hidden>\n<p id=\"go-behaelter\" class=\"go-behaelter\" data-mp-mask>\n<div class=\"go-h\">Deletion list<\/div>\n<p class=\"go-klein\">Everything below can go, grouped as in Tag Manager. Delete in this order: tags, triggers, variables, templates, folders, so that nothing still refers to an element when it is removed. Built-in variables are not deleted but unticked under Variables \u2192 Configure. Entries marked \u201conly used by \u2026\u201d become free once the elements named there are gone.<\/p>\n<div id=\"go-loeschliste\" data-mp-mask><\/div>\n<div class=\"go-reihe\" id=\"go-export\" hidden>\n            <button type=\"button\" id=\"go-kopieren\">Copy deletion list<\/button><br \/>\n            <button type=\"button\" id=\"go-csv\">Download CSV<\/button>\n        <\/div>\n<div id=\"go-weitere-block\">\n<div class=\"go-h\">Errors and notes<\/div>\n<p class=\"go-klein\">These are not deletions but faults to fix: references that point nowhere, duplicate IDs and similar.<\/p>\n<ul id=\"go-befunde\" class=\"go-liste\" data-mp-mask><\/ul>\n<\/p><\/div>\n<details class=\"go-ueberblick\">\n<summary>Overview<\/summary>\n<div class=\"go-tabelle\">\n<table>\n<thead>\n<tr>\n<th>Element<\/th>\n<th class=\"go-zahl\">In the container<\/th>\n<th class=\"go-zahl\">Cleanup candidates<\/th>\n<\/tr>\n<\/thead>\n<tbody id=\"go-zahlen\" data-mp-mask><\/tbody>\n<\/table>\n<\/div>\n<\/details><\/div>\n<div class=\"go-h\">What is checked<\/div>\n<p class=\"go-klein\">Structure: the export format (containerVersion with lists of tags, triggers, variables, folders, built-in variables, templates, zones, clients and transformations), duplicate IDs and duplicate names.<\/p>\n<p class=\"go-klein\">References: firing and blocking triggers, members of trigger groups, setup and cleanup tags by name, every {{variable}} in parameters, filters and consent fields, custom template types (cvt_\u2026) and folder IDs. Names are compared exactly, including case and spaces.<\/p>\n<p class=\"go-klein\">Orphans: a tag is live when it is not paused and has an existing firing trigger or is started by a live tag. Triggers, variables and templates are followed from live tags, zones, clients and transformations. What is not reached is either unused (nothing refers to it) or used only by dead elements (only paused or never-firing elements refer to it).<\/p>\n<p class=\"go-klein\">Order: broken references first, then unused elements, then points worth a look.<\/p>\n<div class=\"go-h\">Limits and privacy<\/div>\n<p class=\"go-klein\">The check works on names and IDs inside the export. It cannot see a name that a script assembles as plain text, an element that another workspace still uses, or a tag that is kept on purpose. A listed element is a candidate, not a verdict: compare with the version history and the preview before deleting. Server containers are read the same way, and references from clients and transformations count as live.<\/p>\n<p class=\"go-klein\">The container JSON is read and analysed in the browser and is sent nowhere. Exports contain tag IDs, measurement IDs and sometimes pixel codes or keys inside custom HTML; for that reason nothing is stored or transmitted here, and the result areas are excluded from session recording.<\/p>\n<div class=\"go-h\">Related<\/div>\n<p class=\"go-klein\">More tools for GTM containers:<\/p>\n<ul class=\"go-links\">\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-duplicates-analyser\/\">GTM Duplicates Analyser<\/a> \u2013 finds tags, triggers and variables that do the same thing twice.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-naming-convention-auditer\/\">GTM Naming Convention Auditer<\/a> \u2013 checks element names against an agreed convention.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-history-deep-search-engine\/\">GTM Container History &amp; Deep Search Engine<\/a> \u2013 finds the version in which an element was added, changed or removed.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-datalayer-variable-path-flattener\/\">GTM DataLayer Variable Path Flattener<\/a> \u2013 turns a nested dataLayer object into the variable paths to create.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/text-code-diff\/\">Text &amp; Code Diff<\/a> \u2013 compares two exports, for example before and after a cleanup.<\/li>\n<\/ul>\n<p class=\"go-klein\">Articles on keeping a container in order:<\/p>\n<ul class=\"go-links\">\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-agreeing-a-gtm-naming-convention-and-enforcing-it-on-an-existing-container\/\">Tutorial: Agreeing a GTM Naming Convention and Enforcing It on an Existing Container<\/a> \u2013 names make orphans easy to spot.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/web-development\/comparing-two-gtm-container-exports-normalising-before-the-diff-and-what-a-line-diff-cannot-show\/\">Comparing Two GTM Container Exports<\/a> \u2013 how to confirm that a cleanup removed only what it should.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-finding-the-container-version-in-which-a-tag-changed\/\">Tutorial: Finding the Container Version in Which a Tag Changed<\/a> \u2013 for the moment a removed element turns out to be needed.<\/li>\n<li><a href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-marketing\/tutorials-en-digital-marketing\/tutorial-using-claude-code-to-prepare-and-review-google-tag-manager-changes\/\">Tutorial: Using Claude Code to Prepare and Review Google Tag Manager Changes<\/a> \u2013 review a cleanup before it is published.<\/li>\n<\/ul>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"A container grows by adding and rarely by removing. This check reads a Google Tag Manager export in the browser and lists what is no longer connected to anything: tags without a working trigger, triggers and variables that nothing references, leftover templates and folders, and elements kept alive only by paused tags. Broken references (a trigger ID or a {{variable}} that does not exist) and duplicate IDs are listed as well, because they are the faults that make a tag silently send nothing.\",\"f_json\":\"Container export (paste the JSON or select the file)\",\"f_hilfe\":\"In Tag Manager: Admin \u2192 Export Container, for a version or the workspace. The API format of a container version works too.\",\"k_pruefen\":\"Check container\",\"k_beispiel\":\"Example container\",\"k_leeren\":\"Clear\",\"k_kopieren\":\"Copy deletion list\",\"k_csv\":\"Download CSV\",\"h_ueberblick\":\"Overview\",\"sp_art\":\"Element\",\"sp_gesamt\":\"In the container\",\"sp_kandidaten\":\"Cleanup candidates\",\"h_befunde\":\"Errors and notes\",\"h_regeln\":\"What is checked\",\"regel_1\":\"Structure: the export format (containerVersion with lists of tags, triggers, variables, folders, built-in variables, templates, zones, clients and transformations), duplicate IDs and duplicate names.\",\"regel_2\":\"References: firing and blocking triggers, members of trigger groups, setup and cleanup tags by name, every {{variable}} in parameters, filters and consent fields, custom template types (cvt_\u2026) and folder IDs. Names are compared exactly, including case and spaces.\",\"regel_3\":\"Orphans: a tag is live when it is not paused and has an existing firing trigger or is started by a live tag. Triggers, variables and templates are followed from live tags, zones, clients and transformations. What is not reached is either unused (nothing refers to it) or used only by dead elements (only paused or never-firing elements refer to it).\",\"regel_4\":\"Order: broken references first, then unused elements, then points worth a look.\",\"h_grenzen\":\"Limits and privacy\",\"grenze\":\"The check works on names and IDs inside the export. It cannot see a name that a script assembles as plain text, an element that another workspace still uses, or a tag that is kept on purpose. A listed element is a candidate, not a verdict: compare with the version history and the preview before deleting. Server containers are read the same way, and references from clients and transformations count as live.\",\"datenschutz\":\"The container JSON is read and analysed in the browser and is sent nowhere. Exports contain tag IDs, measurement IDs and sometimes pixel codes or keys inside custom HTML; for that reason nothing is stored or transmitted here, and the result areas are excluded from session recording.\",\"h_dazu\":\"Related\",\"dazu_werkzeuge\":\"More tools for GTM containers:\",\"dazu_artikel\":\"Articles on keeping a container in order:\",\"w1_titel\":\"GTM Duplicates Analyser\",\"w1_text\":\"finds tags, triggers and variables that do the same thing twice.\",\"w2_titel\":\"GTM Naming Convention Auditer\",\"w2_text\":\"checks element names against an agreed convention.\",\"w3_titel\":\"GTM Container History \\u0026 Deep Search Engine\",\"w3_text\":\"finds the version in which an element was added, changed or removed.\",\"w4_titel\":\"GTM DataLayer Variable Path Flattener\",\"w4_text\":\"turns a nested dataLayer object into the variable paths to create.\",\"w5_titel\":\"Text \\u0026 Code Diff\",\"w5_text\":\"compares two exports, for example before and after a cleanup.\",\"a1_titel\":\"Tutorial: Agreeing a GTM Naming Convention and Enforcing It on an Existing Container\",\"a1_text\":\"names make orphans easy to spot.\",\"a2_titel\":\"Comparing Two GTM Container Exports\",\"a2_text\":\"how to confirm that a cleanup removed only what it should.\",\"a3_titel\":\"Tutorial: Finding the Container Version in Which a Tag Changed\",\"a3_text\":\"for the moment a removed element turns out to be needed.\",\"a4_titel\":\"Tutorial: Using Claude Code to Prepare and Review Google Tag Manager Changes\",\"a4_text\":\"review a cleanup before it is published.\",\"tausendertrenner\":\",\",\"s_position\":\"(line {zeile}, column {spalte})\",\"s_json\":\"This is not valid JSON.\",\"s_kein_container\":\"The JSON does not look like a Tag Manager container: there is no containerVersion and no list of tags, triggers or variables.\",\"s_leer\":\"Nothing to check yet: paste an export or select a file first.\",\"s_zu_gross\":\"The file is larger than 30 MB and was not read.\",\"s_lesen\":\"Reading file \u2026\",\"s_lesefehler\":\"The file could not be read.\",\"s_ok\":\"Checked {n} elements in {ms} ms.\",\"s_beispiel\":\"Example container loaded and checked.\",\"s_kopiert\":\"Cleanup list copied.\",\"s_kopieren_fehl\":\"The browser blocked copying. The CSV download contains the same list.\",\"behaelter_mit_name\":\"Container {name} ({id}): {n} elements read, {k} cleanup candidates, {f} broken references or duplicate IDs.\",\"behaelter_ohne_name\":\"{n} elements read, {k} cleanup candidates, {f} broken references or duplicate IDs.\",\"zeige_elemente\":\"Show elements\",\"ohne_namen\":\"(no name)\",\"weitere\":\"\u2026 and {n} more, all of them are in the CSV.\",\"stufe_fehler\":\"Error\",\"stufe_warnung\":\"Unused\",\"stufe_hinweis\":\"Note\",\"stufe_gut\":\"OK\",\"art_tag\":\"Tags\",\"art_trigger\":\"Triggers\",\"art_variable\":\"Variables\",\"art_builtin\":\"Built-in variables (enabled)\",\"art_vorlage\":\"Custom templates\",\"art_ordner\":\"Folders\",\"art_zone\":\"Zones\",\"art_client\":\"Clients\",\"art_transformation\":\"Transformations\",\"art_tag_eins\":\"Tag\",\"art_trigger_eins\":\"Trigger\",\"art_variable_eins\":\"Variable\",\"art_builtin_eins\":\"Built-in\",\"art_vorlage_eins\":\"Template\",\"art_ordner_eins\":\"Folder\",\"art_zone_eins\":\"Zone\",\"art_client_eins\":\"Client\",\"art_transformation_eins\":\"Transformation\",\"d_pausiert\":\"paused\",\"d_id\":\"ID {x}\",\"d_ordner\":\"folder: {x}\",\"d_id_doppelt\":\"ID {x} is used more than once\",\"d_name_doppelt\":\"{x} elements share this name\",\"d_fehlt_variable\":\"missing: {x}\",\"d_fehlt_trigger\":\"missing trigger ID {x}\",\"d_fehlt_tag\":\"missing tag: {x}\",\"d_fehlt_vorlage\":\"missing template for {x}\",\"d_fehlt_ordner\":\"missing folder ID {x}\",\"d_nur_von\":\"used only by: {x}\",\"csv_art\":\"Kind\",\"csv_name\":\"Name\",\"csv_id\":\"ID\",\"csv_typ\":\"Type\",\"csv_ordner\":\"Folder\",\"csv_hinweis\":\"Note\",\"b_id_doppelt\":\"Duplicate IDs\",\"b_id_doppelt_hilfe\":\"Two elements of the same kind share one ID. A reference by ID can no longer tell them apart. Hand-edited JSON and repeated imports are the usual cause.\",\"b_ref_trigger_fehlt\":\"References to triggers that do not exist\",\"b_ref_trigger_fehlt_hilfe\":\"A tag, trigger group or zone points to a trigger ID that is not in the export, so nothing fires on it. Typical after a trigger was deleted without opening the tags that used it, or after a partial import.\",\"b_ref_variable_fehlt\":\"References to variables that do not exist\",\"b_ref_variable_fehlt_hilfe\":\"A {{variable}} is used, but no variable of that name and no enabled built-in variable exists. Tag Manager resolves it to undefined. The name has to match exactly, including case and spaces.\",\"b_ref_builtin_aus\":\"Built-in variables used but not enabled\",\"b_ref_builtin_aus_hilfe\":\"The name belongs to a built-in variable, but it is not enabled in this container. Enabling it under Variables \u2192 Configure fixes the value; until then it stays undefined.\",\"b_ref_vorlage_fehlt\":\"Custom templates that are missing\",\"b_ref_vorlage_fehlt_hilfe\":\"The element has a template type (cvt_\u2026) whose template is not in the export. Importing a container without its templates does this. The element cannot work until the template is added again.\",\"b_ref_sequenz_fehlt\":\"Tag sequencing points to a missing tag\",\"b_ref_sequenz_fehlt_hilfe\":\"A setup or cleanup tag is named in the sequence of a tag, but no tag of that name exists. The sequence cannot run as configured.\",\"b_zyklus\":\"Variables that refer to each other in a circle\",\"b_zyklus_hilfe\":\"A variable uses a second variable that uses the first one again, or itself. Tag Manager cannot resolve such a chain, so the value is undefined.\",\"b_tag_ohne_ausloeser\":\"Tags that can never fire\",\"b_tag_ohne_ausloeser_hilfe\":\"No existing firing trigger is assigned, and no live tag starts this one as a setup or cleanup tag. It is not paused, so it looks active in the workspace and still sends nothing.\",\"b_trigger_ungenutzt\":\"Triggers that nothing uses\",\"b_trigger_ungenutzt_hilfe\":\"No tag fires on or is blocked by this trigger, and no trigger group or zone includes it. Removing it is usually safe, and an earlier version still holds it.\",\"b_variable_ungenutzt\":\"Variables that nothing uses\",\"b_variable_ungenutzt_hilfe\":\"No tag, trigger, variable, zone or client contains the {{name}} reference. Check before deleting: a name that a script assembles as plain text is not recognised, and another workspace may already use the variable.\",\"b_name_doppelt_var\":\"Variables with the same name\",\"b_name_doppelt_var_hilfe\":\"Two variables carry one name, so the {{reference}} is ambiguous. Which one wins is not something to rely on.\",\"b_ref_ordner_fehlt\":\"Folder assignment points to a missing folder\",\"b_ref_ordner_fehlt_hilfe\":\"The element names a folder that is not in the export. Harmless for firing, but the element sits outside any folder.\",\"b_tag_pausiert\":\"Paused tags\",\"b_tag_pausiert_hilfe\":\"Paused tags do not fire. Triggers and variables that only they use are listed separately. Either remove the tag or note why it stays.\",\"b_trigger_tot\":\"Triggers used only by paused or never-firing tags\",\"b_trigger_tot_hilfe\":\"The only references come from tags that do not fire. The trigger becomes unused as soon as those tags are removed.\",\"b_variable_tot\":\"Variables used only by paused or dead elements\",\"b_variable_tot_hilfe\":\"The only references come from elements that never run. They become unused together with those elements.\",\"b_vorlage_ungenutzt\":\"Custom templates that nothing uses\",\"b_vorlage_ungenutzt_hilfe\":\"No tag, variable or trigger is built from the template. Gallery templates can be added again later.\",\"b_vorlage_tot\":\"Templates used only by dead elements\",\"b_vorlage_tot_hilfe\":\"Only paused or never-firing elements are built from the template.\",\"b_builtin_ungenutzt\":\"Enabled built-in variables that nothing uses\",\"b_builtin_ungenutzt_hilfe\":\"They cost nothing at run time, but the list in the editor and the data in the preview grow. Some are enabled on purpose to be at hand when building a trigger.\",\"b_ordner_leer\":\"Empty folders\",\"b_ordner_leer_hilfe\":\"No tag, trigger or variable sits in the folder.\",\"b_name_doppelt\":\"Tags or triggers with the same name\",\"b_name_doppelt_hilfe\":\"Allowed, but the list and tag sequencing by name become unclear.\",\"b_sauber\":\"No unused or broken elements found.\",\"b_sauber_hilfe\":\"Every tag can fire, and every trigger, variable, template and folder is referenced from something that is live.\",\"h_loeschliste\":\"Deletion list\",\"loesch_hilfe\":\"Everything below can go, grouped as in Tag Manager. Delete in this order: tags, triggers, variables, templates, folders, so that nothing still refers to an element when it is removed. Built-in variables are not deleted but unticked under Variables \u2192 Configure. Entries marked \u201conly used by \u2026\u201d become free once the elements named there are gone.\",\"befunde_hilfe\":\"These are not deletions but faults to fix: references that point nowhere, duplicate IDs and similar.\",\"sp_name\":\"Name\",\"sp_ordner\":\"Folder\",\"sp_grund\":\"Why\",\"loesch_tag\":\"Tags\",\"loesch_trigger\":\"Triggers\",\"loesch_variable\":\"Variables\",\"loesch_vorlage\":\"Templates\",\"loesch_ordner\":\"Folders\",\"loesch_builtin\":\"Built-in variables \u2013 untick\",\"loesch_nichts\":\"Nothing to delete: every tag, trigger, variable, template and folder is in use.\",\"g_tag_ohne_ausloeser\":\"no trigger, never fires\",\"g_tag_pausiert\":\"paused\",\"g_tag_nur_von\":\"only started by: {x}\",\"g_trigger_ungenutzt\":\"no tag uses it\",\"g_trigger_tot\":\"only used by: {x}\",\"g_variable_ungenutzt\":\"not used anywhere\",\"g_variable_tot\":\"only used by: {x}\",\"g_vorlage_ungenutzt\":\"no tag or variable is built from it\",\"g_vorlage_tot\":\"only used by: {x}\",\"g_builtin_ungenutzt\":\"not used anywhere\",\"g_ordner_leer\":\"empty\",\"g_ordner_wird_leer\":\"empty once the entries above are deleted\",\"hinweis_teilexport\":\"Many referenced variables are missing from this file. It looks like a partial export (Export Container with selected items only). For a deletion list, export the whole workspace or version.\",\"hinweis_ohne_id\":\"Some tags, triggers or variables have no ID. Exports from Tag Manager always carry IDs, so this file was probably written by hand or by another tool, and references by ID cannot be checked reliably.\",\"csv_aktion\":\"Action\",\"csv_loeschen\":\"Delete\",\"csv_deaktivieren\":\"Untick\",\"w1_href\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-duplicates-analyser\\\/\",\"w2_href\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-naming-convention-auditer\\\/\",\"w3_href\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-container-history-deep-search-engine\\\/\",\"w4_href\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-datalayer-variable-path-flattener\\\/\",\"w5_href\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/text-code-diff\\\/\"};<\/p>\n<p>    \/\/ KERN-ANFANG\n    var MAX_BYTES = 31457280;      \/\/ 30 MB\n    var MAX_ZEILEN = 400;          \/\/ Elemente je Befund in der Liste<\/p>\n<p>    var ARTEN = ['tag', 'trigger', 'variable', 'builtin', 'vorlage', 'ordner', 'zone', 'client', 'transformation'];\n    \/\/ Arten, die im Container als Liste stehen: Schluessel im Export, Id-Feld\n    var LISTEN = [\n        { art: 'tag', liste: 'tag', id: 'tagId' },\n        { art: 'trigger', liste: 'trigger', id: 'triggerId' },\n        { art: 'variable', liste: 'variable', id: 'variableId' },\n        { art: 'builtin', liste: 'builtInVariable', id: '' },\n        { art: 'vorlage', liste: 'customTemplate', id: 'templateId' },\n        { art: 'ordner', liste: 'folder', id: 'folderId' },\n        { art: 'zone', liste: 'zone', id: 'zoneId' },\n        { art: 'client', liste: 'client', id: 'clientId' },\n        { art: 'transformation', liste: 'transformation', id: 'transformationId' }\n    ];\n    \/\/ Eingebaute Variablen: Typ im Export -> Name in der Oberflaeche (nur sichere Zuordnungen)\n    var BUILTIN_NAMEN = {\n        CLICK_ELEMENT: 'Click Element', CLICK_CLASSES: 'Click Classes', CLICK_ID: 'Click ID', CLICK_TARGET: 'Click Target', CLICK_URL: 'Click URL',\n        CLICK_TEXT: 'Click Text', ERROR_MESSAGE: 'Error Message', ERROR_URL: 'Error URL', ERROR_LINE: 'Error Line', DEBUG_MODE: 'Debug Mode',\n        FORM_ELEMENT: 'Form Element', FORM_CLASSES: 'Form Classes', FORM_ID: 'Form ID', FORM_TARGET: 'Form Target', FORM_URL: 'Form URL', FORM_TEXT: 'Form Text',\n        HISTORY_SOURCE: 'History Source', NEW_HISTORY_FRAGMENT: 'New History Fragment', OLD_HISTORY_FRAGMENT: 'Old History Fragment',\n        NEW_HISTORY_STATE: 'New History State', OLD_HISTORY_STATE: 'Old History State', PAGE_URL: 'Page URL', PAGE_HOSTNAME: 'Page Hostname',\n        PAGE_PATH: 'Page Path', REFERRER: 'Referrer', SCROLL_DEPTH_THRESHOLD: 'Scroll Depth Threshold', SCROLL_DEPTH_UNITS: 'Scroll Depth Units',\n        SCROLL_DEPTH_DIRECTION: 'Scroll Direction', CONTAINER_ID: 'Container ID', CONTAINER_VERSION: 'Container Version', ENVIRONMENT_NAME: 'Environment Name',\n        EVENT: 'Event', HTML_ID: 'HTML ID', RANDOM_NUMBER: 'Random Number', VIDEO_CURRENT_TIME: 'Video Current Time', VIDEO_DURATION: 'Video Duration',\n        VIDEO_PERCENT: 'Video Percent', VIDEO_PROVIDER: 'Video Provider', VIDEO_STATUS: 'Video Status', VIDEO_TITLE: 'Video Title', VIDEO_URL: 'Video URL',\n        VIDEO_VISIBLE: 'Video Visible'\n    };\n    \/\/ Eingebaute Trigger: feste IDs, die im Export nie als Trigger erscheinen\n    var BUILTIN_TRIGGER = { '2147479553': 'All Pages', '2147479572': 'Consent Initialization - All Pages', '2147479573': 'Initialization - All Pages',\n        '2147479574': 'built-in (server container)' };\n    var BUILTIN_BEKANNT = {};\n    Object.keys(BUILTIN_NAMEN).forEach(function (k) { BUILTIN_BEKANNT[BUILTIN_NAMEN[k]] = true; });<\/p>\n<p>    \/\/ Schluessel, die nie nach Verweisen durchsucht werden (Namen, Notizen, Kennungen)\n    var SPERRE = { name: 1, notes: 1, fingerprint: 1, tagManagerUrl: 1, path: 1, accountId: 1, containerId: 1, containerVersionId: 1, workspaceId: 1,\n        tagId: 1, triggerId: 1, variableId: 1, folderId: 1, templateId: 1, zoneId: 1, clientId: 1, transformationId: 1, parentFolderId: 1,\n        firingTriggerId: 1, blockingTriggerId: 1, customEvaluationTriggerId: 1, setupTag: 1, teardownTag: 1 };<\/p>\n<p>    var ORDNUNG = ['id_doppelt', 'ref_trigger_fehlt', 'ref_variable_fehlt', 'ref_builtin_aus', 'ref_vorlage_fehlt', 'ref_sequenz_fehlt', 'zyklus',\n        'tag_ohne_ausloeser', 'trigger_ungenutzt', 'variable_ungenutzt', 'name_doppelt_var',\n        'ref_ordner_fehlt', 'tag_pausiert', 'trigger_tot', 'variable_tot', 'vorlage_ungenutzt', 'vorlage_tot', 'builtin_ungenutzt', 'ordner_leer', 'name_doppelt'];\n    var STUFE = { id_doppelt: 'fehler', ref_trigger_fehlt: 'fehler', ref_variable_fehlt: 'fehler', ref_builtin_aus: 'fehler', ref_vorlage_fehlt: 'fehler',\n        ref_sequenz_fehlt: 'fehler', zyklus: 'fehler', tag_ohne_ausloeser: 'warnung', trigger_ungenutzt: 'warnung', variable_ungenutzt: 'warnung',\n        name_doppelt_var: 'warnung', ref_ordner_fehlt: 'hinweis', tag_pausiert: 'hinweis', trigger_tot: 'hinweis', variable_tot: 'hinweis',\n        vorlage_ungenutzt: 'hinweis', vorlage_tot: 'hinweis', builtin_ungenutzt: 'hinweis', ordner_leer: 'hinweis', name_doppelt: 'hinweis' };\n    \/\/ Befunde, deren Elemente als Aufraeumkandidaten zaehlen\n    var KANDIDAT = { tag_ohne_ausloeser: 1, tag_pausiert: 1, trigger_ungenutzt: 1, trigger_tot: 1, variable_ungenutzt: 1, variable_tot: 1,\n        vorlage_ungenutzt: 1, vorlage_tot: 1, builtin_ungenutzt: 1, ordner_leer: 1 };<\/p>\n<p>    function s(x) { return x === undefined || x === null ? '' : String(x); }\n    function fmt(vorlage, werte) {\n        return String(vorlage).replace(\/\\{(\\w+)\\}\/g, function (alles, k) { return Object.prototype.hasOwnProperty.call(werte, k) ? werte[k] : alles; });\n    }\n    function zahl(n) { return String(n).replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, T.tausendertrenner || ','); }<\/p>\n<p>    \/\/ --- Verweise aus einem Element einsammeln --------------------------------\n    function sammeln(x, r, tiefe) {\n        if (tiefe > 40 || x === null || x === undefined) { return; }\n        if (typeof x === 'string') {\n            var re = \/\\{\\{([^{}]+?)\\}\\}\/g, m;\n            while ((m = re.exec(x)) !== null) { r.vars.push(m[1]); }\n            return;\n        }\n        if (Array.isArray(x)) { for (var i = 0; i < x.length; i++) { sammeln(x[i], r, tiefe + 1); } return; }\n        if (typeof x !== 'object') { return; }\n        if (x.type === 'TRIGGER_REFERENCE' &#038;&#038; x.value !== undefined) { r.trig.push(s(x.value)); }\n        if (x.type === 'TAG_REFERENCE' &#038;&#038; x.value !== undefined) { r.tags.push(s(x.value)); }\n        for (var k in x) {\n            if (!Object.prototype.hasOwnProperty.call(x, k) || SPERRE[k] === 1) { continue; }\n            sammeln(x[k], r, tiefe + 1);\n        }\n    }\n    function verweise(roh, art) {\n        var r = { vars: [], trig: [], fire: [], tags: [], seq: [], tmpl: '' };\n        if (art === 'vorlage' || art === 'ordner') { return r; }\n        sammeln(roh, r, 0);\n        ['firingTriggerId', 'blockingTriggerId', 'customEvaluationTriggerId'].forEach(function (k) {\n            var a = roh[k];\n            if (!a &#038;&#038; roh.boundary &#038;&#038; k === 'customEvaluationTriggerId') { a = roh.boundary.customEvaluationTriggerId; }\n            if (a !== undefined &#038;&#038; a !== null &#038;&#038; !Array.isArray(a)) { a = [a]; }\n            (a || []).forEach(function (id) { r.trig.push(s(id)); if (k === 'firingTriggerId') { r.fire.push(s(id)); } });\n        });\n        ['setupTag', 'teardownTag'].forEach(function (k) {\n            (Array.isArray(roh[k]) ? roh[k] : []).forEach(function (t) { if (t &#038;&#038; t.tagName !== undefined) { r.seq.push(s(t.tagName)); } });\n        });\n        \/\/ Vorlagentyp: cvt_<Container-ID>_<templateId> fuer eigene, cvt_<Galerie-ID> fuer Galerievorlagen\n        if (\/^cvt_.\/.test(s(roh.type))) { r.tmpl = s(roh.type).slice(4); }\n        return r;\n    }<\/p>\n<p>    \/\/ --- Container finden ------------------------------------------------------\n    function holeContainer(wurzel) {\n        if (!wurzel || typeof wurzel !== 'object' || Array.isArray(wurzel)) { return null; }\n        if (wurzel.containerVersion && typeof wurzel.containerVersion === 'object' && !Array.isArray(wurzel.containerVersion)) { return wurzel.containerVersion; }\n        for (var i = 0; i < LISTEN.length; i++) { if (Array.isArray(wurzel[LISTEN[i].liste])) { return wurzel; } }\n        return null;\n    }\n\n    \/\/ --- Analyse ----------------------------------------------------------------\n    function analysiere(cv) {\n        var el = [], idKarte = {}, nameKarte = { tag: {}, variable: {} }, builtinKarte = {}, galerieKarte = {};\n        ARTEN.forEach(function (a) { idKarte[a] = {}; });\n        var fund = {};\n        function melde(key, e, info) {\n            if (!fund[key]) { fund[key] = []; }\n            fund[key].push({ e: e, info: info || null });\n        }\n\n        \/\/ 1. Elemente anlegen\n        LISTEN.forEach(function (L) {\n            var liste = Array.isArray(cv[L.liste]) ? cv[L.liste] : [];\n            liste.forEach(function (roh) {\n                if (!roh || typeof roh !== 'object') { return; }\n                var e = { n: el.length, art: L.art, roh: roh, id: L.id ? s(roh[L.id]) : '', name: s(roh.name), typ: s(roh.type), ordner: s(roh.parentFolderId),\n                    ein: [], aus: [], pausiert: roh.paused === true };\n                if (L.art === 'builtin') { if (!e.name) { e.name = BUILTIN_NAMEN[e.typ] || e.typ; } }\n                e.ref = verweise(roh, L.art);\n                el.push(e);\n                if (e.id !== '') { (idKarte[L.art][e.id] = idKarte[L.art][e.id] || []).push(e); }\n                if (L.art === 'tag' || L.art === 'variable') { (nameKarte[L.art][e.name] = nameKarte[L.art][e.name] || []).push(e); }\n                if (L.art === 'builtin') { builtinKarte[e.name] = e; }\n                if (L.art === 'vorlage' &#038;&#038; roh.galleryReference &#038;&#038; roh.galleryReference.galleryTemplateId) {\n                    (galerieKarte[s(roh.galleryReference.galleryTemplateId)] = galerieKarte[s(roh.galleryReference.galleryTemplateId)] || []).push(e);\n                }\n            });\n        });\n\n        \/\/ 2. Doppelte Kennungen und Namen\n        ARTEN.forEach(function (a) {\n            Object.keys(idKarte[a]).forEach(function (id) {\n                if (idKarte[a][id].length > 1) { idKarte[a][id].forEach(function (e) { melde('id_doppelt', e, { k: 'd_id_doppelt', x: id }); }); }\n            });\n        });\n        ['tag', 'trigger'].forEach(function (a) {\n            var karte = {};\n            el.forEach(function (e) { if (e.art === a && e.name !== '') { (karte[e.name] = karte[e.name] || []).push(e); } });\n            Object.keys(karte).forEach(function (nm) { if (karte[nm].length > 1) { karte[nm].forEach(function (e) { melde('name_doppelt', e, { k: 'd_name_doppelt', x: String(karte[nm].length) }); }); } });\n        });\n        Object.keys(nameKarte.variable).forEach(function (nm) {\n            var l = nameKarte.variable[nm];\n            if (l.length > 1 && nm !== '') { l.forEach(function (e) { melde('name_doppelt_var', e, { k: 'd_name_doppelt', x: String(l.length) }); }); }\n        });<\/p>\n<p>        \/\/ 3. Kanten aufbauen, defekte Verweise melden\n        var hatOrdner = Array.isArray(cv.folder);\n        function kante(von, ziel, art) {\n            if (ziel === von) { return; }\n            von.aus.push({ t: ziel, k: art });\n            ziel.ein.push({ s: von, k: art });\n        }\n        el.forEach(function (e) {\n            var r = e.ref, seen;\n            \/\/ Variablen\n            seen = {};\n            r.vars.forEach(function (nm) {\n                if (seen[nm]) { return; }\n                seen[nm] = true;\n                var ziele = (nameKarte.variable[nm] || []).slice();\n                if (builtinKarte[nm]) { ziele.push(builtinKarte[nm]); }\n                \/\/ {{_event}} ist der feste Verweis auf die eingebaute Variable \"Event\" und braucht keine eigene Variable\n                if (nm === '_event') { if (builtinKarte.Event) { ziele.push(builtinKarte.Event); } else { return; } }\n                if (!ziele.length) {\n                    melde(BUILTIN_BEKANNT[nm] ? 'ref_builtin_aus' : 'ref_variable_fehlt', e, { k: 'd_fehlt_variable', x: nm });\n                } else { ziele.forEach(function (z) { kante(e, z, 'v'); }); }\n            });\n            \/\/ Ausloeser\n            seen = {};\n            r.trig.forEach(function (id) {\n                if (seen[id]) { return; }\n                seen[id] = true;\n                var ziele = idKarte.trigger[id];\n                if (!ziele && BUILTIN_TRIGGER[id]) { return; }   \/\/ eingebauter Trigger, steht nie im Export\n                if (!ziele) { melde('ref_trigger_fehlt', e, { k: 'd_fehlt_trigger', x: id }); } else { ziele.forEach(function (z) { kante(e, z, 't'); }); }\n            });\n            \/\/ Tag-Verweise (Parameter) und Reihenfolge\n            seen = {};\n            r.tags.forEach(function (nm) {\n                if (seen[nm]) { return; }\n                seen[nm] = true;\n                (nameKarte.tag[nm] || idKarte.tag[nm] || []).forEach(function (z) { kante(e, z, 'r'); });\n            });\n            seen = {};\n            r.seq.forEach(function (nm) {\n                if (seen[nm]) { return; }\n                seen[nm] = true;\n                var ziele = nameKarte.tag[nm];\n                if (!ziele) { melde('ref_sequenz_fehlt', e, { k: 'd_fehlt_tag', x: nm }); } else { ziele.forEach(function (z) { kante(e, z, 'q'); }); }\n            });\n            \/\/ Vorlage\n            if (r.tmpl) {\n                var mv = \/_(\\d+)$\/.exec(r.tmpl);\n                var vt = (mv && idKarte.vorlage[mv[1]]) || galerieKarte[r.tmpl];\n                if (!vt) { melde('ref_vorlage_fehlt', e, { k: 'd_fehlt_vorlage', x: e.typ }); } else { vt.forEach(function (z) { kante(e, z, 'p'); }); }\n            }\n            \/\/ Ordner\n            if (hatOrdner && e.ordner !== '' && e.ordner !== '0' && !idKarte.ordner[e.ordner] && e.art !== 'ordner') {\n                melde('ref_ordner_fehlt', e, { k: 'd_fehlt_ordner', x: e.ordner });\n            }\n        });<\/p>\n<p>        \/\/ 4. Zyklen unter Variablen\n        var farbe = {}, inZyklus = {};\n        function tiefensuche(e, pfad) {\n            farbe[e.n] = 1; pfad.push(e);\n            e.aus.forEach(function (ka) {\n                var z = ka.t;\n                if (ka.k !== 'v' || z.art !== 'variable') { return; }\n                if (farbe[z.n] === 1) {\n                    for (var i = pfad.length - 1; i >= 0; i--) { inZyklus[pfad[i].n] = true; if (pfad[i] === z) { break; } }\n                } else if (!farbe[z.n]) { tiefensuche(z, pfad); }\n            });\n            pfad.pop(); farbe[e.n] = 2;\n        }\n        el.forEach(function (e) { if (e.art === 'variable' && !farbe[e.n]) { tiefensuche(e, []); } });\n        el.forEach(function (e) {\n            if (e.art !== 'variable') { return; }\n            if (inZyklus[e.n]) { melde('zyklus', e, null); return; }\n            e.aus.forEach(function (ka) { if (ka.k === 'v' && ka.t === e) { melde('zyklus', e, null); } });\n        });\n        \/\/ Selbstverweis: kante() legt keine Kante auf sich selbst an, daher gesondert\n        el.forEach(function (e) {\n            if (e.art === 'variable' && e.ref.vars.indexOf(e.name) !== -1 && !inZyklus[e.n]) {\n                var schon = (fund.zyklus || []).some(function (f) { return f.e === e; });\n                if (!schon) { melde('zyklus', e, null); }\n            }\n        });<\/p>\n<p>        \/\/ 5. Tags: wann feuern sie wirklich?\n        var live = {};\n        function vorhandeneAusloeser(e) { var n = 0; e.ref.fire.forEach(function (id) { if (idKarte.trigger[id] || BUILTIN_TRIGGER[id]) { n++; } }); return n; }\n        var geaendert = true;\n        while (geaendert) {\n            geaendert = false;\n            el.forEach(function (e) {\n                if (e.art !== 'tag' || live[e.n] || e.pausiert) { return; }\n                var ok = vorhandeneAusloeser(e) > 0;\n                if (!ok) {\n                    ok = e.ein.some(function (ka) { return ka.s.art === 'tag' && live[ka.s.n] && (ka.k === 'q' || ka.k === 'r'); });\n                }\n                if (ok) { live[e.n] = true; geaendert = true; }\n            });\n        }<\/p>\n<p>        \/\/ 6. Von lebenden Wurzeln aus verfolgen\n        var erreicht = {}, stapel = [];\n        el.forEach(function (e) {\n            if ((e.art === 'tag' && live[e.n]) || e.art === 'zone' || e.art === 'client' || e.art === 'transformation') { erreicht[e.n] = true; stapel.push(e); }\n        });\n        while (stapel.length) {\n            var a = stapel.pop();\n            a.aus.forEach(function (ka) {\n                if (ka.t.art === 'tag' || erreicht[ka.t.n]) { return; }\n                erreicht[ka.t.n] = true; stapel.push(ka.t);\n            });\n        }<\/p>\n<p>        \/\/ 7. Aufraeumkandidaten einordnen\n        function referrer(e) {\n            var namen = [], gesehen = {};\n            e.ein.forEach(function (ka) {\n                var nm = ka.s.name || ka.s.id;\n                if (!gesehen[ka.s.n]) { gesehen[ka.s.n] = true; namen.push(nm + (ka.s.pausiert ? ' (' + T.d_pausiert + ')' : '')); }\n            });\n            var rest = namen.length > 3 ? ' +' + (namen.length - 3) : '';\n            return namen.slice(0, 3).join(', ') + rest;\n        }\n        var benutzteOrdner = {};\n        el.forEach(function (e) { if (e.ordner !== '' && e.art !== 'ordner') { benutzteOrdner[e.ordner] = true; } });\n        el.forEach(function (e) {\n            if (e.art === 'tag') {\n                if (e.pausiert) { melde('tag_pausiert', e, null); }\n                else if (!live[e.n]) { melde('tag_ohne_ausloeser', e, e.ein.length ? { k: 'd_nur_von', x: referrer(e) } : null); }\n            } else if (e.art === 'trigger' || e.art === 'variable' || e.art === 'vorlage' || e.art === 'builtin') {\n                if (erreicht[e.n]) { return; }\n                var wohin = e.art === 'builtin' ? 'variable' : e.art;\n                if (e.ein.length === 0) {\n                    melde(e.art === 'builtin' ? 'builtin_ungenutzt' : wohin + '_ungenutzt', e, null);\n                } else {\n                    melde(wohin + '_tot', e, { k: 'd_nur_von', x: referrer(e) });\n                }\n            }\n        });\n        \/\/ Ordner zuletzt: leer, oder leer, sobald alle Elemente darin von der Loeschliste weg sind\n        var weg = {};\n        Object.keys(fund).forEach(function (key) { if (KANDIDAT[key]) { fund[key].forEach(function (f) { weg[f.e.n] = true; }); } });\n        var bleibt = {};\n        el.forEach(function (e) { if (e.ordner !== '' && e.art !== 'ordner' && !weg[e.n]) { bleibt[e.ordner] = true; } });\n        el.forEach(function (e) {\n            if (e.art !== 'ordner') { return; }\n            if (!benutzteOrdner[e.id]) { melde('ordner_leer', e, null); }\n            else if (!bleibt[e.id]) { melde('ordner_leer', e, { k: 'g_ordner_wird_leer', x: '' }); }\n        });<\/p>\n<p>        \/\/ 8. Ergebnis zusammenstellen\n        var befunde = [];\n        ORDNUNG.forEach(function (key) {\n            if (fund[key] && fund[key].length) { befunde.push({ key: key, stufe: STUFE[key], eintraege: fund[key] }); }\n        });\n        var kand = {}, zaehl = {};\n        ARTEN.forEach(function (a) { zaehl[a] = { gesamt: 0, kandidaten: 0 }; });\n        el.forEach(function (e) { zaehl[e.art].gesamt++; });\n        befunde.forEach(function (b) {\n            if (!KANDIDAT[b.key]) { return; }\n            b.eintraege.forEach(function (f) {\n                if (!kand[f.e.n]) { kand[f.e.n] = true; zaehl[f.e.art].kandidaten++; }\n            });\n        });\n        var fehler = 0, kandidaten = 0;\n        befunde.forEach(function (b) { if (b.stufe === 'fehler') { fehler += b.eintraege.length; } });\n        Object.keys(kand).forEach(function () { kandidaten++; });\n        var ordnerName = {};\n        el.forEach(function (e) { if (e.art === 'ordner') { ordnerName[e.id] = e.name; } });\n        var cont = (cv.container && typeof cv.container === 'object') ? cv.container : {};\n        \/\/ Teilexport: der Tag Manager kann auch nur ausgewaehlte Elemente exportieren, dann fehlen viele Variablen\n        var fehlendeNamen = {};\n        (fund.ref_variable_fehlt || []).forEach(function (f) { fehlendeNamen[f.info.x] = true; });\n        var nFehlend = Object.keys(fehlendeNamen).length;\n        var teilexport = nFehlend >= 10 && nFehlend > zaehl.variable.gesamt * 0.5;\n        \/\/ Elemente ohne ID kommen in Exporten aus dem Tag Manager nicht vor (Datei von Hand oder per Werkzeug erzeugt)\n        var ohneId = el.some(function (e) { return (e.art === 'tag' || e.art === 'trigger' || e.art === 'variable') && e.id === ''; });\n        return { befunde: befunde, zahlen: zaehl, kandidaten: kandidaten, fehler: fehler, elemente: el.length, ordnerName: ordnerName, teilexport: teilexport, ohneId: ohneId,\n            container: { name: s(cont.name), oeffentlich: s(cont.publicId) } };\n    }<\/p>\n<p>    \/\/ --- Text lesen ----------------------------------------------------------------\n    function textLesen(text) {\n        var t = String(text);\n        if (t.charCodeAt(0) === 0xFEFF) { t = t.slice(1); }\n        var wurzel;\n        try { wurzel = JSON.parse(t); }\n        catch (fehler) {\n            var pos = \/position (\\d+)\/.exec(String(fehler && fehler.message));\n            var info = '';\n            if (pos) {\n                var p = parseInt(pos[1], 10), zeile = 1, letzter = -1, idx = -1;\n                while ((idx = t.indexOf('\\n', idx + 1)) !== -1 && idx < p) { zeile++; letzter = idx; }\n                info = fmt(T.s_position, { zeile: zeile, spalte: p - letzter });\n            }\n            return { fehler: 'json', info: info };\n        }\n        var cv = holeContainer(wurzel);\n        if (!cv) { return { fehler: 'container' }; }\n        return { cv: cv };\n    }\n\n    \/\/ --- Ausgabe -----------------------------------------------------------------------\n    function el_(id) { return document.getElementById(id); }\n    function knoten(tag, klasse, text) {\n        var n = document.createElement(tag);\n        if (klasse) { n.className = klasse; }\n        if (text !== undefined) { n.textContent = text; }\n        return n;\n    }\n    function status(text, schlecht) {\n        var n = el_('go-status');\n        if (!n) { return; }\n        n.textContent = text;\n        n.className = 'go-status' + (schlecht ? ' go-schlecht' : '');\n    }\n    function detailText(info, ordnerName, e) {\n        var teile = [];\n        if (e.id !== '') { teile.push(fmt(T.d_id, { x: e.id })); }\n        if (e.typ) { teile.push(e.typ); }\n        if (e.ordner !== '' &#038;&#038; ordnerName[e.ordner]) { teile.push(fmt(T.d_ordner, { x: ordnerName[e.ordner] })); }\n        if (info) { teile.push(fmt(T[info.k], { x: info.x })); }\n        return teile.join(' \u00b7 ');\n    }\n    var letztes = null;\n\n    function zeigen(res) {\n        letztes = res;\n        var c = res.container;\n        el_('go-behaelter').textContent = fmt(c.name ? T.behaelter_mit_name : T.behaelter_ohne_name,\n            { name: c.name, id: c.oeffentlich, n: zahl(res.elemente), k: zahl(res.kandidaten), f: zahl(res.fehler) });\n        if (res.teilexport) {\n            el_('go-behaelter').appendChild(document.createElement('br'));\n            el_('go-behaelter').appendChild(knoten('strong', 'go-warnung', T.hinweis_teilexport));\n        }\n        if (res.ohneId) {\n            el_('go-behaelter').appendChild(document.createElement('br'));\n            el_('go-behaelter').appendChild(knoten('strong', 'go-warnung', T.hinweis_ohne_id));\n        }\n\n        var tb = el_('go-zahlen');\n        tb.textContent = '';\n        ARTEN.forEach(function (a) {\n            var z = res.zahlen[a];\n            if (!z.gesamt) { return; }\n            var tr = document.createElement('tr');\n            tr.appendChild(knoten('td', '', T['art_' + a]));\n            tr.appendChild(knoten('td', 'go-zahl', zahl(z.gesamt)));\n            tr.appendChild(knoten('td', 'go-zahl', zahl(z.kandidaten)));\n            tb.appendChild(tr);\n        });\n\n        \/\/ Loeschliste: je Art eine Tabelle mit Name, Ordner, Grund\n        var lw = el_('go-loeschliste');\n        lw.textContent = '';\n        var gruppen = loeschGruppen(res), gesamt = 0;\n        LOESCH_ARTEN.forEach(function (a) {\n            var liste = gruppen[a];\n            if (!liste.length) { return; }\n            gesamt += liste.length;\n            var g = knoten('div', 'go-gruppe');\n            g.setAttribute('data-art', a);\n            var kopf = knoten('div', 'go-gruppe-kopf', T['loesch_' + a] + ' ');\n            kopf.appendChild(knoten('span', 'go-anzahl', '(' + zahl(liste.length) + ')'));\n            g.appendChild(kopf);\n            var wrap = knoten('div', 'go-tabelle');\n            var tab = knoten('table', 'go-loesch');\n            var th = document.createElement('thead'), trh = document.createElement('tr');\n            trh.appendChild(knoten('th', '', T.sp_name));\n            trh.appendChild(knoten('th', 'go-o', T.sp_ordner));\n            trh.appendChild(knoten('th', '', T.sp_grund));\n            th.appendChild(trh); tab.appendChild(th);\n            var tb2 = document.createElement('tbody');\n            liste.slice(0, MAX_ZEILEN).forEach(function (f) {\n                var tr = document.createElement('tr');\n                tr.appendChild(knoten('td', 'go-n', f.e.name || T.ohne_namen));\n                tr.appendChild(knoten('td', 'go-o', res.ordnerName[f.e.ordner] || '\u2013'));\n                tr.appendChild(knoten('td', 'go-g', grundText(f)));\n                tb2.appendChild(tr);\n            });\n            if (liste.length > MAX_ZEILEN) {\n                var trm = document.createElement('tr'), tdm = knoten('td', 'go-mehr', fmt(T.weitere, { n: zahl(liste.length - MAX_ZEILEN) }));\n                tdm.colSpan = 3; trm.appendChild(tdm); tb2.appendChild(trm);\n            }\n            tab.appendChild(tb2); wrap.appendChild(tab); g.appendChild(wrap);\n            lw.appendChild(g);\n        });\n        if (!gesamt) { lw.appendChild(knoten('p', 'go-nichts', T.loesch_nichts)); }<\/p>\n<p>        \/\/ Fehler und Hinweise, die kein Loeschen bedeuten\n        var weitere = res.befunde.filter(function (b) { return !KANDIDAT[b.key]; });\n        el_('go-weitere-block').hidden = !weitere.length;\n        var ul = el_('go-befunde');\n        ul.textContent = '';\n        weitere.forEach(function (b) {\n            var li = document.createElement('li');\n            li.setAttribute('data-befund', b.key);\n            li.appendChild(knoten('span', 'go-marke go-' + b.stufe, T['stufe_' + b.stufe]));\n            var inh = knoten('div', 'go-inhalt');\n            inh.appendChild(knoten('span', 'go-titel', T['b_' + b.key] + ' (' + zahl(b.eintraege.length) + ')'));\n            inh.appendChild(knoten('span', 'go-hilfe', T['b_' + b.key + '_hilfe']));\n            var d = document.createElement('details');\n            d.appendChild(knoten('summary', '', T.zeige_elemente));\n            var liste = knoten('ul', 'go-elemente');\n            b.eintraege.slice(0, MAX_ZEILEN).forEach(function (f) {\n                var z = document.createElement('li');\n                z.appendChild(knoten('span', 'go-art', T['art_' + f.e.art + '_eins']));\n                z.appendChild(document.createTextNode((f.e.name || T.ohne_namen) + ' '));\n                z.appendChild(knoten('span', 'go-nr', detailText(f.info, res.ordnerName, f.e)));\n                liste.appendChild(z);\n            });\n            if (b.eintraege.length > MAX_ZEILEN) {\n                liste.appendChild(knoten('li', 'go-mehr', fmt(T.weitere, { n: zahl(b.eintraege.length - MAX_ZEILEN) })));\n            }\n            d.appendChild(liste);\n            inh.appendChild(d);\n            li.appendChild(inh);\n            ul.appendChild(li);\n        });\n        el_('go-export').hidden = !res.befunde.length;\n        el_('go-ergebnis').hidden = false;\n    }<\/p>\n<p>    \/\/ Reihenfolge, in der im Tag Manager geloescht werden kann (zuerst, was auf anderes verweist)\n    var LOESCH_ARTEN = ['tag', 'trigger', 'variable', 'vorlage', 'ordner', 'builtin'];\n    var GRUND_RANG = { tag_ohne_ausloeser: 0, trigger_ungenutzt: 0, variable_ungenutzt: 0, vorlage_ungenutzt: 0, builtin_ungenutzt: 0, ordner_leer: 0,\n        tag_pausiert: 1, trigger_tot: 1, variable_tot: 1, vorlage_tot: 1 };\n    function loeschGruppen(res) {\n        var g = {}, gesehen = {};\n        LOESCH_ARTEN.forEach(function (a) { g[a] = []; });\n        res.befunde.forEach(function (b) {\n            if (!KANDIDAT[b.key]) { return; }\n            b.eintraege.forEach(function (f) {\n                if (gesehen[f.e.n] || !g[f.e.art]) { return; }\n                gesehen[f.e.n] = true;\n                g[f.e.art].push({ e: f.e, key: b.key, info: f.info });\n            });\n        });\n        LOESCH_ARTEN.forEach(function (a) {\n            g[a].sort(function (x, y) {\n                return (GRUND_RANG[x.key] - GRUND_RANG[y.key]) || x.e.name.localeCompare(y.e.name);\n            });\n        });\n        return g;\n    }\n    function grundText(f) {\n        if (f.key === 'tag_ohne_ausloeser' && f.info) { return fmt(T.g_tag_nur_von, { x: f.info.x }); }\n        if (f.key === 'ordner_leer' && f.info) { return T.g_ordner_wird_leer; }\n        return fmt(T['g_' + f.key], { x: f.info ? f.info.x : '' });\n    }<\/p>\n<p>    \/\/ --- Export ---------------------------------------------------------------------------\n    function csvZelle(x) {\n        var t = s(x);\n        if (\/^[=+\\-@\\t\\r]\/.test(t)) { t = \"'\" + t; }\n        return '\"' + t.replace(\/\"\/g, '\"\"') + '\"';\n    }\n    function csvBauen(res) {\n        var zeilen = [[T.csv_aktion, T.csv_art, T.csv_name, T.csv_ordner, T.csv_hinweis, T.csv_id, T.csv_typ].map(csvZelle).join(',')];\n        var gr = loeschGruppen(res);\n        LOESCH_ARTEN.forEach(function (a) {\n            gr[a].forEach(function (f) {\n                zeilen.push([a === 'builtin' ? T.csv_deaktivieren : T.csv_loeschen, T['art_' + a + '_eins'], f.e.name, res.ordnerName[f.e.ordner] || '',\n                    grundText(f), f.e.id, f.e.typ].map(csvZelle).join(','));\n            });\n        });\n        res.befunde.forEach(function (b) {\n            if (KANDIDAT[b.key]) { return; }\n            b.eintraege.forEach(function (f) {\n                zeilen.push([T['stufe_' + b.stufe], T['art_' + f.e.art + '_eins'], f.e.name, res.ordnerName[f.e.ordner] || '',\n                    T['b_' + b.key] + (f.info ? ': ' + fmt(T[f.info.k], { x: f.info.x }) : ''), f.e.id, f.e.typ].map(csvZelle).join(','));\n            });\n        });\n        return '\ufeff' + zeilen.join('\\r\\n') + '\\r\\n';\n    }\n    function listeBauen(res) {\n        var aus = [], gr = loeschGruppen(res);\n        LOESCH_ARTEN.forEach(function (a) {\n            if (!gr[a].length) { return; }\n            aus.push(T['loesch_' + a] + ' (' + gr[a].length + ')');\n            gr[a].forEach(function (f) { aus.push('- ' + (f.e.name || T.ohne_namen) + '  (' + grundText(f) + ')'); });\n            aus.push('');\n        });\n        return aus.join('\\n');\n    }\n    function kopieren() {\n        if (!letztes) { return; }\n        var text = listeBauen(letztes);\n        function geschafft() { status(T.s_kopiert, false); }\n        function misslungen() { status(T.s_kopieren_fehl, true); }\n        if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(geschafft, misslungen); return; }\n        misslungen();\n    }\n    function csvLaden() {\n        if (!letztes) { return; }\n        var blob = new Blob([csvBauen(letztes)], { type: 'text\/csv;charset=utf-8' });\n        var url = URL.createObjectURL(blob);\n        var a = document.createElement('a');\n        a.href = url; a.download = 'gtm-container-cleanup.csv';\n        document.body.appendChild(a); a.click(); document.body.removeChild(a);\n        setTimeout(function () { URL.revokeObjectURL(url); }, 2000);\n    }<\/p>\n<p>    \/\/ --- Ablauf ------------------------------------------------------------------------------\n    function los(text, meldung) {\n        var eingabe = text === undefined ? el_('go-json').value : text;\n        if (!\/\\S\/.test(eingabe)) { el_('go-ergebnis').hidden = true; status(T.s_leer, true); return null; }\n        var t0 = (window.performance && performance.now) ? performance.now() : Date.now();\n        var gelesen = textLesen(eingabe);\n        if (gelesen.fehler === 'json') { el_('go-ergebnis').hidden = true; status(T.s_json + (gelesen.info ? ' ' + gelesen.info : ''), true); return null; }\n        if (gelesen.fehler === 'container') { el_('go-ergebnis').hidden = true; status(T.s_kein_container, true); return null; }\n        var res = analysiere(gelesen.cv);\n        zeigen(res);\n        var ms = Math.max(1, Math.round(((window.performance && performance.now) ? performance.now() : Date.now()) - t0));\n        status(meldung || fmt(T.s_ok, { n: zahl(res.elemente), ms: ms }), false);\n        return res;\n    }\n    function dateiLesen(datei) {\n        if (!datei) { return; }\n        if (datei.size > MAX_BYTES) { el_('go-ergebnis').hidden = true; status(T.s_zu_gross, true); return; }\n        status(T.s_lesen, false);\n        var fr = new FileReader();\n        fr.onload = function () { el_('go-json').value = String(fr.result); los(); };\n        fr.onerror = function () { status(T.s_lesefehler, true); };\n        fr.readAsText(datei);\n    }<\/p>\n<p>    var BEISPIEL = JSON.stringify({\n        exportFormatVersion: 2,\n        exportTime: '2026-11-03 09:00:00',\n        containerVersion: {\n            path: 'accounts\/1\/containers\/2\/versions\/0', accountId: '1', containerId: '2', containerVersionId: '0',\n            container: { accountId: '1', containerId: '2', name: 'example.com', publicId: 'GTM-EXAMPLE', usageContext: ['WEB'] },\n            folder: [{ folderId: '1', name: 'GA4' }, { folderId: '2', name: 'Old campaign' }, { folderId: '3', name: 'Experiments' }],\n            builtInVariable: [{ type: 'PAGE_URL', name: 'Page URL' }, { type: 'CLICK_TEXT', name: 'Click Text' }, { type: 'PAGE_PATH', name: 'Page Path' }],\n            variable: [\n                { variableId: '1', name: 'Const - GA4 ID', type: 'c', parameter: [{ type: 'TEMPLATE', key: 'value', value: 'G-EXAMPLE123' }], parentFolderId: '1' },\n                { variableId: '2', name: 'DLV - currency', type: 'v', parameter: [{ type: 'INTEGER', key: 'dataLayerVersion', value: '2' }, { type: 'TEMPLATE', key: 'name', value: 'ecommerce.currency' }], parentFolderId: '1' },\n                { variableId: '3', name: 'Const - old pixel ID', type: 'c', parameter: [{ type: 'TEMPLATE', key: 'value', value: '1234567890' }], parentFolderId: '2' }\n            ],\n            trigger: [\n                { triggerId: '1', name: 'All Pages', type: 'PAGEVIEW' },\n                { triggerId: '2', name: 'CE - purchase', type: 'CUSTOM_EVENT', customEventFilter: [{ type: 'EQUALS', parameter: [{ type: 'TEMPLATE', key: 'arg0', value: '{{_event}}' }, { type: 'TEMPLATE', key: 'arg1', value: 'purchase' }] }], parentFolderId: '1' },\n                { triggerId: '3', name: 'Click - spring sale banner', type: 'CLICK', filter: [{ type: 'CONTAINS', parameter: [{ type: 'TEMPLATE', key: 'arg0', value: '{{Click Text}}' }, { type: 'TEMPLATE', key: 'arg1', value: 'Spring sale' }] }], parentFolderId: '2' }\n            ],\n            tag: [\n                { tagId: '1', name: 'GA4 - Google tag', type: 'googtag', parameter: [{ type: 'TEMPLATE', key: 'tagId', value: '{{Const - GA4 ID}}' }], firingTriggerId: ['1'], parentFolderId: '1' },\n                { tagId: '2', name: 'GA4 - purchase', type: 'gaawe', parameter: [{ type: 'TEMPLATE', key: 'eventName', value: 'purchase' }, { type: 'TEMPLATE', key: 'measurementIdOverride', value: '{{Const - GA4 ID}}' }], firingTriggerId: ['9'], parentFolderId: '1' },\n                { tagId: '3', name: 'Pixel - old campaign', type: 'html', paused: true, parameter: [{ type: 'TEMPLATE', key: 'html', value: '<script>track(\"{{Const - old pixel ID}}\");<\\\/script>' }], firingTriggerId: ['3'], parentFolderId: '2' },\n                { tagId: '4', name: 'Floodlight - draft', type: 'fls', parameter: [{ type: 'TEMPLATE', key: 'activityTag', value: 'draft' }] },\n                { tagId: '5', name: 'HTML - consent bridge', type: 'html', parameter: [{ type: 'TEMPLATE', key: 'html', value: '<script>window.cmp = \"{{DLV - consent}}\"; var u = \"{{Page URL}}\";<\\\/script>' }], firingTriggerId: ['1'] }\n            ],\n            customTemplate: [{ templateId: '1', name: 'Unused gallery template', fingerprint: '1', templateData: '___INFO___' }]\n        }\n    }, null, 2);<\/p>\n<p>    window.LW_TEST = window.LW_TEST || {};\n    window.LW_TEST.GTMORPHAN = { T: T, analysiere: analysiere, textLesen: textLesen, holeContainer: holeContainer, los: los, csvBauen: csvBauen, listeBauen: listeBauen,\n        BEISPIEL: BEISPIEL, ORDNUNG: ORDNUNG, ARTEN: ARTEN, loeschGruppen: loeschGruppen, BUILTIN_TRIGGER: BUILTIN_TRIGGER };\n    \/\/ KERN-ENDE<\/p>\n<p>    if (!el_('go-wurzel') || !el_('go-json')) { return; }\n    el_('go-los').addEventListener('click', function () { los(); });\n    el_('go-beispiel').addEventListener('click', function () { el_('go-json').value = BEISPIEL; los(BEISPIEL, T.s_beispiel); });\n    el_('go-leeren').addEventListener('click', function () {\n        el_('go-json').value = ''; el_('go-datei').value = ''; el_('go-ergebnis').hidden = true; letztes = null; status('', false);\n    });\n    el_('go-datei').addEventListener('change', function () { dateiLesen(this.files && this.files[0]); });\n    el_('go-kopieren').addEventListener('click', kopieren);\n    el_('go-csv').addEventListener('click', csvLaden);\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reads a Google Tag Manager container export in the browser and lists what has lost its connection: tags that can never fire, triggers and variables nothing uses, leftover templates and folders, elements kept alive only by paused tags, and references that point nowhere. Nothing is uploaded.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[91297,91267,91516],"class_list":["post-28767","page","type-page","status-publish","hentry","tag-data-quality","tag-google-tag-manager","tag-tracking"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables | Lukas Wojcik<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Reads a Google Tag Manager container export in the browser and lists what has lost its connection: tags that can never fire, triggers and variables nothing uses, leftover templates and folders, elements kept alive only by paused tags, and references that point nowhere. Nothing is uploaded.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-11-04T18:17:22+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-container-orphan-checker-unused-tags-triggers-variables\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-container-orphan-checker-unused-tags-triggers-variables\\\/\",\"name\":\"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-11-04T17:06:23+00:00\",\"dateModified\":\"2026-11-04T18:17:22+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-container-orphan-checker-unused-tags-triggers-variables\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-container-orphan-checker-unused-tags-triggers-variables\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/gtm-container-orphan-checker-unused-tags-triggers-variables\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables | Lukas Wojcik","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/","og_locale":"en_US","og_type":"article","og_title":"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables | Lukas Wojcik","og_description":"Reads a Google Tag Manager container export in the browser and lists what has lost its connection: tags that can never fire, triggers and variables nothing uses, leftover templates and folders, elements kept alive only by paused tags, and references that point nowhere. Nothing is uploaded.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/","og_site_name":"Lukas Wojcik - Blog","article_modified_time":"2026-11-04T18:17:22+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/","name":"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-11-04T17:06:23+00:00","dateModified":"2026-11-04T18:17:22+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/gtm-container-orphan-checker-unused-tags-triggers-variables\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"GTM Container Cleanup Checker: Orphaned Tags, Triggers and Variables"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/28767","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=28767"}],"version-history":[{"count":4,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/28767\/revisions"}],"predecessor-version":[{"id":28791,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/28767\/revisions\/28791"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=28767"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=28767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}