<div class="parent">
    <div class="child"></div>
    <div class="highlight"></div>
    <pre class="output"></pre>
</div>
body {
    margin: 0;
    cursor: crosshair;
    overflow: hidden auto;
}

.parent {
    min-height: 280px;
    margin: 80px 160px;
    border: 4px dashed rgb(128 214 255 / 80%);
    display: flex;
    align-items: center;
    background-color: rgb(128 214 255 / 50%);
}

.child {
    position: absolute;
    width: 120px;
    height: 60px;
    background-color: rgb(70 1 255 / 75%);
    transform: translate(-50%, -50%);
}

.highlight {
    position: absolute;
    outline: 2px solid #ff6300;
    transition: opacity 0.25s ease-in-out;
}

.output {
    padding: 16px;
    margin: 0;
    font-size: 16px;
}
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');
const output = document.querySelector('.output');
const highlight = document.querySelector('.highlight');

const clamp = (min, max, value) => Math.max(min, Math.min(max, value));

const update = () => {
    const parentRect = parent.getBoundingClientRect();
    const childRect = child.getBoundingClientRect();
    const top = clamp(parentRect.top, parentRect.bottom, childRect.top);
    const right = clamp(parentRect.left, parentRect.right, childRect.right);
    const bottom = clamp(parentRect.top, parentRect.bottom, childRect.bottom);
    const left = clamp(parentRect.left, parentRect.right, childRect.left);
    const width = right - left;
    const height = bottom - top;
    
    const totalArea = childRect.width * childRect.height;
    const intersectionArea = width * height;
    const intersectionRatio = intersectionArea / totalArea;
    
    highlight.style.setProperty('top', `${top + scrollY}px`);
    highlight.style.setProperty('left', `${left + scrollX}px`);
    highlight.style.setProperty('width', `${width}px`);
    highlight.style.setProperty('height', `${height}px`);
    highlight.style.setProperty('opacity', intersectionRatio === 0 ? 0.5 : 1);
    
    output.textContent = JSON.stringify({
        actual: {
            width: childRect.width,
            height: childRect.height
        },
        intersection: {
            width,
            height
        },
        ratio: Number(intersectionRatio.toPrecision(3))
    }, null, 2);
};

window.addEventListener('DOMContentLoaded', () => {
    const parentRect = parent.getBoundingClientRect();
    
    child.style.setProperty('top', `${parentRect.top}px`);
    child.style.setProperty('left', `${parentRect.left + parentRect.width / 2}px`);
    
    update();
});

window.addEventListener('mousemove', (event) => {
    child.style.setProperty('top', `${event.pageY}px`);
    child.style.setProperty('left', `${event.pageX}px`);
    
    update();
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.