saber - Desplazarse al elemento solo si no está a la vista-jQuery
ventana modal jquery ejemplo (7)
Sé que se ha preguntado una variación sobre esto varias veces; He estado navegando SO por un tiempo, pero o bien estoy haciendo algo mal o no he encontrado lo que necesito.
Tengo una estructura de comentarios anidados, más o menos lo mismo que el plugin Comentarios de Facebook , y cada vez que se hace clic en la reply
aparece un pequeño formulario con texto y un botón en la parte inferior de los comentarios.
Una vez más, el comportamiento es el mismo que el plugin Comentarios de Facebook y quiero lograr lo mismo cuando se trata de desplazar el área de texto agregada recientemente a la vista.
He probado el complemento scrollTo y funciona sin problemas, pero, incluso si me desplazo manualmente hasta el final de la página, la animación de desplazamiento siempre restablecerá la posición de desplazamiento y comenzará desde la parte superior .
Para el registro, así es como llamo scrollTo:
$.scrollTo($(''#addReply_1''), 800);
donde addReply_1
es el div
contiene el formulario. Me he cansado de desplazarme a la forma misma y al área de textarea
. Mismos resultados
¿Hay alguna manera de desplazarse a un elemento solo si todavía no está visible?
He intentado con muchas soluciones ofrecidas en SO, como Desplazarme a un elemento usando jQuery , pero ninguna parece comportarse como se desea; Incluso Desplácese a un elemento particular con jQuery o Compruebe si el elemento está visible después de desplazarse muestra el mismo comportamiento "nervioso".
ACTUALIZACIÓN: demostración en línea para mostrar el comportamiento
Subí una página demo en HTML que muestra el comportamiento del que me quejo: http://www.wouldbebetter.com/demo/comment-demo.htm
Solo desplácese hasta la parte inferior de la página y haga clic en cualquiera de los enlaces de Reply
para ver el desplazamiento "brusco" al que me refiero.
Tenga en cuenta que esta demostración usa el plugin scrollintoview
de la respuesta de @Robert Koritnik, pero el comportamiento es el mismo si utilizo, por ejemplo, ScrollTo.
JQuery no es necesario para esto.
Esta función solo muestra el elemento especificado:
function scrollIntoView(elm) {
if(elm) {
let bnd=elm.getBoundingClientRect();
if (bnd.top<0 ) { elm.scrollIntoView(true ); }
else if(bnd.bottom>window.innerHeight) { elm.scrollIntoView(bnd.top<=0); }
}
return elm;
}
Si bien esta función más capaz permite visualizar a la vista un contenedor del elemento deseado, pero proporciona la capacidad de garantizar fácilmente que todo el contenedor se haga visible, de modo que se eviten las imágenes parcialmente ocluidas.
Esto hace que sea fácil, por ejemplo, mostrar esto cuando se desplaza hacia arriba:
en lugar de esto:
/**
* Scroll the specified element into view, optionally first searching for a specific container and
* first making that visible. This function does it''s best to scroll the entire container into view
* but ultimately ensures that as much of the element as fits in the viewport will be visible.
*
* #### Arguments:
*
* elm (DOMElement) The element to make visible.
* contag (string) Optional name of a container tag. Ignored if blank/null/omitted.
* conprp (string) Optional name of a container property to also match. Ignored if blank/null/omitted.
* conval (string) Optional value of the container property. Ignored if `conprp` is not supplied; defaults to "" if omitted.
*/
function scrollIntoView(elm,contag,conprp,conval) {
if(elm) {
if(contag || conprp) {
let con;
if(conval==null) { conval=""; }
for(con=elm; con!=null && con.tagName!="BODY"; con=con.parentNode) {
if((!contag || contag==con.tagName) && (!conprp || con[conprp]==conval)) {
break; // matched container tag and property
}
}
if(con) { scrollIntoView(con); }
}
let bnd=elm.getBoundingClientRect();
if (bnd.top<0 ) { elm.scrollIntoView(true ); }
else if(bnd.bottom>window.innerHeight) { elm.scrollIntoView(bnd.top<=0); }
}
return elm;
}
Modifiqué un poco la respuesta de @iandisme y la envolví como un pequeño complemento jquery:
(function ($) {
''use strict'';
$.fn.scrollToSimple = function ($target) {
var $container = this.first(); // Only scrolls the first matched container
var pos = $target.position(), height = $target.outerHeight();
var containerScrollTop = $container.scrollTop(), containerHeight = $container.height();
var top = pos.top + containerScrollTop; // position.top is relative to the scrollTop of the containing element
var paddingPx = containerHeight * 0.15; // padding keeps the target from being butted up against the top / bottom of the container after scroll
if (top < containerScrollTop) { // scroll up
$container.scrollTop(top - paddingPx);
}
else if (top + height > containerScrollTop + containerHeight) { // scroll down
$container.scrollTop(top + height - containerHeight + paddingPx);
}
};
})(jQuery);
Eliminé las llamadas a .animate porque estaba buscando un desplazamiento instantáneo. También agregué la capacidad de desplazar cualquier contenedor (desplazable), en lugar de solo la ventana. Ejemplo de uso:
// scroll the window so #target is visible
$(window).scrollToSimple( $("#target") );
// scroll the scrollable element #container so that #target is visible
$("#container").scrollToSimple( $("#target") );
No sé si entendí lo que quieres, pero ver si esto es así, cerca o si estoy totalmente perdido:
<!DOCTYPE html>
<html>
<head>
<title>Scroll To Reply</title>
<meta charset="utf-8" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var $contextarea = $(''#contextform textarea'');
$(''a.reply'').live(''click'',function(){
$(this).closest(''p'').css({''margin-top'':''300px;''});
$(''#contextform'').appendTo($(this).closest(''p''));
$(''#contextform'').slideDown(1000);//.css({''display'':''block''});
$(this).closest(''p'').attr(''id'',''scrolltome'');
$(''html,body'').animate({slideDown: $(''#scrolltome'').offset().top}, 2000);
});
$(''.sendreply'').live(''click'',function(){
if($contextarea.val() == null || $contextarea.val() == '''') {
alert(''textarea is empty!'');
} else {
$(''#contextform'').slideUp(800);
}
});
// $(''button'').click(function() {
// $(''html,body'').animate({scrollTop: $(''#body'').offset().top}, 2000);//scrolls to the top
// });
});
</script>
<style type="text/css">
body{
font-size:25px;
font-family: ''Arimo'', Arial, sans-serif;
}
#contextform {
display:none;
width:500px;
height: 150px;
background: #0489B7;
padding: 5px
}
#contextform textarea {
width: 490px;
height: 90px;
}
</style>
</head>
<body id="body">
<h1>Scroll to reply</h1>
<div id="contextform">
<form method="post" action="">
<textarea id="textarea"></textarea>
<button type="button" class="sendreply">Send Reply</button>
</form>
<a name="myAnchor" id="myAnchor">anchor</a>
</div>
<ol>
<?php for ($i = 0; $i < 20; $i++) { ?>
<li><p>The quick brown fox jumps over the lazy dog
<a href="#scrolltome" class="reply">Reply</a>
</p></li>
<?php } ?>
</ol>
</body>
</html>
Todos los navegadores modernos lo admiten. Visita: http://caniuse.com/#search=scrollIntoView
function scrollIntoViewIfNeeded(target) {
var rect = target.getBoundingClientRect();
if (rect.bottom > window.innerHeight) {
target.scrollIntoView(false);
}
if (rect.top < 0) {
target.scrollIntoView();
}
}
Actualizar
El objetivo debe ser un Elemento. Si usa jQuery llame a la función de esta manera:
scrollIntoViewIfNeeded($(".target")[0]);
Tuve el mismo problema ... después de revisar algunas respuestas, esto es lo que se me ocurrió para hacer esto ... sin quitar otro complemento.
function scrollIntoViewIfNeeded($target) {
if ($target.position()) {
if ($target.position().top < jQuery(window).scrollTop()){
//scroll up
$(''html,body'').animate({scrollTop: $target.position().top});
}
else if ($target.position().top + $target.height() >
$(window).scrollTop() + (
window.innerHeight || document.documentElement.clientHeight
)) {
//scroll down
$(''html,body'').animate({scrollTop: $target.position().top -
(window.innerHeight || document.documentElement.clientHeight)
+ $target.height() + 15}
);
}
}
}
El "15" en la última línea es solo un relleno extra, es posible que deba ajustarlo o agregarlo a la línea de desplazamiento hacia arriba.
EDITAR: window.innerHeight
modificado (window.innerHeight || document.documentElement.clientHeight)
para IE <8 soporte
para asegurarse de que elem esté a la vista dentro de un contenedor:
let rectElem = elem.getBoundingClientRect(), rectContainer=container.getBoundingClientRect();
if (rectElem.bottom > rectContainer.bottom) elem.scrollIntoView(false);
if (rectElem.top < rectContainer.top) elem.scrollIntoView();
Sí, hay un complemento jQuery que se desplaza a un elemento solo si no está dentro de los límites visibles del antecesor desplazable. He escrito que uno hace exactamente lo que necesita. Y probablemente le resulte más fácil de usar en comparación con scrollTo()
ya que solo debe proporcionar el elemento que le gustaría ver.
Podría copiar y pegar el código aquí, pero como agrego algunas adiciones de vez en cuando, es mejor que lo vincule a una publicación de blog donde encontrará todos los detalles relacionados con el desplazamiento programático y el último código de complemento. El desplazamiento programático puede ser bastante molesto para los usuarios y para toda la experiencia de la interfaz de usuario, por lo que supongo que será una lectura interesante.
Uso
El complemento es realmente simple de usar:
$("#ElementToScrollIntoView").scrollintoview();
El complemento busca automáticamente el antecesor desplazable más cercano y lo desplaza en consecuencia (si es necesario). Hay algunas configuraciones adicionales para este complemento que puede usar y así es como se ven:
scrollintoview: function (options) {
/// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary>
/// <param name="options" type="Object">Additional options that can configure scrolling:
/// duration (default: "fast") - jQuery animation speed (can be a duration string or number of milliseconds)
/// direction (default: "both") - select possible scrollings ("vertical" or "y", "horizontal" or "x", "both")
/// complete (default: none) - a function to call when scrolling completes (called in context of the DOM element being scrolled)
/// </param>
/// <return type="jQuery">Returns the same jQuery set that this function was run on.</return>
Estoy usando este complemento en mi sitio Sharepoint 2010 en páginas donde presento datos tabulares largos. Cada vez que agrego un nuevo elemento (fila) a esta tabla, también me desplazo a este nuevo registro y lo destaco, para que los usuarios puedan ver el nuevo registro inmediatamente.
Sharepoint también fue la razón por la cual decidí no proporcionar manualmente el elemento desplazable, sino que lo busqué programáticamente. Sharepoint usa las páginas maestras costumizables del administrador, lo que significa que no sé qué elemento se podrá desplazar en el tiempo de ejecución. Pero sí sé qué elemento quiero ver. De ahí este plugin. También es bastante simplificado en comparación con el complemento scrollTo()
que admite varios escenarios diferentes. La mayoría de las veces, los desarrolladores tienden a usar solo uno (o un número muy limitado de ellos).
Observaciones adicionales
Prevención de procesamiento de clic de enlace predeterminado
Usar mis complementos todavía lo hace bastante problemático, ya que hay algunos parpadeos al agregar esos cuadros de respuesta. El problema es que el clic de tu enlace realmente se ejecuta. Debe evitar esto para que su página funcione sin problemas:
configure eventos de clic en sus enlaces de una de estas dos maneras:
<a href="javascript:void AddReplyForm(44); return false;">Reply</a>
o
<a href="#" onclick="void AddReplyForm(44); return false;">Reply</a>
una mejor manera sería ejecutar esto en el documento listo:
$(function() { $("a").click(function(evt) { evt.preventDefault(); }); });
La idea principal es evitar que el navegador procese clics de enlace. Debido a que esto hace que el navegador busque el ancla en la página y, como no puede encontrarla, se desplaza automáticamente hacia arriba. Luego dígale que se desplace a su elemento.
ID duplicados
Cuando crea un formulario de respuesta, agrega elementos nuevos y nuevos y nuevos, pero todos tienen el mismo ID. Deberías evitar hacer esto o usar otros medios. Puede eliminar la necesidad de ID por completo al proporcionar el elemento a su función BindClick()
. La función principal de generación de respuestas podría ser así (esta función está escrita de una manera que elimina por completo la necesidad de ID de elementos):
function AddReplyForm(topCommentID)
{
var el = $(addReplyForm).appendTo(''#comment_'' + topCommentID + '' .right'');
BindClick(el); // mind this !! you provide the element to remove
el.scrollintoview();
}