2012-04-19 16 views
17

¿Alguien sabe de una extensión del componente popover de twitter bootstrap que cambia dinámicamente la opción de ubicación para asegurarse de que el popover se muestre en la pantalla?¿Cómo se puede asegurar de que las ventanas de arranque de twitter popover estén visibles?

+0

posible duplicado de [Twitter colocación tema de arranque popover] (http://stackoverflow.com/questions/8839387/twitter-bootstrap-popover-placement-issue) No es un duplicado exacto, pero lo suficientemente cerca a la materia. el resto del problema es una cuestión de matemática. –

Respuesta

24

La ubicación puede ser una función en lugar de una cadena. Un ejemplo de la colocación de auto escrito por grasa y luego portado a la versión más reciente de arranque por wleeper se encuentra en uno de los temas de GitHub en el proyecto aquí: https://github.com/twitter/bootstrap/issues/345

Aquí es el resultado de la compilación de la CoffeeScript a JavaScript:

$("a[rel=popover]").popover({ 
    placement: function(tip, element) { 
    var $element, above, actualHeight, actualWidth, below, boundBottom, boundLeft, boundRight, boundTop, elementAbove, elementBelow, elementLeft, elementRight, isWithinBounds, left, pos, right; 
    isWithinBounds = function(elementPosition) { 
     return boundTop < elementPosition.top && boundLeft < elementPosition.left && boundRight > (elementPosition.left + actualWidth) && boundBottom > (elementPosition.top + actualHeight); 
    }; 
    $element = $(element); 
    pos = $.extend({}, $element.offset(), { 
     width: element.offsetWidth, 
     height: element.offsetHeight 
    }); 
    actualWidth = 283; 
    actualHeight = 117; 
    boundTop = $(document).scrollTop(); 
    boundLeft = $(document).scrollLeft(); 
    boundRight = boundLeft + $(window).width(); 
    boundBottom = boundTop + $(window).height(); 
    elementAbove = { 
     top: pos.top - actualHeight, 
     left: pos.left + pos.width/2 - actualWidth/2 
    }; 
    elementBelow = { 
     top: pos.top + pos.height, 
     left: pos.left + pos.width/2 - actualWidth/2 
    }; 
    elementLeft = { 
     top: pos.top + pos.height/2 - actualHeight/2, 
     left: pos.left - actualWidth 
    }; 
    elementRight = { 
     top: pos.top + pos.height/2 - actualHeight/2, 
     left: pos.left + pos.width 
    }; 
    above = isWithinBounds(elementAbove); 
    below = isWithinBounds(elementBelow); 
    left = isWithinBounds(elementLeft); 
    right = isWithinBounds(elementRight); 
    if (above) { 
     return "top"; 
    } else { 
     if (below) { 
     return "bottom"; 
     } else { 
     if (left) { 
      return "left"; 
     } else { 
      if (right) { 
      return "right"; 
      } else { 
      return "right"; 
      } 
     } 
     } 
    } 
    } 
}); 

se está trabajando bien para mí excepto en un caso: si el elemento está en la esquina superior derecha hay ningún buen lugar para el popover a parecer que es una de las opciones y aparece parcialmente fuera la pantalla.

+4

Esta es una buena respuesta, pero no resuelve el problema por completo. El ancho y alto de la información sobre herramientas está codificado en 'actualWidth' y' actualHeight' y no hay forma de obtener dinámicamente el tamaño de la información sobre herramientas antes de que se represente. –

+0

@ BjörnLindqvist Ese es un buen punto. He estado pensando en ello por un tiempo, pero parece un problema difícil de resolver, además de ajustar los valores para que coincidan con su ventana emergente (suponiendo que es un tamaño común). Uno podría representarlo en un iframe oculto o hacer algunos otros hijinks para determinar el tamaño, pero no puedo pensar en una forma limpia de hacerlo. – Cymen

+0

Esta es una gran solución si necesita voltear/reorientar un popover boostrap de twitter para asegurarse de que esté visible. –

12

Para aquellos interesados ​​en una solución que tendrá una ubicación predeterminada (utilizando el atributo data-placement en el elemento), he adaptado la gran respuesta de Cymen.

También me he asegurado de que no se calculen los límites innecesariamente, por lo que debería ser un poco más eficiente.

$('[data-toggle="popover"]').each(function() { 
    var trigger = $(this); 
    trigger.popover({ 
     animation: true, 
     delay: { show: 0, hide: 0 }, 
     html: true, 
     trigger: 'hover focus', 
     placement: getPlacementFunction(trigger.attr("data-placement"), 283, 117) 
    }); 
}); 

var getPlacementFunction = function (defaultPosition, width, height) { 
    return function (tip, element) { 
     var position, top, bottom, left, right; 

     var $element = $(element); 
     var boundTop = $(document).scrollTop(); 
     var boundLeft = $(document).scrollLeft(); 
     var boundRight = boundLeft + $(window).width(); 
     var boundBottom = boundTop + $(window).height(); 

     var pos = $.extend({}, $element.offset(), { 
      width: element.offsetWidth, 
      height: element.offsetHeight 
     }); 

     var isWithinBounds = function (elPos) { 
      return boundTop < elPos.top && boundLeft < elPos.left && boundRight > (elPos.left + width) && boundBottom > (elPos.top + height); 
     }; 

     var testTop = function() { 
      if (top === false) return false; 
      top = isWithinBounds({ 
       top: pos.top - height, 
       left: pos.left + pos.width/2 - width/2 
      }); 
      return top ? "top" : false; 
     }; 

     var testBottom = function() { 
      if (bottom === false) return false; 
      bottom = isWithinBounds({ 
       top: pos.top + pos.height, 
       left: pos.left + pos.width/2 - width/2 
      }); 
      return bottom ? "bottom" : false; 
     }; 

     var testLeft = function() { 
      if (left === false) return false; 
      left = isWithinBounds({ 
       top: pos.top + pos.height/2 - height/2, 
       left: pos.left - width 
      }); 
      return left ? "left" : false; 
     }; 

     var testRight = function() { 
      if (right === false) return false; 
      right = isWithinBounds({ 
       top: pos.top + pos.height/2 - height/2, 
       left: pos.left + pos.width 
      }); 
      return right ? "right" : false; 
     }; 

     switch (defaultPosition) { 
      case "top": 
       if (position = testTop()) return position; 
      case "bottom": 
       if (position = testBottom()) return position; 
      case "left": 
       if (position = testLeft()) return position; 
      case "right": 
       if (position = testRight()) return position; 
      default: 
       if (position = testTop()) return position; 
       if (position = testBottom()) return position; 
       if (position = testLeft()) return position; 
       if (position = testRight()) return position; 
       return defaultPosition; 
     } 
    } 
}; 
+1

Esto fue excelente, y funcionó bastante bien con actualizaciones mínimas. ¡Gracias! – JazzyWhit

+0

funciona con Bootstrap 2 o solo con 3? – isapir

+0

no podría decir, tendrías que probarlo –

Cuestiones relacionadas