JSFiddle - React, Tailwind, and code Playground

by Alexandru Gatea

HTML

<div class="grid">
  <div class="row">
    <div class="el" data-tooltip="Tooltip Text"></div>
    <div class="el" data-tooltip="Tooltip Text that's a bit longer"></div>
		<div class="el" data-tooltip="Tooltip"></div>
  </div>
  <div class="row">
    <div class="el" data-tooltip="Tooltip Text"></div>
		<div class="el" data-tooltip="Tooltip Text that's"></div>
    <div class="el" data-tooltip="Tooltip Text that's a bit longer"></div>
  </div>
  <div class="row">
    <div class="el" data-tooltip="Tooltip Text that's a bit longer"></div>
    <div class="el" data-tooltip="Tooltip Text"></div>
		<div class="el" data-tooltip="Tooltip"></div>
  </div>
</div>

<div id="tooltip">
	<div class="tooltip-head"></div>
	<div class="tooltip-body">
		<div class="tooltip-graphic"></div>
		<div class="tooltip-text"></div>
	</div>
</div>

SCSS

* {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}


//Vars

$buttonSize: 40;
$toltipHandleSize: 10;


$tooltipOffset: $buttonSize + $toltipHandleSize;
// Vars end

body {
	width: 100%;
	height: 100vh; 
	overflow: hidden;
}

@function rem($value) {
    $remValue: ($value / 16) + rem; 
    @return $remValue;
}

.el {
	width: rem($buttonSize);
	height: rem($buttonSize);
	background: #212121;
	border-radius: rem(4);
	
}


.grid {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	position: fixed;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	padding: rem(40);
	overflow: hidden;
}

.row {
	display: flex;
	align-items: space-between;
	justify-content: space-between;
}


#tooltip {
	position: fixed;
	background: #212121;
	max-width: rem(200);
	padding: rem(20);
	border-radius: rem(8);
	
	.tooltip-text {
		color: #fff;
		text-align: center;
	}
	
	&:not([data-pos-x]) {
		display: none;
	}
	
	&:before {
		content: "";
		display: block;
		width: 0;
		height: 0;
		border-style: solid;
		border-width: 10px 10px 10px 0;
		border-color: transparent #212121 transparent transparent;
		position: absolute;
		top: $buttonSize / 2 - 10px;
		left: -9px;
	}
}

$i: rem($buttonSize);
$halfi: rem($buttonSize / 2);
$j: rem($toltipHandleSize);

#tooltip[data-pos-x="left"][data-pos-y="top"] {
	transform-origin: left top;
	transform: translate(rem($tooltipOffset), 0);
}

#tooltip[data-pos-x="center"][data-pos-y="top"] {
	trasnform-origin: center top;
	transform: translate(calc(#{$halfi} - 50%), rem($tooltipOffset));
	&:before {
		top: -9px;
		left: calc(50% - 10px);
		border-width: 0 10px 10px 10px;
		border-color: transparent transparent #212121 transparent;
	}
}

#tooltip[data-pos-x="right"][data-pos-y="top"] {
	transform-origin: right top;	
	transform: translate(calc(#{$i} - 100% ), rem($tooltipOffset) );
	
	&:before {
		top: -9px;
		left: calc(100% - 10px - 20px);
		border-width: 0 10px 10px 10px;
		border-color: transparent transparent #212121...

JavaScript

// Define the tooltip container, 
// that will be displayed on hover
let tooltipContainer = document.getElementById("tooltip");

// Collect the Child of the tooltip 
// container that will store the text
let tooltipTextContainer = tooltipContainer.querySelector(".tooltip-text");

// Collect the tooltip triggers, 
// that upon hover will trigger
// tooltip display
let tooltipTriggers = document.querySelectorAll("[data-tooltip]");

// An offset that will be used to 
// determine where tp position the tooltip, 
//  around its trigger element
const tooltipOffset = 300;

// an extra offset for placing the tooltip 
// a bit further from trigger
const tooltipHandleSize = 20;
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;


// Add mouseover/mouseout events for each element that has a tooltip text
tooltipTriggers.forEach((e) => {
  let showToltipFor = e;

  showToltipFor.addEventListener("mouseover", () => {
    showTooltip(showToltipFor);

  });

  showToltipFor.addEventListener("mouseout", () => {
    hideTooltip();
  });

});

function showTooltip(el) {

  // get the position on screen
  let triggerScreenPosX = el.getBoundingClientRect().left;
  let triggerScreenPosY = el.getBoundingClientRect().top;


  // get size of element
  // will be used to position the tooltips
  let triggerWidth = el.offsetWidth;
  let triggerHeight = el.offsetHeight;


  let tooltipText = el.getAttribute("data-tooltip");

  tooltipTextContainer.innerHTML = tooltipText;

  tooltipContainer.style.left = triggerScreenPosX + "px";
	tooltipContainer.style.top  = triggerScreenPosY + "px";
	
  // Check where to place tooltips
  // based on trigger's position on screen

  // check on X axis
  if (triggerScreenPosX < tooltipOffset) {
    tooltipContainer.dataset.posX = "left"; // el is closer to left edge
  } else if ((screenWidth - triggerScreenPosX) < tooltipOffset) {
    // el is closer to right edge
    tooltipContainer.dataset.posX = "right";
  } else {
    // we...