Always-in-viewport popup tooltips

by Vallek

HTML

<section class="box-item">
  <div class="job__info info visually-hidden">
    <h4>Заголовок</h4>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi tempore eius, iste commodi aliquid deleniti vel corporis quaerat perspiciatis aut dolor tempora voluptatem, consectetur tenetur. Incidunt hic eveniet nam saepe.</p>
  </div>
</section>
<section class="box-item">
  <div class="job__info info visually-hidden">
    <h4>Заголовок</h4>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi tempore eius, iste commodi aliquid deleniti vel corporis quaerat perspiciatis aut dolor tempora voluptatem, consectetur tenetur. Incidunt hic eveniet nam saepe.</p>
  </div>
</section>
<section class="box-item">
  <div class="job__info info visually-hidden">
    <h4>Заголовок</h4>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi tempore eius, iste commodi aliquid deleniti vel corporis quaerat perspiciatis aut dolor tempora voluptatem, consectetur tenetur. Incidunt hic eveniet nam saepe.</p>
  </div>
</section>

CSS

body {
  display: flex;
  justify-content: space-between;
}

.box-item {
	display: flex;
	flex-direction: column;
	position: relative;
	width: 150px;
	height: 150px;
	outline: 1px solid #e2e2e2;
  background-color: rgb(240, 104, 41);
}

.box-item:nth-of-type(2) {
  width: 300px;
}

.info {
	display: block;
	visibility: hidden;
	position: absolute;
	z-index: 0;
	padding: 1rem 1rem 0;
	box-shadow: 5px 3px 10px -3px rgb(56, 56, 56);
	background-color: #fff;
	width: 150px;
}

/* JS */
.visually-hidden {
	border: 0;
	clip: rect(0 0 0 0);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
}

.out-of-viewport {
	visibility: visible;
	right: 0;
	z-index: 1;
	transition: transform 0.2s ease-in,
	z-index 0.1s ease-in 0.2s;
	box-shadow: -5px 3px 10px -3px rgb(56, 56, 56);
}

.inside-viewport {
	position: absolute;
	visibility: visible;
	left: 0;
	z-index: 1;
	transition: transform 0.2s ease-in,
	z-index 0.1s ease-in 0.2s;
}

JavaScript

let hovered = document.querySelectorAll('.box-item');
let page = document.querySelector('.page');

function remove() {	
				target.classList.add('visually-hidden');	
			}

hovered.forEach(
	function doit(el) {
		
		let target = el.querySelector('.info');
		let heading = el.querySelector('.box-item__heading');
		el.addEventListener('mouseenter', isInViewport, { passive: true });

		function isInViewport() {
			let boxSide = el.offsetWidth;
			let rightSide = window.innerWidth - el.getBoundingClientRect().right;
			let leftSide = el.getBoundingClientRect().left;
			let viewportWidth = window.innerWidth;
			if (
				target !== null &&
				rightSide >= boxSide &&
				viewportWidth >= 500
			) {
				target.classList.remove('visually-hidden');
				target.classList.add('inside-viewport');
				target.style.left = boxSide + 'px';
			}
			else {
				if (
					target !== null &&
					leftSide >= boxSide &&
					viewportWidth >= 500
					) {
					target.classList.remove('visually-hidden');
					target.classList.add('out-of-viewport');
					target.style.right = boxSide + 'px';
				}
				else {
					return;
				} 
			}	
		}
		
		el.addEventListener('mouseleave', isOutViewport, { passive: true });
		function isOutViewport() {
			if (target !== null) {
				target.classList.remove('out-of-viewport');
				target.classList.remove('inside-viewport');
				target.classList.add('visually-hidden');
			}
			else {
				return;
			} 
		}
		if (target !== null) {
			let removeIt = function remove() {	
				target.classList.add('visually-hidden');	
			};
			target.addEventListener('mouseover', removeIt);
		}
		else {
			return;
		}	
	}
);