🎯 Click-to-Generate Link System
This script generates the backlink only when user clicks, reducing initial page load.
html
<script>
(function() {
'use strict';
// Configuration
const CONFIG = {
buttonText: '🔗 Generate Backlink',
generatingText: '⏳ Generating...',
successText: '✅ Backlink Created!',
errorText: '❌ Generation Failed',
buttonClass: 'aepiot-generator-btn',
resultClass: 'aepiot-result'
};
// Utility: Extract page metadata
const MetadataExtractor = {
getTitle() {
return document.title ||
document.querySelector('h1')?.textContent?.trim() ||
document.querySelector('meta[property="og:title"]')?.content ||
'Untitled Page';
},
getDescription() {
const sources = [
() => document.querySelector('meta[name="description"]')?.content,
() => document.querySelector('meta[property="og:description"]')?.content,
() => document.querySelector('article p, main p')?.textContent?.trim().substring(0, 160),
() => document.querySelector('.content p, .post-content p')?.textContent?.trim().substring(0, 160),
() => 'Discover valuable content on this page'
];
for (let source of sources) {
const result = source();
if (result && result.length > 20) return result;
}
return sources[sources.length - 1]();
},
getCanonicalURL() {
return document.querySelector('link[rel="canonical"]')?.href ||
document.querySelector('meta[property="og:url"]')?.content ||
window.location.href;
}
};
// Utility: URL encoding with validation
const URLGenerator = {
sanitize(text, maxLength = 500) {
if (!text) return '';
// Remove control characters
text = text.replace(/[\x00-\x1F\x7F]/g, '');
// Normalize whitespace
text = text.replace(/\s+/g, ' ').trim();
// Limit length
if (text.length > maxLength) {
text = text.substring(0, maxLength - 3) + '...';
}
return text;
},
generate(title, description, url) {
const cleanTitle = this.sanitize(title, 200);
const cleanDesc = this.sanitize(description, 500);
const encoded = {
title: encodeURIComponent(cleanTitle),
description: encodeURIComponent(cleanDesc),
url: encodeURIComponent(url)
};
return `https://aepiot.com/backlink.html?title=${encoded.title}&description=${encoded.description}&link=${encoded.url}`;
}
};
// UI Component: Generator Button
const GeneratorUI = {
createButton() {
const button = document.createElement('button');
button.type = 'button';
button.className = CONFIG.buttonClass;
button.textContent = CONFIG.buttonText;
button.setAttribute('aria-label', 'Generate aéPiot backlink for this page');
this.applyButtonStyles(button);
return button;
},
applyButtonStyles(button) {
Object.assign(button.style, {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
padding: '12px 24px',
margin: '20px 0',
backgroundColor: '#6366f1',
color: 'white',
border: 'none',
borderRadius: '8px',
fontSize: '16px',
fontWeight: '600',
cursor: 'pointer',
transition: 'all 0.3s ease',
boxShadow: '0 2px 8px rgba(99, 102, 241, 0.3)',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
});
// Hover effect
button.addEventListener('mouseenter', () => {
button.style.backgroundColor = '#4f46e5';
button.style.transform = 'translateY(-2px)';
button.style.boxShadow = '0 4px 12px rgba(99, 102, 241, 0.4)';
});
button.addEventListener('mouseleave', () => {
button.style.backgroundColor = '#6366f1';
button.style.transform = 'translateY(0)';
button.style.boxShadow = '0 2px 8px rgba(99, 102, 241, 0.3)';
});
},
createResultContainer() {
const container = document.createElement('div');
container.className = CONFIG.resultClass;
container.style.display = 'none';
container.setAttribute('role', 'status');
container.setAttribute('aria-live', 'polite');
Object.assign(container.style, {
marginTop: '15px',
padding: '15px',
backgroundColor: '#f0f9ff',
border: '1px solid #bae6fd',
borderRadius: '8px',
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