MediaWiki:Common.js: Difference between revisions
MediaWiki interface page
More actions
Jonatanktk (talk | contribs) No edit summary |
Jonatanktk (talk | contribs) No edit summary |
||
| Line 25: | Line 25: | ||
document.body.appendChild(tooltip); | document.body.appendChild(tooltip); | ||
const arrow = document.createElement('div'); | const arrow = document.createElement('div'); | ||
Object.assign(arrow.style, { | Object.assign(arrow.style, { | ||
| Line 48: | Line 47: | ||
activeEl = el; | activeEl = el; | ||
el.removeAttribute('title'); // | el.removeAttribute('title'); // native verhindern | ||
// Tooltip vorbereiten, aber noch unsichtbar | |||
tooltip.textContent = storedTitle; | tooltip.textContent = storedTitle; | ||
tooltip.appendChild(arrow); | tooltip.appendChild(arrow); | ||
tooltip.style.opacity = ' | tooltip.style.opacity = '0'; | ||
tooltip.style.transform = 'translateY(0)'; | |||
// Force Reflow um korrekte Größe zu bekommen | |||
tooltip.getBoundingClientRect(); | |||
tooltip. | |||
// Position berechnen | |||
const rect = el.getBoundingClientRect(); | const rect = el.getBoundingClientRect(); | ||
const ttRect = tooltip.getBoundingClientRect(); | const ttRect = tooltip.getBoundingClientRect(); | ||
const spacing = 6; | const spacing = 6; | ||
let top, left; | |||
const spaceAbove = rect.top; | const spaceAbove = rect.top; | ||
const spaceBelow = window.innerHeight - rect.bottom; | const spaceBelow = window.innerHeight - rect.bottom; | ||
| Line 77: | Line 69: | ||
const spaceLeft = rect.left; | const spaceLeft = rect.left; | ||
// | // Intelligente Position: genug Platz bevorzugen | ||
let position = ' | let position = 'bottom'; | ||
if (spaceAbove > ttRect.height + spacing) position = 'top'; | |||
if ( | |||
else if (spaceRight > ttRect.width + spacing) position = 'right'; | else if (spaceRight > ttRect.width + spacing) position = 'right'; | ||
else if (spaceLeft > ttRect.width + spacing) position = 'left'; | else if (spaceLeft > ttRect.width + spacing) position = 'left'; | ||
switch(position) { | switch (position) { | ||
case 'top': | case 'top': | ||
top = rect.top - ttRect.height - spacing; | top = rect.top - ttRect.height - spacing; | ||
| Line 91: | Line 82: | ||
top: '100%', | top: '100%', | ||
left: '50%', | left: '50%', | ||
transform: 'translateX(-50%)', | transform: 'translateX(-50%) rotate(0deg)', | ||
borderTopColor: 'rgba(20,20,20,0.95)', | borderTopColor: 'rgba(20,20,20,0.95)', | ||
}); | }); | ||
break; | break; | ||
| Line 104: | Line 94: | ||
transform: 'translateX(-50%) rotate(180deg)', | transform: 'translateX(-50%) rotate(180deg)', | ||
borderTopColor: 'rgba(20,20,20,0.95)', | borderTopColor: 'rgba(20,20,20,0.95)', | ||
}); | }); | ||
break; | break; | ||
| Line 115: | Line 104: | ||
transform: 'translateY(-50%) rotate(90deg)', | transform: 'translateY(-50%) rotate(90deg)', | ||
borderTopColor: 'rgba(20,20,20,0.95)', | borderTopColor: 'rgba(20,20,20,0.95)', | ||
}); | }); | ||
break; | break; | ||
| Line 126: | Line 114: | ||
transform: 'translateY(-50%) rotate(-90deg)', | transform: 'translateY(-50%) rotate(-90deg)', | ||
borderTopColor: 'rgba(20,20,20,0.95)', | borderTopColor: 'rgba(20,20,20,0.95)', | ||
}); | }); | ||
break; | break; | ||
} | } | ||
// | // Clamp an Viewport | ||
if (left < 4) left = 4; | if (left < 4) left = 4; | ||
if (left + ttRect.width > window.innerWidth - 4) | if (left + ttRect.width > window.innerWidth - 4) | ||
| Line 141: | Line 128: | ||
tooltip.style.top = top + 'px'; | tooltip.style.top = top + 'px'; | ||
tooltip.style.left = left + 'px'; | tooltip.style.left = left + 'px'; | ||
tooltip.style.opacity = '1'; | |||
// Verstecken bei Mouseleave | |||
el.addEventListener('mouseleave', hideTooltip, { once: true }); | |||
} | |||
function hideTooltip() { | |||
if (!activeEl) return; | |||
tooltip.style.opacity = '0'; | |||
activeEl.setAttribute('title', storedTitle); | |||
activeEl = null; | |||
storedTitle = null; | |||
} | } | ||
document.addEventListener('mouseover', showTooltip); | document.addEventListener('mouseover', showTooltip); | ||
})(); | })(); | ||
Revision as of 23:51, 1 November 2025
/* Any JavaScript here will be loaded for all users on every page load. */
/* Schönes Tooltip-System auf Basis von alt-Attributen
Funktioniert sicher mit Citizen & MediaWiki UI */
(function() {
'use strict';
const tooltip = document.createElement('div');
tooltip.id = 'mw-smart-tooltip';
Object.assign(tooltip.style, {
position: 'absolute',
zIndex: 999999,
background: 'rgba(20,20,20,0.95)',
color: '#fff',
padding: '8px 12px',
borderRadius: '6px',
fontSize: '13px',
lineHeight: '1.4',
maxWidth: '220px',
wordWrap: 'break-word',
pointerEvents: 'none',
opacity: '0',
transition: 'opacity 0.2s, transform 0.15s',
});
document.body.appendChild(tooltip);
const arrow = document.createElement('div');
Object.assign(arrow.style, {
position: 'absolute',
width: '0',
height: '0',
borderLeft: '6px solid transparent',
borderRight: '6px solid transparent',
borderTop: '6px solid rgba(20,20,20,0.95)',
pointerEvents: 'none',
});
tooltip.appendChild(arrow);
let activeEl = null;
let storedTitle = null;
function showTooltip(e) {
const el = e.target.closest('[title]');
if (!el) return;
storedTitle = el.getAttribute('title');
if (!storedTitle) return;
activeEl = el;
el.removeAttribute('title'); // native verhindern
// Tooltip vorbereiten, aber noch unsichtbar
tooltip.textContent = storedTitle;
tooltip.appendChild(arrow);
tooltip.style.opacity = '0';
tooltip.style.transform = 'translateY(0)';
// Force Reflow um korrekte Größe zu bekommen
tooltip.getBoundingClientRect();
// Position berechnen
const rect = el.getBoundingClientRect();
const ttRect = tooltip.getBoundingClientRect();
const spacing = 6;
let top, left;
const spaceAbove = rect.top;
const spaceBelow = window.innerHeight - rect.bottom;
const spaceRight = window.innerWidth - rect.right;
const spaceLeft = rect.left;
// Intelligente Position: genug Platz bevorzugen
let position = 'bottom';
if (spaceAbove > ttRect.height + spacing) position = 'top';
else if (spaceRight > ttRect.width + spacing) position = 'right';
else if (spaceLeft > ttRect.width + spacing) position = 'left';
switch (position) {
case 'top':
top = rect.top - ttRect.height - spacing;
left = rect.left + rect.width/2 - ttRect.width/2;
Object.assign(arrow.style, {
top: '100%',
left: '50%',
transform: 'translateX(-50%) rotate(0deg)',
borderTopColor: 'rgba(20,20,20,0.95)',
});
break;
case 'bottom':
top = rect.bottom + spacing;
left = rect.left + rect.width/2 - ttRect.width/2;
Object.assign(arrow.style, {
top: '-6px',
left: '50%',
transform: 'translateX(-50%) rotate(180deg)',
borderTopColor: 'rgba(20,20,20,0.95)',
});
break;
case 'right':
top = rect.top + rect.height/2 - ttRect.height/2;
left = rect.right + spacing;
Object.assign(arrow.style, {
top: '50%',
left: '-6px',
transform: 'translateY(-50%) rotate(90deg)',
borderTopColor: 'rgba(20,20,20,0.95)',
});
break;
case 'left':
top = rect.top + rect.height/2 - ttRect.height/2;
left = rect.left - ttRect.width - spacing;
Object.assign(arrow.style, {
top: '50%',
left: '100%',
transform: 'translateY(-50%) rotate(-90deg)',
borderTopColor: 'rgba(20,20,20,0.95)',
});
break;
}
// Clamp an Viewport
if (left < 4) left = 4;
if (left + ttRect.width > window.innerWidth - 4)
left = window.innerWidth - ttRect.width - 4;
if (top < 4) top = 4;
if (top + ttRect.height > window.innerHeight - 4)
top = window.innerHeight - ttRect.height - 4;
tooltip.style.top = top + 'px';
tooltip.style.left = left + 'px';
tooltip.style.opacity = '1';
// Verstecken bei Mouseleave
el.addEventListener('mouseleave', hideTooltip, { once: true });
}
function hideTooltip() {
if (!activeEl) return;
tooltip.style.opacity = '0';
activeEl.setAttribute('title', storedTitle);
activeEl = null;
storedTitle = null;
}
document.addEventListener('mouseover', showTooltip);
})();