Create A Simple and Dynamic Tooltip With Svelte and JavaScript

Published: June 19, 2024

Create A Simple and Dynamic Tooltip With Svelte and JavaScript

Background

When you can't show all information on the webpage, it comes in handy to have a tooltip to show additional information on demand.

This could be an info button that looks like this:

Hover over the info button to see what it has to say to you

You can see that hovering over the info button shows some information, which becomes available as you hover over the info button.

Now let's see how we can create a tooltip with JavaScript and Svelte.

Objective

In this blog post, we will create a bare-minimum dynamic tooltip, whose position will be adjusted depending on the mouse's location, with a simple syntax using JavaScript and Svelte.

The tooltip should also support HTML, as we'd like to show rich content in the tooltip, such as titles and images.

Method

First, add an element which you'd like to show a tooltip when hovered:

<Info size={50} color="#0ea5e9" />

Currently this button is dead. Now, let's develop the backend for enabling tooltip:

<script>
// OnMount is needed to find all tooltips once the component is mounted
import { onMount } from 'svelte';

// info: the tooltip DOM element (info window)
// container: main container  DOM element in the page
// innerWidth: window innerWidth. Use to calculate the location of info window
// innerHeight: window innerHeight. Use to calculate the location of info window
let info, container, innerWidth, innerHeight;

// showInfo: Used to control whether to show an info
$: showInfo = false;

// Function that controls the location of tooltip based on mouse location
function handleMouseMove(event) {

	// Get mouse location
	let x = event.clientX;
	let y = event.clientY;

	// Get container location
	let rect = container.getBoundingClientRect();

	// If mouse is in the top half of the screen, set its 'top' style, otherwise none
	info.style.top = y < innerHeight / 2 ? y - rect.top + 10 + 'px' : '';

	// If mouse is in the bottom half of the screen, set its 'bottom' style, otherwise none
	info.style.bottom = y < innerHeight / 2 ? '' : rect.bottom - y + 10 + 'px';

	// If mouse is in the left half of the screen, set its 'left' style, otherwise none
	info.style.left = x < innerWidth / 2 ? x - rect.left + 10 + 'px' : '';

	// If mouse is in the left right of the screen, set its 'right' style, otherwise none
	info.style.right = x < innerWidth / 2 ? '' : rect.right - x + 10 + 'px';
}

onMount(() => {

	// Get all elements which require showing a tooltip when hovered
	let tooltips = document.querySelectorAll('[tooltip]');

	for (const element of Array.from(tooltips)) {

		// When hovered, show tooltip
		element.addEventListener('mouseenter', () => {
			showInfo = true;
			info.innerHTML = element.getAttribute('tooltip');
		});

		// When hover is gone, hide the tooltip element
		element.addEventListener('mouseleave', () => {
			showInfo = false;
			info.innerHTML = '';
		});
	}
});

</script>

// Get innerWidth and  innerHeight by binding to window
// Listen to mousemove on the window
<svelte:window bind:innerWidth bind:innerHeight on:mousemove={(e) => handleMouseMove(e)} />

// Overall Container. Bind to container variable.
// This is used to adjust the location of tooltip
<div class="flex gap-4 flex-col relative" bind:this={container}>

	<!-- Tooltip element. It's contents are only assigned when the user hovers over an info button -->
		<div bind:this={info}
		class={`z-50 bg-white px-4  max-w-[500px] py-2 flex absolute rounded overflow-hidden shadow-xl hidden`}></div>

	<!-- Info button. This triggers the tooltip -->
	<div class="inline-block"
		tooltip="<p class='font-bold'>Thanks for checking out this tooltip. Much appreciated.</p>">
		<Info size={50} color="#0ea5e9" />
	</div>
</div>

It's a long code and most of it is boilerplate, so I want to focus on the 3 key parts of the code.

1. Info Element

Info elements are the elements which trigger a tooltip. In our example, it was an Info button.

These elements should have a tooltip attribute. The value of this attribute should be the HTML element that you'd like to show inside the tooltip when this element is hovered.

// Info button. This triggers the tooltip
<div class="inline-block"
	tooltip="<p class='font-bold'>Thanks for checking out this tooltip. Much appreciated.</p>">
	<Info size={50} color="#0ea5e9" />
</div>

2. Event Listeners

Assigning tooltip attribute to all info elements will help us identify them when the component is mounted.

Once all elements with tooltip attribute are identified, we listen to the mouseenter event and show the tooltip. This event also assigns the tooltip's HTML to the value of tooltip attribute.

When mouse leaves the info element, tooltip should be hidden.

This is how we control the visibility of tooltip:

onMount(() => {

	// Get all elements which require showing a tooltip when hovered
	let tooltips = document.querySelectorAll('[tooltip]');

	for (const element of Array.from(tooltips)) {

		// When hovered, show tooltip
		element.addEventListener('mouseenter', () => {
			showInfo = true;
			info.innerHTML = element.getAttribute('tooltip');
		});

		// When hover is gone, hide the tooltip element
		element.addEventListener('mouseleave', () => {
			showInfo = false;
			info.innerHTML = '';
		});
	}
});

3. Tooltip locator

Now that we can show/ hide tooltip when the info element is hovered, now we need to determine where to locate the tooltip.

Below code is responsible for achieving that:

// Function that controls the location of tooltip based on mouse location
function handleMouseMove(event) {

	// Get mouse location
	let x = event.clientX;
	let y = event.clientY;

	// Get container location
	let rect = container.getBoundingClientRect();

	// If mouse is in the top half of the screen, set its 'top' style, otherwise none
	info.style.top = y < innerHeight / 2 ? y - rect.top + 10 + 'px' : '';

	// If mouse is in the bottom half of the screen, set its 'bottom' style, otherwise none
	info.style.bottom = y < innerHeight / 2 ? '' : rect.bottom - y + 10 + 'px';

	// If mouse is in the left half of the screen, set its 'left' style, otherwise none
	info.style.left = x < innerWidth / 2 ? x - rect.left + 10 + 'px' : '';

	// If mouse is in the left right of the screen, set its 'right' style, otherwise none
	info.style.right = x < innerWidth / 2 ? '' : rect.right - x + 10 + 'px';
}

Above code is smart, beacuse it divides the screen into 4 quadrants and shows the tooltip accordingly, so that it doesn't bleed out from the screen.

Hover over below quadrants and see how this works in real life:

Quadrant 1
Quadrant 2
Quadrant 3
Quadrant 4

You can see that depending on the location of the info element, we smartly locate the tooltip. Works like charm doesn't it?

Conclusion

In this blogpost, we have developed a tooltip using Svelte and JavaScript.

Our solution only requires you to add tooltip attribute to the elements that you'd like to have tooltip on. The value of this attribute will be displayed in the tooltop.

Our solution accepts HTML and it smartly locates the tooltip so that it doesn't bleed out of screen.

Any questions? Let me know below.

Until further notice, happy hacking!

Leave comment

Comments

Check out other blog posts

Create an Interactive Map of Tokyo with JavaScript

2024/06/17

Create an Interactive Map of Tokyo with JavaScript

SvelteSVGJavaScriptTailwindInteractive MapTokyoJapanTokyo Metropolitan Area23 Wards
How to Easily Fix Japanese Character Issue in Matplotlib

2024/06/14

How to Easily Fix Japanese Character Issue in Matplotlib

MatplotlibGraphChartPythonJapanese charactersIssueBug
Book Review | Talking to Strangers: What We Should Know about the People We Don't Know by Malcolm Gladwell

2024/06/13

Book Review | Talking to Strangers: What We Should Know about the People We Don't Know by Malcolm Gladwell

Book ReviewTalking to StrangersWhat We Should Know about the People We Don't KnowMalcolm Gladwell
Most Commonly Used 3,000 Kanjis in Japanese

2024/06/07

Most Commonly Used 3,000 Kanjis in Japanese

Most CommonKanji3000ListUsage FrequencyJapaneseJLPTLanguageStudyingWordsKanji ImportanceWord Prevalence
Replace With Regex Using VSCode

2024/06/07

Replace With Regex Using VSCode

VSCodeRegexFindReplaceConditional Replace
Do Not Use Readable Store in Svelte

2024/06/06

Do Not Use Readable Store in Svelte

SvelteReadableWritableState ManagementStoreSpeedMemoryFile Size
Increase Website Load Speed by Compressing Data with Gzip and Pako

2024/06/05

Increase Website Load Speed by Compressing Data with Gzip and Pako

GzipCompressionPakoWebsite Load SpeedSvelteKit
Find the Word the Mouse is Pointing to on a Webpage with JavaScript

2024/05/31

Find the Word the Mouse is Pointing to on a Webpage with JavaScript

JavascriptMousePointerHoverWeb Development
Create an Interactive Map with Svelte using SVG

2024/05/29

Create an Interactive Map with Svelte using SVG

SvelteSVGInteractive MapFront-end
Book Review | Originals: How Non-Conformists Move the World by Adam Grant & Sheryl Sandberg

2024/05/28

Book Review | Originals: How Non-Conformists Move the World by Adam Grant & Sheryl Sandberg

Book ReviewOriginalsAdam Grant & Sheryl SandbergHow Non-Conformists Move the World
How to Algorithmically Solve Sudoku Using Javascript

2024/05/27

How to Algorithmically Solve Sudoku Using Javascript

Solve SudokuAlgorithmJavaScriptProgramming
How I Increased Traffic to my Website by 10x in a Month

2024/05/26

How I Increased Traffic to my Website by 10x in a Month

Increase Website TrafficClicksImpressionsGoogle Search Console
Life is Like Cycling

2024/05/24

Life is Like Cycling

CyclingLifePhilosophySuccess
Generate a Complete Sudoku Grid with Backtracking Algorithm in JavaScript

2024/05/19

Generate a Complete Sudoku Grid with Backtracking Algorithm in JavaScript

SudokuComplete GridBacktracking AlgorithmJavaScript
Why Tailwind is Amazing and How It Makes Web Dev a Breeze

2024/05/16

Why Tailwind is Amazing and How It Makes Web Dev a Breeze

TailwindAmazingFront-endWeb Development
Generate Sitemap Automatically with Git Hooks Using Python

2024/05/15

Generate Sitemap Automatically with Git Hooks Using Python

Git HooksPythonSitemapSvelteKit
Book Review | Range: Why Generalists Triumph in a Specialized World by David Epstein

2024/05/14

Book Review | Range: Why Generalists Triumph in a Specialized World by David Epstein

Book ReviewRangeDavid EpsteinWhy Generalists Triumph in a Specialized World
What is Svelte and SvelteKit?

2024/05/13

What is Svelte and SvelteKit?

SvelteSvelteKitFront-endVite
Internationalization with SvelteKit (Multiple Language Support)

2024/05/12

Internationalization with SvelteKit (Multiple Language Support)

InternationalizationI18NSvelteKitLanguage Support
Reduce Svelte Deploy Time With Caching

2024/05/11

Reduce Svelte Deploy Time With Caching

SvelteEnhanced ImageCachingDeploy Time
Lazy Load Content With Svelte and Intersection Oberver

2024/05/10

Lazy Load Content With Svelte and Intersection Oberver

Lazy LoadingWebsite Speed OptimizationSvelteIntersection Observer
Find the Optimal Stock Portfolio with a Genetic Algorithm

2024/05/10

Find the Optimal Stock Portfolio with a Genetic Algorithm

Stock marketPortfolio OptimizationGenetic AlgorithmPython
Convert ShapeFile To SVG With Python

2024/05/09

Convert ShapeFile To SVG With Python

ShapeFileSVGPythonGeoJSON
Reactivity In Svelte: Variables, Binding, and Key Function

2024/05/08

Reactivity In Svelte: Variables, Binding, and Key Function

SvelteReactivityBindingKey Function
Book Review | The Art Of War by Sun Tzu

2024/05/07

Book Review | The Art Of War by Sun Tzu

Book ReviewThe Art Of WarSun TzuThomas Cleary
Specialists Are Dead. Long Live Generalists!

2024/05/06

Specialists Are Dead. Long Live Generalists!

SpecialistGeneralistParadigm ShiftSoftware Engineering
Analyze Voter Behavior in Turkish Elections with Python

2024/05/03

Analyze Voter Behavior in Turkish Elections with Python

TurkeyAge Analysis2018 ElectionsVoter Behavior
Create Turkish Voter Profile Database With Web Scraping

2024/05/01

Create Turkish Voter Profile Database With Web Scraping

PythonSeleniumWeb ScrapingTurkish Elections
Make Infinite Scroll With Svelte and Tailwind

2024/04/30

Make Infinite Scroll With Svelte and Tailwind

SvelteTailwindInfinite ScrollFront-end
How I Reached Japanese Proficiency In Under A Year

2024/04/29

How I Reached Japanese Proficiency In Under A Year

JapaneseProficiencyJLPTBusiness
Use-ready Website Template With Svelte and Tailwind

2024/04/25

Use-ready Website Template With Svelte and Tailwind

Website TemplateFront-endSvelteTailwind
Lazy Engineers Make Lousy Products

2024/01/29

Lazy Engineers Make Lousy Products

Lazy engineerLousy productStarbucksSBI
On Greatness

2024/01/28

On Greatness

GreatnessMeaning of lifeSatisfactory lifePurpose
Converting PDF to PNG on a MacBook

2024/01/28

Converting PDF to PNG on a MacBook

PDFPNGMacBookAutomator
Recapping 2023: Compilation of 24 books read

2023/12/31

Recapping 2023: Compilation of 24 books read

BooksReading2023Reflections
Create a Photo Collage with Python PIL

2023/12/30

Create a Photo Collage with Python PIL

PythonPILImage ProcessingCollage
Detect Device & Browser of Visitors to Your Website

2024/01/09

Detect Device & Browser of Visitors to Your Website

JavascriptDevice DetectionBrowser DetectionWebsite Analytics
Anatomy of a ChatGPT Response

2024/01/19

Anatomy of a ChatGPT Response

ChatGPTLarge Language ModelMachine LearningGenerative AI