Pregunta ¿Puedes especificar un "target de datos" para Bootstrap que se refiera a un elemento DOM hermano sin usar una ID?


Estoy agregando dinámicamente elementos colapsables a una página. Bootstrap usa el atributo "data-target" (destino de datos) para especificar a qué elemento se aplica la contracción de colapso.

De los documentos:

The data-target attribute accepts a css selector

¿Hay alguna forma de escribir un selector que especifique el siguiente hermano del elemento padre? Todos los ejemplos de los documentos parecen usar selecciones por ID.

Específicamente, el HTML se ve así:

<div class="accordion-group">
<div class="accordion-heading">
  <a class="accordion-toggle" data-toggle="collapse" data-target="#collapseOne">
    Generated Title
  </a>
</div>
<div id="collapseOne" class="accordion-body collapse in">
  <div class="accordion-inner">
    Generated Content... this is big and sometimes needs collapsing
  </div>
</div>
</div>

Me gustaría escribir algo como (pseudo código usando la sintaxis de jquery de forma ilegal):

<a class="accordion-toggle" data-toggle="collapse" data-target="$(this).parent().next()">

Pero estoy empezando a sospechar que esto puede no ser posible con los selectores de CSS.

En este momento, como una solución alternativa, estoy generando una nueva ID (un número creciente agregado a una cadena) cuando creo el elemento.

¿Hay un enfoque más agradable usando un selector? ¿Debería utilizar algún javascript posterior a la creación para establecer el atributo de destino de datos? ¿Generar identificadores para contenido dinámico el enfoque estándar?


32
2017-10-09 18:05


origen


Respuestas:


Si bien es cierto que el selector en una data-target atributo es un selector de jQuery, la especificación api de datos para este complemento no proporciona ningún medio para hacer referencia de nuevo a this en el alcance de la ejecución (ver líneas 147-153 en bootstrap-collapse.js para su uso).

Sin embargo, me gustaría ofrecer otro enfoque alternativo, que es ampliar el api de datos con su propio especificador de alternar personalizado. Llamémoslo colapsar-siguiente.

JS (ver la nota de actualización)

$('body').on('click.collapse-next.data-api', '[data-toggle=collapse-next]', function (e) {
  var $target = $(this).parent().next()
  $target.data('collapse') ? $target.collapse('toggle') : $target.collapse()
})

HTML

<a class="accordion-toggle" data-toggle="collapse-next">

JSFiddle (actualizado)

La desventaja aquí es que es un enfoque bastante estrechamente acoplado, ya que el JS supone una estructura específica para el marcado.


Nota sobre problemas de IE

Como @slhck señaló en su respuesta, IE9 y menores aparentemente fallan en el primer clic cuando utilizo una revisión anterior de mi respuesta. La causa no es un problema de IE en absoluto, sino más bien un Bootstrap. Si uno invoca .collapse('toggle') en un objetivo cuya Carousel el objeto no está inicializado, el toggle() El método se llamará dos veces, una durante la inicialización y otra vez explícitamente después de la inicialización. Esto es definitivamente un error de Bootstrap y con suerte será reparado. La única razón por la que no lo hace Aparecer como un problema en Chrome, FF, IE10, etc., es porque todos son compatibles con las transiciones de CSS, y por lo tanto, cuando se realiza la segunda llamada, produce un cortocircuito porque el primero todavía está activo. La solución alternativa anterior simplemente evita el problema de la doble llamada al verificar primero la inicialización y manejarla de manera diferente.


25
2017-10-09 23:18



La solución de @ merv no funcionó para mí en IE9 y versiones posteriores, ya que el estado plegable no estaba disponible a menos que hicieras clic en cada elemento una vez. Sin embargo, funcionó bien en Firefox y Chrome. Entonces, después de dos clics, todo funcionaría.

Lo que hice fue establecer un .collapse-next clase a los elementos desencadenantes, luego forzar su ul hermanos a colapsar con toggle ajustado a false:

$(".collapse-next").closest('li').each(function(){
  if ($(this).hasClass('active')) {
    // pop up active menu items
    $(this).children("ul").collapse('show');
  } else {
    // just make it collapsible but don't expand
    $(this).children("ul").collapse({ toggle: false });
  }
});

Esto es para realmente alternar el estado del menú:

$('.collapse-next').click(function(e){
  e.preventDefault();
  $(this).parent().next().collapse('toggle');
});

Parece que usar data- atributos es un enfoque un tanto más moderno y más limpio, pero para navegadores antiguos que trabajan con clases y jQuery parece hacer el trabajo también.


4
2018-03-21 14:59



Creo que el mejor enfoque sería hacer esto repetir todo accordion-toggle elementos y establecer su data-target atribuir dinámicamente a través de jquery y luego colocar el código de acordeón mencionado en bootstrap.

Ejemplo: 

$(function(){
    $("a.accordion-toggle").each(function(index) {
        $(this).attr("data-target", "#" + $(this).parent().next().attr("id"));
    });

    // accoridon code
});

Espero que esto ayude


1
2017-10-09 18:22



Sí, puedes hacerlo fácilmente!

Simplemente agregue el siguiente código a sus scripts y disfrute: 

$('body').on('click','[data-toggle="collapse"][data-mytarget^="$("]',function(){
     eval($(this).data('mytarget')).collapse('toggle');
});

Ahora, en cualquier lugar de tu código, puedes establecer el objetivo de colapso mediante un comando jQuery completamente dinámico dentro data-mytarget.

Ahora úsalo así:

<a data-toggle="collapse" data-mytarget="$(this).parent().next()">Link</a>

o

<a data-toggle="collapse" data-mytarget="$('#TopMenuBar').closest('ul')">Link</a>

o

<a data-toggle="collapse" data-mytarget="[ EACH IDEAl JQUERY CODE OF YOU STARTED WITH $( ]">Link</a>

Manifestación:

$('body').on('click','[data-toggle="collapse"][data-mytarget^="$("]',function(){
     eval($(this).data('mytarget')).collapse('toggle');
});
a{
  padding:10px;
  cursor:pointer;
  margin:20px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
  
  
<div class="accordion-group">
    <div class="accordion-heading">
      <a class="accordion-toggle btn btn-info" data-toggle="collapse" data-mytarget="$(this).parent().next()">Collapse It</a>
    </div>
    <div id="collapseOne" class="accordion-body collapse ">
      <div class="accordion-inner">
        Generated Content... this is big and sometimes needs collapsing
      </div>
    </div>
</div>

solía data-mytarget en lugar de data-target. Si utiliza data-target funciona también pero la biblioteca jQuery genera un error como este: Uncaught Error: Syntax error, unrecognized expression: $(this).parent().next().

Así que definí mi propiedad diferente con data-mytarget nombre.


1
2018-06-30 11:58



Sin javascript solución o depende de js bootstrap ya en uso, simplemente explotando la estructura DOM

Ver el data-target=""...

Una pista para una forma fácil de reducir las soluciones voluminosas-

....
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="collapse" data-target=".dropdown-toggle:hover + .more-menu">
    Toggle Bagal Wala
  </button>
  <div class="collapse more-menu">
....

Esta estructura de selección CSS seleccionará el DOM deseado .dropdown-toggle:hover + .more-menu y allí podemos aplicar nuestro CSS deseado.


1
2017-07-09 10:59



TYPO3 FCE y Acordeón Bootstrap

También estoy teniendo problemas con este problema. Lo estoy usando en TYPO3 para un cliente que quiere agregar una cantidad infinita de elementos al acordeón. Así que creé un Elemento de Contenido Flexible y mapeé los elementos.

La idea con ese data-toggle = "collapse-next" no funcionó para mí como se esperaba, ya que no cerró los elementos abiertos. Creé una nueva función de Javascript haciendo eso. Encuentra el código aquí. Esperemos que alguien encuentre las cosas útiles.

Javascript

$(document).on('click.collapse-next.data-api', '[data-toggle=collapse-next]', function (e) {
  var $container = $(this).parents(".accordion");
  var $opencontainers = $container.find(".in");
  var $target = $(this).parent().next();
  $target.data('collapse') ? $target.collapse('toggle') : $target.collapse();
  $opencontainers.each(function() {$(this).collapse('toggle')});
})

HTML

<html>
    <div class="accordion">
        <div class="accordion-section">
            <div class="accordion-group">
                <div class="accordion-heading">
                    <a class="accordion-toggle" data-toggle="collapse-next">
                        Collapsible Group Item #1
                    </a>
                </div>
                <div class="accordion-body collapse">
                    <div class="accordion-inner">
                        Anim pariatur cliche...
                    </div>
                </div>
            </div>
        </div>
    </div>
</html>

0
2017-08-06 10:50



Aquí hay un enfoque que evita la necesidad de ID únicos y evita una estructura html específica.

Esto incluye cada instancia del disparador de colapso y el objetivo en una "sección" de html. Me gusta usar selectores de clase, especialmente para instancias múltiples. En este caso, evita tener que crear identificaciones únicas artificiales.

Tomando prestado de la excelente respuesta de @ merv, nombré a los datos-alternar collapse-section similar a su collapse-nexty agregó un atributo de sección de datos. En lugar de parent (). Next (), busca el nombre de la sección más cercana (), luego abajo para el nombre del objetivo dado. Pueden ser hermanos o cualquier otro nivel.

(Tengo una convención de nomenclatura que usa "id ..." como un prefijo para los nombres de clase que se usan como selectores de jquery, para evitar mezclarlos con el estilo).

HTML

<div class="accordion-group idCollapseSection">
  <div class="accordion-heading">
    <a class="accordion-toggle" data-toggle="collapse-section"
       data-section=".idCollapseSection" data-target=".idCollapseTarget">
      Generated Title
    </a>
  </div>
  <div>Any other html, at various depths.
    <div class="accordion-body collapse in idCollapseTarget">
      <div class="accordion-inner">
        Generated Content... this is big and sometimes needs collapsing
      </div>
    </div>
  </div>
</div>

JS

//------------------------------
// Bootstrap Collapse.
//------------------------------
// Extend collapse to contain trigger and target within an enclosing section.
$('body').on('click.collapse-section.data-api', '[data-toggle=collapse-section]', function (e) {
  var thisTrigger = $(this);
  var sectionSelector = thisTrigger.data("section");
  var targetSelector = thisTrigger.data("target");
  var target = thisTrigger.closest(sectionSelector).find(targetSelector);
  target.data('bs.collapse') ? target.collapse('toggle') : target.collapse();
});

0
2017-09-03 02:47