Estoy trabajando en un cuadro emergente que muestra un texto a cierta posición de un elemento cuando el cursor pasa sobre él. Puede ver una demostración simplificada aquí: https://jsfiddle.net/xsvm2Lur/61/
En este momento, el cuadro emergente se aplastará cuando esté cerca del cuadro delimitador. Quiero que la ventana emergente aparezca en la parte inferior izquierda (si la ventana emergente se desbordará hacia la derecha) o en la parte superior izquierda (si la ventana emergente se desbordará hacia abajo y hacia la derecha) de la posición del elemento sobre el que se desplaza si la ventana emergente se desbordará.
La posición y el texto que se mostrarán se generan dinámicamente (por ejemplo, no lo sé antes de renderizar).
En este momento tengo una versión de trabajo usando Javascript. La forma en que lo hago es:
textLength . Esto se establecerá como el ancho de la ventana emergente para que todo el texto entre en 1 línea.textLength + x posición del cursor > ancho del cuadro, "invierte" el cuadro emergente en el eje x deduciendo el valor "izquierdo" del elemento emergente por textLength y a cierta distancia del elemento.La solución funciona, pero me pregunto si hay una mejor manera de hacerlo sin contar caracteres, o si hay otra forma de hacerlo elegantemente, ¿quizás CSS solo sin Javascript?
Lamentablemente, no creo que haya una manera de hacerlo solo con CSS. Sin embargo, al trabajar en su violín, logré agregar la funcionalidad que deseaba.
La forma en que lo hice fue simplemente incluir una referencia al contenedor y verificar si la posición y el tamaño de la ventana emergente estaban dentro del contenedor BoundingClientRect.
Este es el código actualizado para la función popupShow:
const showPopup = (top, left, text, container) => { popup.textContent = text; const containerBCR = container.getBoundingClientRect(); const popupBCR = popup.getBoundingClientRect(); const popupWidth = popupBCR.width, popupHeight = popupBCR.height; let popupTop = top + 20, popupLeft = left + 20, newPopupWidth; console.log("height: ", popupHeight); console.log("top: ", top); console.log("bottomPopup: ", top + 20 + popupHeight); console.log("bottomBoundary", containerBCR.bottom); if (left + 20 + popupWidth > containerBCR.right) { popupLeft = left - popupWidth; if (popupLeft < containerBCR.left) { popupLeft = containerBCR.left; newPopupWidth = left - containerBCR.left; } } if (top + 20 + popupHeight > containerBCR.bottom) { popupTop = top - popupHeight; if (popupTop < containerBCR.top) { popupTop = containerBCR.top; } } popup.style.top = popupTop + "px"; popup.style.left = popupLeft + "px"; popup.style.width = newPopupWidth; popup.style.visibility = 'visible'; }Como puede ver, también edité la ventana emergente para usar "visibilidad: oculta" en lugar de "pantalla: ninguna". Esto se debe a que si la pantalla está configurada en "ninguno", no podremos obtener su tamaño (aunque puede haber soluciones para esto).
Intenta revisar el violín actualizado y dime lo que piensas.
Empujé un círculo un poco más abajo porque el código actualmente no verifica el relleno de la ventana emergente, por lo que se desbordaba un poco (unos pocos píxeles).
Esto se basa en cuadrantes, calcula de manera simple si tenemos más del 50% de ancho y/o alto y cambia el estilo para usar el derecho o el inferior en su lugar. A esto no le importa el contenido de la ventana emergente, no se requiere medir.
const popup = document.getElementById("pop-up") const parsePx = (px) => parseFloat(px.slice(0, -2)) const showPopup = (text, position) => { popup.textContent = text; popup.style.top = position.top; popup.style.left = position.left; popup.style.right = position.right; popup.style.bottom = position.bottom; popup.style.display = 'inline-block'; } const hidePopup = () => { popup.style.display = 'none'; } const circles = document.querySelectorAll(".red-circle") circles.forEach(el => el.addEventListener('mouseover', (e) => { const hoveredEl = e.target; const textContent = hoveredEl.getAttribute('data-content'); //get absolute position of elements let elBounds = hoveredEl.getBoundingClientRect(); //get absolute position of parent; let ctBounds = popup.parentElement.getBoundingClientRect(); //calculate relative positions let left = elBounds.left - ctBounds.left + (elBounds.width / 2), top = elBounds.top - ctBounds.top + (elBounds.height / 2), width = ctBounds.width, height = ctBounds.height //prepare position settings let position = { left: "auto", top: "auto", bottom: "auto", right: "auto" } //calculate if we're over 50% of box size if(top>ctBounds.height/2) position.bottom = ctBounds.height - top + 20 + 'px'; else position.top = top + 20 + 'px'; if(left>ctBounds.width/2) position.right = ctBounds.width - left + 20 + 'px'; else position.left = left + 20 + 'px'; showPopup(textContent, position); })) circles.forEach(el => el.addEventListener('mouseout', (e) => { hidePopup() })) .container { width: 200px; height: 200px; border: 1px solid black; position: relative;} .red-circle { border-radius: 50%; background: red; width: 20px; height: 20px; position: absolute;} #pop-up { background-color: #EFEFEF; padding: 0.25em; position: absolute;} <div class="container"> <div style="top:20px;left:20px;" class="red-circle" data-content="This is a red circle"></div> <div style="top:10px;left:150px;" class="red-circle" data-content="This is the top-right red circle"></div> <div style="top:140px;left:150px;" class="red-circle" data-content="This is the bottom-right red circle"></div> <div style="top:140px;left:15px;" class="red-circle" data-content="This is the bottom-left red circle"></div> <span style="display:hidden" id="pop-up"></span> </div>