fontSize: '14px',
wordBreak: 'break-all'
});
return container;
},
showResult(container, url, success = true) {
container.style.display = 'block';
if (success) {
container.innerHTML = ` <div style="margin-bottom: 10px; font-weight: 600; color: #059669;">
${CONFIG.successText}
</div>
<div style="margin-bottom: 10px;">
<strong>Your aéPiot Backlink:</strong>
</div>
<a href="${url}"
target="_blank"
rel="noopener noreferrer"
style="color: #2563eb; text-decoration: underline; word-break: break-all;">
${url}
</a>
<div style="margin-top: 10px;">
<button onclick="navigator.clipboard.writeText('${url.replace(/'/g, "\\'")}').then(() => alert('Copied to clipboard!'))"
style="padding: 8px 16px; background: #10b981; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;">
📋 Copy Link
</button>
</div>
`;
} else {
container.innerHTML = ` <div style="color: #dc2626; font-weight: 600;">
${CONFIG.errorText}
</div>
<div style="margin-top: 8px; color: #666;">
Please try again or contact support if the problem persists.
</div>
`;
}
}
};
// Main: Initialize the generator
function initialize() {
// Create UI elements
const button = GeneratorUI.createButton();
const resultContainer = GeneratorUI.createResultContainer();
// Create wrapper
const wrapper = document.createElement('div');
wrapper.style.margin = '30px 0';
wrapper.appendChild(button);
wrapper.appendChild(resultContainer);
// Button click handler
button.addEventListener('click', async function() {
// Prevent multiple clicks
if (button.disabled) return;
button.disabled = true;
button.textContent = CONFIG.generatingText;
button.style.cursor = 'wait';
resultContainer.style.display = 'none';
try {
// Extract metadata
const title = MetadataExtractor.getTitle();
const description = MetadataExtractor.getDescription();
const url = MetadataExtractor.getCanonicalURL();
// Small delay for better UX (simulates processing)
await new Promise(resolve => setTimeout(resolve, 500));
// Generate backlink
const backlinkURL = URLGenerator.generate(title, description, url);
// Show result
GeneratorUI.showResult(resultContainer, backlinkURL, true);
button.textContent = CONFIG.successText;
button.style.backgroundColor = '#10b981';
// Log for analytics (if available)
if (typeof gtag !== 'undefined') {
gtag('event', 'aepiot_backlink_generated', {
'event_category': 'engagement',
'event_label': title
});
}
} catch (error) {
console.error('aéPiot generation error:', error);
GeneratorUI.showResult(resultContainer, '', false);
button.textContent = CONFIG.errorText;
button.style.backgroundColor = '#dc2626';
} finally {
// Re-enable button after 2 seconds
setTimeout(() => {
button.disabled = false;
button.textContent = CONFIG.buttonText;
button.style.backgroundColor = '#6366f1';
button.style.cursor = 'pointer';
}, 2000);
}
});
// Insert into page
const insertionPoint = document.querySelector('article, main, .content, .post-content') || document.body;
// Try to insert after first paragraph or at the end
const firstParagraph = insertionPoint.querySelector('p');
if (firstParagraph) {
firstParagraph.insertAdjacentElement('afterend', wrapper);
} else {
insertionPoint.appendChild(wrapper);
}
}
// Execute when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();
</script>
Features:
- ⚡ Generates link only on user action
- 🎨 Clean, modern UI with animations
- 📋 One-click copy to clipboard
- ✅ Success/error state management
- 🔒 Input validation and sanitization
- ♿ Accessibility-compliant (ARIA attributes)
- 📊 Analytics integration support
🔄 Scroll-Triggered Progressive Generator
Generates backlink when user scrolls to specific point, improving perceived performance.
html
<script>
(function() {
'use strict';
let backlinkGenerated = false;
let observer = null;
// Configuration
const CONFIG = {
triggerPercent: 50, // Generate when 50% scrolled
triggerElement: null, // Or specify element: '.trigger-point'
insertPosition: 'end' // 'end', 'start', or CSS selector
};
// Data extraction utilities
const DataUtils = {
extractMetadata() {
const metadata = {
title: this.findTitle(),
description: this.findDescription(),
url: this.findURL(),
image: this.findImage()
};
return metadata;
},
findTitle() {
const selectors = [
'h1[itemprop="headline"]',
'h1.entry-title',
'h1.post-title',
'article h1',
'h1'
];
for (let selector of selectors) {
const element = document.querySelector(selector);
if (element && element.textContent.trim()) {
return element.textContent.trim();
}
}
return document.title || 'Page';
},
findDescription() {
const metaDesc = document.querySelector('meta[name="description"]')?.content;
if (metaDesc && metaDesc.length > 30) return metaDesc;
// Try first paragraph
const firstP = document.querySelector('article p, main p, .entry-content p, .post-content p');
if (firstP) {
return firstP.textContent.trim().substring(0, 160);
}
return 'Valuable content available on this page';
},
findURL() {
return document.querySelector('link[rel="canonical"]')?.href ||
window.location.href;
},
findImage() {
return document.querySelector('meta[property="og:image"]')?.content ||
document.querySelector('article img, main img')?.src ||
'';
}
};
// Link generation
const LinkGenerator = {
create(metadata) {
const title = this.sanitize(metadata.title, 200);
const desc = this.sanitize(metadata.description, 500);
const url = metadata.url;
const params = new URLSearchParams({
title: title,
description: desc,
link: url
});
return `https://aepiot.com/backlink.html?${params.toString()}`;
},
sanitize(text, maxLength) {
if (!text) return '';
// Remove excess whitespace
text = text.replace(/\s+/g, ' ').trim();
// Remove control characters
text = text.replace(/[\x00-\x1F\x7F]/g, '');
// Truncate if needed
if (text.length > maxLength) {
text = text.substring(0, maxLength - 3) + '...';
}
return text;
}
};
// UI creation
const UI = {
createBacklinkElement(url) {
const container = document.createElement('div');
container.className = 'aepiot-scroll-generated';
container.setAttribute('role', 'complementary');
container.setAttribute('aria-label', 'Page backlink');
Object.assign(container.style, {
margin: '40px 0',
padding: '24px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: '12px',
boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
animation: 'slideIn 0.5s ease-out'
});
container.innerHTML = ` <style>
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<div style="color: white; text-align: center;">
<div style="font-size: 24px; margin-bottom: 8px;">🔗</div>
<div style="font-weight: 600; font-size: 18px; margin-bottom: 12px;">
Share this page via aéPiot
</div>
<a href="${url}"
target="_blank"
rel="noopener noreferrer"
style="display: inline-block; padding: 12px 32px; background: white; color: #667eea;
text-decoration: none; border-radius: 8px; font-weight: 600;
transition: transform 0.2s ease, box-shadow 0.2s ease;"
onmouseenter="this.style.transform='scale(1.05)'; this.style.boxShadow='0 4px 12px rgba(0,0,0,0.2)';"
onmouseleave="this.style.transform='scale(1)'; this.style.boxShadow='none';">
View Backlink →
</a>
</div>
`;
return container;
},
insert(element) {
const article = document.querySelector('article, main, .content, .post-content');
if (CONFIG.insertPosition === 'start' && article) {
article.insertBefore(element, article.firstChild);
} else if (typeof CONFIG.insertPosition === 'string' && CONFIG.insertPosition !== 'end') {
const target = document.querySelector(CONFIG.insertPosition);
if (target) {
target.appendChild(element);
return;
}
}
// Default: append to article or body
(article || document.body).appendChild(element);
}
};
// Scroll detection
const ScrollDetector = {
init() {
if (CONFIG.triggerElement) {
this.observeElement();
} else {
this.observeScroll();
}
},
observeElement() {
const target = document.querySelector(CONFIG.triggerElement);
if (!target) {
console.warn('Trigger element not found, falling back to scroll detection');
this.observeScroll();
return;
}
observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !backlinkGenerated) {
this.generateBacklink();
}
});
}, { threshold: 0.5 });
observer.observe(target);
},
observeScroll() {
const checkScroll = () => {
if (backlinkGenerated) return;
const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
if (scrollPercent >= CONFIG.triggerPercent) {
this.generateBacklink();
}
};
// Throttle scroll events
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(() => {
checkScroll();
ticking = false;
});
ticking = true;
}
});
},
generateBacklink() {
if (backlinkGenerated) return;
backlinkGenerated = true;
// Disconnect observer if used
if (observer) {
observer.disconnect();
}
// Extract data and generate
const metadata = DataUtils.extractMetadata();
const backlinkURL = LinkGenerator.create(metadata);
// Create and insert UI
const element = UI.createBacklinkElement(backlinkURL);
UI.insert(element);
// Analytics
if (typeof gtag !== 'undefined') {
gtag('event', 'aepiot_scroll_generated', {
'event_category': 'engagement',
'scroll_depth': CONFIG.triggerPercent
});
}
console.log('[aéPiot] Backlink generated:', backlinkURL);
}
};
// Initialize when ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => ScrollDetector.init());
} else {
ScrollDetector.init();
}
})();
</script>Features:
- 📜 Scroll-triggered generation (lazy loading)
- 👁️ Intersection Observer API support
- ⚡ Improved initial page load performance
- 🎬 Smooth animations
- 🎯 Flexible trigger configuration
- 📊 Analytics tracking built-in
🖱️ Hover-to-Preview System
Shows a preview card when user hovers over content, with option to generate link.
html
<script>
(function() {
'use strict';
// Configuration
const CONFIG = {
hoverDelay: 800, // ms before showing preview
targetSelector: 'h1, h2.entry-title, .post-title',
previewPosition: 'below' // 'below', 'above', 'side'
};
let hoverTimeout = null;
let previewCard = null;
let currentTarget = null;
// Preview card builder
const PreviewCard = {
create(metadata, backlinkURL) {
const card = document.createElement('div');
card.className = 'aepiot-hover-preview';
card.setAttribute('role', 'tooltip');
Object.assign(card.style, {
position: 'absolute',
zIndex: '10000',
width: '320px',
padding: '20px',
background: 'white',
border: '2px solid #e5e7eb',
borderRadius: '12px',
boxShadow: '0 10px 40px rgba(0,0,0,0.15)',
fontSize: '14px',
lineHeight: '1.6',
animation: 'fadeIn 0.3s ease-out'
});
card.innerHTML = ` <style>
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.aepiot-hover-preview-btn {
transition: all 0.2s ease;
}
.aepiot-hover-preview-btn:hover {
transform: translateY(-2px);
}
</style>
<div style="margin-bottom: 12px;">
<div style="font-weight: 700; color: #111827; margin-bottom: 4px; font-size: 16px;">
📎 aéPiot Backlink
</div>
<div style="color: #6b7280; font-size: 13px;">
Share this content with a semantic backlink
</div>
</div>
<div style="margin-bottom: 16px; padding: 12px; background: #f9fafb; border-radius: 8px;">
<div style="font-size: 13px; color: #374151; margin-bottom: 6px;">
<strong>Title:</strong> ${this.truncate(metadata.title, 50)}
</div>
<div style="font-size: 12px; color: #6b7280;">
${this.truncate(metadata.description, 80)}
</div>
</div>
<div style="display: flex; gap: 8px;">
<a href="${backlinkURL}"
target="_blank"
rel="noopener noreferrer"
class="aepiot-hover-preview-btn"
style="flex: 1; padding: 10px 16px; background: #6366f1; color: white;
text-align: center; text-decoration: none; border-radius: 8px;
font-weight: 600; font-size: 13px;">
Open Link
</a>
<button onclick="navigator.clipboard.writeText('${backlinkURL.replace(/'/g, "\\'")}');
this.textContent='✅ Copied';
setTimeout(() => this.textContent='📋 Copy', 2000);"
class="aepiot-hover-preview-btn"
style="padding: 10px 16px; background: #f3f4f6; color: #374151;
border: 1px solid #d1d5db; border-radius: 8px;
font-weight: 600; font-size: 13px; cursor: pointer;">
📋 Copy
</button>
</div>
<div style="margin-top: 12px; text-align: center;">
<button onclick="this.closest('.aepiot-hover-preview').remove();"
style="background: none; border: none; color: #9ca3af;
font-size: 12px; cursor: pointer; text-decoration: underline;">
Close
</button>
</div>
`;
return card;
},
truncate(text, length) {
if (!text) return '';
if (text.length <= length) return text;
return text.substring(0, length - 3) + '...';
},
position(card, target) {
const rect = target.getBoundingClientRect();
const cardHeight = 280; // approximate
if (CONFIG.previewPosition === 'above') {
card.style.top = (rect.top + window.scrollY - cardHeight - 10) + 'px';
card.style.left = (rect.left + window.scrollX) + 'px';
} else if (CONFIG.previewPosition === 'side') {
card.style.top = (rect.top + window.scrollY) + 'px';
card.style.left = (rect.right + window.scrollX + 15) + 'px';
} else {
// Default: below
card.style.top = (rect.bottom + window.scrollY + 10) + 'px';
card.style.left = (rect.left + window.scrollX) + 'px';
}
}
};
// Metadata extraction
function extractMetadata() {
return {
title: document.title || 'Untitled',
description: document.querySelector('meta[name="description"]')?.content ||
document.querySelector('p')?.textContent?.substring(0, 160) ||
'Content',
url: window.location.href
};
}
// Generate backlink URL
function generateBacklinkURL(metadata) {
const params = new URLSearchParams({
title: metadata.title,
description: metadata.description,
link: metadata.url
});
return `https://aepiot.com/backlink.html?${params.toString()}`;
}
// Show preview
function showPreview(target) {
// Remove existing preview
if (previewCard) {
previewCard.remove();
}
const metadata = extractMetadata();
const backlinkURL = generateBacklinkURL(metadata);
previewCard = PreviewCard.create(metadata, backlinkURL);
PreviewCard.position(previewCard, target);
document.body.appendChild(previewCard);
currentTarget = target;
// Auto-hide when clicking outside
setTimeout(() => {
document.addEventListener('click', hidePreview);
}, 100);
}
// Hide preview
function hidePreview(event) {
if (previewCard &&
!previewCard.contains(event?.target) &&
currentTarget !== event?.target) {
previewCard.remove();
previewCard = null;
document.removeEventListener('click', hidePreview);
}
}
// Initialize hover listeners
function initialize() {
const targets = document.querySelectorAll(CONFIG.targetSelector);
targets.forEach(target => {
target.style.cursor = 'help';
target.title = 'Hover to generate aéPiot backlink';
target.addEventListener('mouseenter', () => {
hoverTimeout = setTimeout(() => {
showPreview(target);
}, CONFIG.hoverDelay);
});
target.addEventListener('mouseleave', () => {
clearTimeout(hoverTimeout);
});
});
console.log(`[aéPiot] Hover preview enabled on ${targets.length} elements`);
}
// Start when ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();
</script>Features:
- 🖱️ Hover-triggered preview cards
- ⏱️ Configurable delay
- 📋 Quick copy functionality
- 🎯 Smart positioning
- 🎨 Modern, clean design
- ♿ Accessible tooltips
Continue to Part 3: Modular & Advanced Patterns →
Alternative aéPiot Working Scripts Guide - Part 3: Modular & Advanced Patterns
Web Components, Observers, and Dynamic Content Handling
🎨 Custom Web Component Implementation
Create a reusable <aepiot-link> web component for maximum flexibility.
html
<script>
(function() {
'use strict';
class AePiotLinkElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this._backlinkURL = null;
}
// Observed attributes
static get observedAttributes() {
return ['title', 'description', 'url', 'style-variant', 'auto-generate'];
}
// Lifecycle: Connected to DOM
connectedCallback() {
this.render();
if (this.getAttribute('auto-generate') === 'true') {
this.generateBacklink();
}
}
// Lifecycle: Attribute changed
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}
// Generate backlink URL
generateBacklink() {
const title = this.getAttribute('title') || this.extractTitle();
const description = this.getAttribute('description') || this.extractDescription();
const url = this.getAttribute('url') || window.location.href;
const params = new URLSearchParams({
title: this.sanitize(title, 200),
description: this.sanitize(description, 500),
link: url
});
this._backlinkURL = `https://aepiot.com/backlink.html?${params.toString()}`;
this.render();
// Dispatch custom event
this.dispatchEvent(new CustomEvent('backlink-generated', {
detail: { url: this._backlinkURL },
bubbles: true,
composed: true
}));
}
// Extract title from page
extractTitle() {
return document.title ||
document.querySelector('h1')?.textContent?.trim() ||
'Page';
}
// Extract description from page
extractDescription() {
return document.querySelector('meta[name="description"]')?.content ||
document.querySelector('p')?.textContent?.trim().substring(0, 160) ||
'Content';
}
// Sanitize text
sanitize(text, maxLength) {
if (!text) return '';
text = text.replace(/\s+/g, ' ').trim();
if (text.length > maxLength) {
text = text.substring(0, maxLength - 3) + '...';
}
return text;
}
// Render component
render() {
const variant = this.getAttribute('style-variant') || 'default';
const styles = this.getStyles(variant);
this.shadowRoot.innerHTML = ` <style>
:host {
display: block;
margin: 20px 0;
}
.container {
${styles.container}
}
.button {
${styles.button}
transition: all 0.3s ease;
}
.button:hover {
${styles.buttonHover}
}
.link {
${styles.link}
}
.generated {
${styles.generated}
}
@media (max-width: 768px) {
.container {
padding: 16px;
}
.button {
font-size: 14px;
padding: 10px 20px;
}
}
</style>
${this._backlinkURL ? this.renderGenerated() : this.renderButton()}
`;
this.attachEventListeners();
}
// Render button state
renderButton() {
return `
<div class="container">
<button class="button" id="generate-btn">
🔗 Generate aéPiot Backlink
</button>
</div>
`;
}
// Render generated state
renderGenerated() {
return `
<div class="container generated">
<div style="margin-bottom: 12px; font-weight: 600; color: #059669;">
✅ Backlink Generated
</div>
<a href="${this._backlinkURL}"
class="link"
target="_blank"
rel="noopener noreferrer">
${this.truncate(this._backlinkURL, 60)}
</a>
<div style="margin-top: 12px;">
<button class="button" id="copy-btn" style="font-size: 14px;">
📋 Copy to Clipboard
</button>
<button class="button" id="regenerate-btn"
style="font-size: 14px; margin-left: 8px; background: #6b7280;">
🔄 Regenerate
</button>
</div>
</div>
`;
}