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 })