Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions detectAutocomplete.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
javascript: (function () {
// Get all custom elements in the document
function findCustomElements(root = document) {
return Array.from(root.querySelectorAll('*')).filter((elm) =>
elm.tagName.includes('-')
);
}

// Deep querySelectorAll to include shadow DOMs
function deepQuerySelectorAll(selector, root = document) {
const results = Array.from(root.querySelectorAll(selector));
const customElements = findCustomElements(root);
for (const el of customElements) {
if ('shadowRoot' in el && el.shadowRoot) {
results.push(...deepQuerySelectorAll(selector, el.shadowRoot));
}
}
return results;
}

// Remove existing elements and styles from previous executions
function cleanup() {
document
.querySelectorAll('.ac-indicator, .ac-panel')
.forEach((el) => el.remove());
const existingStyle = document.querySelector('#ac-styles');
if (existingStyle) existingStyle.remove();
deepQuerySelectorAll('.ac-indicator, .ac-panel').forEach((el) =>
el.remove()
);
const existingStyles = deepQuerySelectorAll('#ac-styles');
existingStyles.forEach((style) => style.remove());
}
cleanup();

Expand All @@ -14,7 +33,7 @@ javascript: (function () {
const fragment = document.createDocumentFragment();

// Get all form elements
const elements = document.querySelectorAll('input, select, textarea');
const elements = deepQuerySelectorAll('input, select, textarea');

// Initialize results object
const results = {
Expand Down Expand Up @@ -279,6 +298,12 @@ javascript: (function () {
`;
fragment.appendChild(style);

// Append styles to shadow DOMs of custom elements
const customElements = findCustomElements();
customElements.forEach((customElement) => {
customElement.shadowRoot?.appendChild(style.cloneNode(true));
});

// Define Maps for allowed values with metadata
const allowedValuesMap = new Map([
// General purpose
Expand Down Expand Up @@ -1288,9 +1313,9 @@ javascript: (function () {
);

requestAnimationFrame(() => {
document
.querySelectorAll('.ac-indicator')
.forEach((el) => el.classList.toggle('ac-hidden'));
deepQuerySelectorAll('.ac-indicator').forEach((el) =>
el.classList.toggle('ac-hidden')
);
});
});
}
Expand Down
65 changes: 54 additions & 11 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,13 @@
</head>

<body>
<h1><span aria-hidden="true">🔍</span> Autocomplete Attribute Audit - Demo</h1>
<p>This demo lets you test a bookmarklet that audits <code>autocomplete</code> attributes on form elements.</p>
<h1>
<span aria-hidden="true">🔍</span> Autocomplete Attribute Audit - Demo
</h1>
<p>
This demo lets you test a bookmarklet that audits
<code>autocomplete</code> attributes on form elements.
</p>

<fieldset>
<legend><span aria-hidden="true">✅</span> Valid values</legend>
Expand Down Expand Up @@ -117,7 +122,9 @@ <h1><span aria-hidden="true">🔍</span> Autocomplete Attribute Audit - Demo</h1
</fieldset>

<fieldset>
<legend><span aria-hidden="true">⚠️</span> Missing <code>autocomplete</code></legend>
<legend>
<span aria-hidden="true">⚠️</span> Missing <code>autocomplete</code>
</legend>
<label>
Job title:
<input type="text" name="job" />
Expand Down Expand Up @@ -156,33 +163,69 @@ <h1><span aria-hidden="true">🔍</span> Autocomplete Attribute Audit - Demo</h1
</label>
</fieldset>

<fieldset>
<legend><span aria-hidden="true">🧩</span> Web Component: valid vs invalid</legend>
<custom-input label="Valid Web Component" autocomplete="email"></custom-input>
<custom-input label="Invalid Web Component" autocomplete="my-custom-field"></custom-input>
</fieldset>
</fieldset>

<button class="launch-btn" id="runAudit">Run Autocomplete Audit</button>
<p class="note">The audit script will be dynamically loaded and executed.</p>
<p class="note">
The audit script will be dynamically loaded and executed.
</p>

<p class="note">
<span aria-hidden="true">ℹ️</span> Learn more in the <a
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute"
<span aria-hidden="true">ℹ️</span> Learn more in the
<a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute"
target="_blank" rel="noopener">HTML Standard for the autocomplete attribute</a>.
</p>
</body>


<script>
const btn = document.getElementById('runAudit');
btn.addEventListener('click', runAudit);
const btn = document.getElementById("runAudit");
btn.addEventListener("click", runAudit);

function runAudit() {
// Prevent adding the script multiple times
const existingScript = document.getElementById('ac-script');
const existingScript = document.getElementById("ac-script");
if (existingScript) {
existingScript.remove();
}

const script = document.createElement("script");
script.id = "ac-script";
script.src = "https://cdn.jsdelivr.net/gh/MewenLeHo/detectAutocomplete@main/detectAutocomplete.min.js";
script.src =
"https://cdn.jsdelivr.net/gh/MewenLeHo/detectAutocomplete@main/detectAutocomplete.min.js";
document.body.appendChild(script);
}
</script>

<script>
class CustomInput extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: "open" });
const labelText = this.getAttribute("label") || "";
const type = this.getAttribute("type") || "text";
const autocomplete = this.getAttribute("autocomplete") || "";
const label = document.createElement("label");
label.textContent = labelText;
const input = document.createElement("input");
input.type = type;
input.setAttribute("autocomplete", autocomplete);
label.appendChild(input);
// Récupère le style global de la page
const globalStyle = document.querySelector('style');
if (globalStyle) {
const style = document.createElement('style');
style.textContent = globalStyle.textContent;
shadow.appendChild(style);
}
shadow.appendChild(label);
}
}
customElements.define("custom-input", CustomInput);
</script>

</html>