MediaWiki:Gadget-tooltips.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
1 // <nowiki>
2 /* JavaScript tooltips
3 usage:
4
5 recommended usage: see [[Template:Tooltip]] and [[Template:Tooltip text]], or [[Module:Tooltip]] for module interface
6
7
8 raw usage:
9
10 Place this where you want the button to appear:
11 <span class="hidden js-tooltip-click" style="display:none;" data-tooltip-name="test">clickable</span>
12
13 place this elsewhere to define the content of the tooltip:
14 <div class="hidden js-tooltip-wrapper" style="display:none;" data-tooltip-for="test" data-tooltip-arrow="yes" data-tooltip-arrow-size="10" data-tooltip-style="custom style"><div class="js-tooltip-text">Content</div></div>
15
16
17 span.js-tooltip-click - required
18 attribute: data-tooltip-name - links to the corresponding divl; can have many with the same name
19 content: the clickable thing, defaults to ?
20
21 div.js-tooltip-wrapper - required
22 attributes:
23 data-tooltip-for - required; links this to spans with the data-tooltip-name equal to this
24 data-tooltip-arrow - optional; yes for arrow, no/default for no arrow
25 data-tooltip-arrow-size - optional; yes for arrow, no/default for no arrow
26 data-tooltip-style - optional; the width of the arrow (height=2width) in px; also defines the gap between the tooltip and the span. defaults to 10
27
28 content: div.js-tooltip-text
29
30 div.js-tooltip-text - required
31 contains: text/html to display inside tooltip
32
33 */
34 $(function () {
35 if (!($('.js-tooltip-wrapper').length && $('.js-tooltip-click').length)) {
36 return;
37 }
38
39 // every tooltip wrapper on the page considered separately
40
41 // remove excess tooltip wrappers for the same name - can cause issues
42 (function(){
43 var forarr = {}, forarrv, key, first;
44 $('.js-tooltip-wrapper').each(function(){
45 forarr[$(this).attr('data-tooltip-for')] = true;
46 });
47 for (key in forarr) {
48 first = $('.js-tooltip-wrapper[data-tooltip-for="'+key+'"]').first();
49 $('.js-tooltip-wrapper[data-tooltip-for="'+key+'"]').not(first).remove();
50 }
51 })();
52
53 $('.js-tooltip-wrapper').each(function () {
54 var $span,
55 $text,
56 $arrow,
57 $wrapper,
58 $close,
59 resizeEvent,
60 hasArrow = true,
61 arrpos,
62 style,
63 styles,
64 parsed_styles,
65 name,
66 size,
67 limitwidth = false,
68 $currspan = $(null);
69
70 // setup vars
71 $wrapper = $(this);
72 name = $wrapper.attr('data-tooltip-for');
73
74 if ($wrapper.attr('data-tooltip-arrow')) {
75 hasArrow = $wrapper.attr('data-tooltip-arrow').toLowerCase() == 'yes';
76 }
77 if ($wrapper.attr('data-tooltip-limit-width')) {
78 limitwidth = $wrapper.attr('data-tooltip-limit-width').toLowerCase() == 'yes';
79 }
80 style = $wrapper.attr('data-tooltip-style');
81 size = parseInt($wrapper.attr('data-tooltip-arrow-size'), 10);
82 if (typeof size !== 'number' || isNaN(size)) {
83 size = 10;
84 }
85
86 $text = $wrapper.find('.js-tooltip-text');
87
88 // setup wrapper css for movement
89 $wrapper.removeClass('hidden')
90 .on('js-tooltip-close', function () {
91 $wrapper.hide();
92 $currspan.removeAttr('data-is-currspan');
93 $currspan = $(null);
94 });
95
96 // setup span css
97 $span = $('span.js-tooltip-click[data-tooltip-name="' + name + '"]');
98 $span.removeClass('hidden')
99 .attr('title', 'Click for explanation, click again to close');
100 if ($span.html() === '') {
101 $span.text('?');
102 }
103
104 // setup arrow
105 $arrow = $('<div>');
106 $arrow.addClass('js-tooltip-arrow')
107 .css({
108 top: ($wrapper.outerHeight() * 0.3) + 'px',
109 left: ('-' + (size+2) + 'px'), // width of arrow + width of text div border
110 'margin-top': ('-' + (size/2) + 'px'),
111 'border-width': size + 'px', //actual width of the arrow
112 });
113 arrpos = '-' + (size+2) + 'px';
114
115 // easiest way to deal with arrow is to just not add it if it isn't specified
116 if (hasArrow) {
117 $wrapper.prepend($arrow);
118 }
119
120 // setup close button
121 $close = $('<button>');
122 $close.html('<img src="/images/Close-x-white.svg?1ccac" />')
123 .addClass('close js-tooltip-close')
124 .click(function(){
125 $wrapper.trigger('js-tooltip-close');
126 });
127 $text.prepend($close);
128
129 // setup resize event for repositioning tooltips
130 resizeEvent = function () {
131 if ($currspan.length === 0) {
132 return;
133 }
134 var offset, position, width, $body, $mwtext;
135 offset = $currspan.offset();
136 position = $currspan.position();
137 width = $currspan.outerWidth();
138 $body = $('body');
139 $mwtext = $('#mw-content-text');
140
141
142 $wrapper.css({
143 top: (offset.top - $wrapper.outerHeight()*0.3) + 'px',
144 });
145 $arrow.css({
146 top: ($wrapper.outerHeight() * 0.3) + 'px',
147 });
148
149 if ((!limitwidth && offset.left > 0.5 * $body.width())
150 || (limitwidth && position.left > 0.5 * $mwtext.width())) {
151 $wrapper.css({
152 right: (($body.width() - offset.left) - 5 + size) + 'px',
153 left: '', // remove other pos to prevent overspecification
154 });
155 $arrow.removeClass('js-tooltip-arrow-pointleft').addClass('js-tooltip-arrow-pointright').css({
156 left: '', // remove other pos to prevent overspecification
157 right: arrpos,
158 'border-left-width': size + 'px',
159 'border-right-width': 0,
160 });
161 if (limitwidth) {
162 $wrapper.css({
163 'max-width': '500px',
164 });
165 }
166 } else {
167 $wrapper.css({
168 left: (offset.left + width - 5 + size) + 'px',
169 right: '', // remove other pos to prevent overspecification
170 });
171 $arrow.removeClass('js-tooltip-arrow-pointright').addClass('js-tooltip-arrow-pointleft').css({
172 right: '', // remove other pos to prevent overspecification
173 left: arrpos,
174 'border-right-width': size + 'px',
175 'border-left-width': 0,
176 });
177 if (limitwidth) {
178 $wrapper.css({
179 'max-width': '500px',
180 });
181 }
182 }
183 };
184
185 // attach resize event
186 $(window).resize(resizeEvent);
187
188 // attach click event to span
189 $span.click(function (event) {
190 //no bubbles
191 event.preventDefault();
192 event.stopPropagation();
193 $this = $(event.currentTarget);
194 if ($this.attr('data-is-currspan') == 'true') {
195 // if the current span is clicked while the popup is open, close the popup
196 $this.removeAttr('data-is-currspan');
197 $currspan = $(null);
198 $wrapper.trigger('js-tooltip-close');
199 } else {
200 // else move and show the currently open popup
201 $currspan = $this;
202 $('.js-tooltip-wrapper').not($wrapper).trigger('js-tooltip-close');
203 $this.attr('data-is-currspan', true);
204 $wrapper.show();
205 resizeEvent();
206 }
207 });
208
209 // add custom style
210 if (typeof style === 'string' && style !== '') {
211 styles = style.split(';');
212 styles_parsed = {};
213 styles.forEach(function(v) {
214 if (typeof v === 'string') {
215 var arr = v.split(':');
216 if (typeof arr[1] === 'string' && arr[1].trim() !== '') {
217 styles_parsed[arr[0].trim()] = arr[1].trim();
218 }
219 }
220 });
221 $wrapper.css(styles_parsed);
222 }
223
224 // finish up
225 $wrapper.hide();
226 $span.show();
227 $wrapper.appendTo($('body'));
228 });
229
230 // close tooltip if clicked outside of
231 $(document).click(function (event) {
232 if ($('.js-tooltip-wrapper:visible').length && !$(event.target).closest('.js-tooltip-wrapper, .js-tooltip-click').length) {
233 $('.js-tooltip-wrapper').trigger('js-tooltip-close');
234 }
235 });
236 })