aboutsummaryrefslogtreecommitdiff
path: root/resources/assets/javascripts/bootstrap/tooltip.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/assets/javascripts/bootstrap/tooltip.js')
-rw-r--r--resources/assets/javascripts/bootstrap/tooltip.js72
1 files changed, 72 insertions, 0 deletions
diff --git a/resources/assets/javascripts/bootstrap/tooltip.js b/resources/assets/javascripts/bootstrap/tooltip.js
new file mode 100644
index 0000000..4b17cb0
--- /dev/null
+++ b/resources/assets/javascripts/bootstrap/tooltip.js
@@ -0,0 +1,72 @@
+// Attach global hover handler for tooltips.
+// Applies to all elements having a "data-tooltip" attribute.
+// Tooltip may be provided in the data-attribute itself or by
+// defining a title attribute. The latter is prefered due to
+// the obvious accessibility issues.
+
+let timeout = null;
+
+STUDIP.Tooltip.threshold = 6;
+
+$(document).on('mouseenter mouseleave focusin focusout', '[data-tooltip],.tooltip:has(.tooltip-content)', function(event) {
+ let data = $(this).data();
+
+ const visible = ['mouseenter', 'focusin'].includes(event.type);
+ const offset = $(this).offset();
+ const x = offset.left + $(this).outerWidth(true) / 2;
+ const y = offset.top;
+ const delay = data.tooltipDelay ?? 300;
+
+ let content;
+ let tooltip;
+
+ if (!data.tooltipObject) {
+ // If tooltip has not yet been created (first hover), obtain it's
+ // contents and create the actual tooltip object.
+ if (!data.tooltip || !$.isPlainObject(data.tooltip)) {
+ content = $('<div/>').text(data.tooltip || $(this).attr('title')).html();
+ } else if (data.tooltip.html !== undefined) {
+ content = data.tooltip.html;
+ } else if (data.tooltip.text !== undefined) {
+ content = data.tooltip.text;
+ } else {
+ throw "Invalid content for tooltip via data";
+ }
+ if (!content) {
+ content = $(this).find('.tooltip-content').remove().html();
+ }
+ $(this).attr('title', null);
+ $(this).attr('data-tooltip', content);
+
+ tooltip = new STUDIP.Tooltip(x, y, content);
+
+ data.tooltipObject = tooltip;
+
+ if (!this.hasAttribute('aria-label')) {
+ const div = document.createElement('div');
+ div.innerHTML = content;
+ this.setAttribute('aria-label', div.innerText.trim());
+ }
+
+ $(this).on('remove', function() {
+ tooltip.remove();
+ });
+ } else if (visible) {
+ // If tooltip has already been created, update it's position.
+ // This is neccessary if the surrounding content is scrollable AND has
+ // been scrolled. Otherwise the tooltip would appear at it's previous
+ // and now wrong location.
+ data.tooltipObject.position(x, y);
+ }
+
+ if (visible) {
+ $('.studip-tooltip').not(data.tooltipObject).hide();
+ data.tooltipObject.show();
+ } else {
+ timeout = setTimeout(() => data.tooltipObject.hide(), delay);
+ }
+}).on('mouseenter focusin', '.studip-tooltip', () => {
+ clearTimeout(timeout);
+}).on('mouseleave focusout', '.studip-tooltip', function() {
+ $(this).hide();
+});