HTML / CSS: qual é a melhor opção para o layout de uma árvore de elementos aninhados que as tabelas aninhadas?

8

Ok, eu tenho um conjunto de caixas de seleção para selecionar critérios. Para argumentar, vamos dizer que os dados são assim:

[] Vehicles
   [] Unpowered
      [] Bicycle
      [] Skateboard
   [] Powered
      [] Two-wheeled
         [] Motorcycle
         [] Scooter
      [] Four-wheeled
etc

Os [] s representam caixas de seleção.

Ignorando a natureza obviamente inventada deste exemplo, a ideia é esta:

  • Para começar, apenas a caixa de seleção Veículo fica visível;
  • Se o usuário clicar na caixa de seleção Veículo, o próximo nível será ativado (Powered, Unpowered);
  • Se o usuário selecionar Powered, abre o próximo nível (de duas rodas, de quatro rodas);
  • Se o usuário, em seguida, desmarcar Powered, esse nível desaparece.

Agora, isso é relativamente fácil de configurar com o onclick alternando o atributo CSS de exibição entre bloco e nenhum.

Atualmente, isso está estruturado na página como:

<table>
<tr>
  <td><input type="checkbox" onclick="toggle('__Vehicles');"></td>
  <td>Vehicles
    <table id="__Vehicles">
    <tr>
      <td><input type="checkbox"></td>
      <td>Unpowered
etc

Eu devo apontar antes que alguém pergunte: o motivo pelo qual a caixa de seleção foi colocada na célula da tabela era controlar a formatação. Isso facilitou o recuo efetivo, já que tudo na próxima célula da tabela seria alinhado.

Tudo funciona bem, mas o aninhamento de tabelas é bastante profundo. Eu continuo pensando que tem que haver uma maneira melhor do que isso. Ele deve ser capaz de ser facilmente construído dinamicamente e ter um bom suporte entre navegadores para a formatação da "árvore".

Eu também devo mencionar que o jQuery está disponível. Estou usando isso para outras coisas.

Sugestões?

Editar: Sim, o estilo da caixa de seleção é importante, pois alguns comentários foram observados. Além disso, eu postei uma solução para isso, com base nas respostas que obtive, como uma resposta abaixo (muito grande para adicionar aqui), apenas para aqueles curiosos para ver um exemplo.

    
por cletus 27.01.2009 в 23:19
fonte

10 respostas

21
<ul>
    <li><input type="checkbox" />Vehicles <ul>
        <li><input type="checkbox" />Unpowered</li>
        <li><input type="checkbox" />Bicycle</li>
        <li><input type="checkbox" />Skateboard</li>
    </ul></li>
    <li><input type="checkbox" />Powered <ul>
        <li><input type="checkbox" />Two-wheeled <ul>
            <li><input type="checkbox" />Motorcycle</li>
            <li><input type="checkbox" />Scooter</li>
        </ul></li>
        <li><input type="checkbox" />Four-wheeled</li>
     </ul></li>
</ul>

Editar: um pouco css & amp; js para mostrar & amp; ocultar elementos aninhados (sem caixas de seleção)

li.opened ul {
display: block;
}

li.closed ul {
    display: none;
}

e js ...

$(document).ready(function() {

$('li input:checkbox').click(function () {
    $(this).parent().toggleClass('opened');
    $(this).parent().toggleClass('closed');
});

$('li').addClass('closed');
});

edite, mais uma vez, porque o Sparr quer alguns estilos melhores (assumindo que as caixas de seleção tenham um estilo de "caixa de seleção"

li input.checkbox { /* input:checkbox is not 100% compatible */
    width: 6px;
    margin: 0 2px;
    /* This makes 10px be the total "width" ofh the checkbox */
}

ul {
    margin: 0 0 0 10px; /* Or whatever your total checkbox width is */
    padding: 0;
}

li {
    padding: 0;
}
    
por davethegr8 27.01.2009 / 23:24
fonte
10

Você pode fazer isso:

<ul>
  <li>
    <input type="checkbox" /> Option 1
    <ul>
      <li><input type="checkbox" /> Option 1 Sub Option A</li>
    </ul>
  </li>
</ul>

Você definiria o preenchimento / margem dos ULs para 0 e 0. Em seguida, defina o preenchimento à esquerda dos LIs para 10px.

ul {
  margin:0;
  padding:0;
}

li {
  margin:0;
  padding:0 0 0 20px; /* Each nested li will be padded incrementally */
}

Para o javascript, anexe um evento a cada caixa de seleção que determina se o irmão UL (se houver algum) deve estar visível. Se a caixa estiver marcada, mostre-a, ou oculte-a.

    
por Sampson 27.01.2009 / 23:24
fonte
4

Listas não ordenadas aninhadas são a melhor prática para esse tipo de coisa.

<ul>
   <li>Item 1</li>
   <li>Item 2
      <ul>
         <li>Sub Item 1</li>
         <li>Sub Item 2</li>
         <li>Sub Item 3</li>
      </ul>
   </li>
   <li>Item 3</li>
   <li>Item 4
      <ul>
         <li>Sub Item 1</li>
         <li>Sub Item 2</li>
         <li>Sub Item 3</li>
      </ul>
   </li>
</ul>
    
por Birk 27.01.2009 / 23:23
fonte
3

Quer ver alguma mágica jQuery profunda?

<ul class="tree">
  <li><input type="checkbox" name="Vehicles" checked>Vehicles
    <ul>
      <li<input type="checkbox" name="Unpowered">Unpowered
        <ul>
          <li><input type="checkbox" name="Bicycle">Bicycle</li>
          <li><input type="checkbox" name="Skateboard">Skateboard</li>
        </ul>
      </li>
      <li><input type="checkbox" name="Powered" checked>Powered
        <ul>
          <li><input type="checkbox" name="Two-wheeled">Two-wheeled
            <ul>
              <li><input type="checkbox" name="Motorcycle" checked>Motorcycle</li>
              <li><input type="checkbox" name="Scooter">Scooter</li>
            </ul>
          </li>
          <li><input type="checkbox" name="Two-Wheeled">Four-wheeled</li>
        </ul>
    </ul>
  </li>
</ul>

Observação: a única decoração aqui é a classe de árvore.

ul.tree {
    list-style-type: none;
    margin: 0 0 0 -22px;
    padding: 0;
}

ul.tree ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
}

ul.tree input {
    margin-right: 6px;
}

ul.tree li {
    padding: 0 0 0 22px;
    margin: 1px;
}

.closed ul {
    display: none;
}

e a mágica:

$(function() {
    $("ul.tree li:has(ul) > :checkbox").click(function() {
        jQuery(this).parent().toggleClass('closed');
    }).not(":checked").parent().addClass("closed");
});

Isso transforma a coisa toda em uma árvore de abertura e fechamento de trabalho quando você clica nas caixas de seleção. Impressionante.

Obrigado a davethegr8, Jonathon Sampson e outros por conselhos.

    
por cletus 28.01.2009 / 03:08
fonte
2

Eu tenho que entrar em contato para sugerir que você extraia o javascript da sua marcação, além das sugestões acima. Usando uma biblioteca como a lowpro (uma das minhas favoritas), você pode criar um objeto para manipular seu comportamento de caixa de seleção aninhada e aplicá-lo automaticamente, sem obstruções. Embalar seu código como esse torna sua marcação mais fácil de manter e seu código mais fácil e rápido de escrever, mais poderoso e mais fácil de manter.

    
por Christopher Swasey 27.01.2009 / 23:33
fonte
1

As tabelas são para dados tabulares. Use listas aninhadas e CSS para formatação.

Se você está procurando uma solução completa, aqui está uma usando CSS puro para navegadores modernos e JavaScript para o IE:

<style>
ul.tree, ul.tree ul {
    position: relative;
    list-style: none;
    margin: 0 0 0 20px;
    padding: 0;
}
ul.tree input {
    position: absolute;
    margin-left: -20px;
}
ul.tree ul {
    display: none;
}
ul.tree input:checked ~ ul {
    display: block;
}
ul.tree label:hover {
    text-decoration: underline;
}
</style>
<ul class="tree">
  <li>
    <input type="checkbox" id="option1">
    <label for="option1">Option 1</label>
    <ul>
      <li>
       <input type="checkbox" id="option1a">
       <label for="option1a">Option 1 Sub Option A</label>
      </li>
      <li>
       <input type="checkbox" id="option1b">
       <label for="option1b">Option 1 Sub Option B</label>
      </li>
    </ul>
  </li>
  <li>
   <input type="checkbox" id="option2">
   <label for="option2">Option 2</label>
  </li>
</ul>
<!--[if lte IE 7]>
<script>
var tree = document.getElementsByTagName('ul')[0];
tree.attachEvent('onclick', function() {
    var src = event.srcElement;

    if(src.nodeName.toLowerCase() === 'label')
        var box = document.getElementById(src.htmlFor);
    else if(src.nodeName.toLowerCase() === 'input')
        var box = src;
    else return;

    for(var current = src.nextSibling;
        current && current.nodeName.toLowerCase() !== 'ul';
        current = current.nextSibling);

    if(current)
        current.style.display = box.checked ? 'block' : 'none';
});
</script>
<![endif]-->

Supõe que uma caixa de seleção não seja mais ampla que 20px .

    
por Christoph 27.01.2009 / 23:23
fonte
1

Enquanto o mcDropdown do jQuery aborda o problema da lista aninhada de uma maneira diferente (sem caixas de seleção), ele pode seja adequado às suas necessidades.

    
por user59593 28.01.2009 / 02:33
fonte
0

ao criar o cb, dê a eles uma classe css dependendo do nível

<input class="LevelXcb" type="checkbox" />

CSS:

.Level0cb{left:4px;}
.Level1cb{left:16px;}
.Level2cb{left:28px;}

se "left" não funcionar, tente definir "margin-left".

    
por Spikolynn 27.01.2009 / 23:30
fonte
0

Eu sei que isso não está muito relacionado à pergunta, então pls não votam é apenas uma sugestão.

Neste exemplo de davethegr8:

<ul>
    <li>Vehicles <ul>
        <li>Unpowered</li>
        <li>Bicycle</li>
        <li>Skateboard</li>
    </ul></li>
    <li>Powered <ul>
        <li>Two-wheeled <ul>
            <li>Motorcycle</li>
            <li>Scooter</li>
        </ul></li>
        <li>Four-wheeled</li>
     </ul></li>
</ul>

Se você der a cada passo uma aula, você pode usar o jquery para fazer alguns efeitos interessantes com um clique:

Como

$("li.Two-wheeled").click(function(event){
   $("div.main_wrapper ul.Moto_or_scooter:hidden").slideDown("slow");
}

Apenas uma pequena sugestão:)

    
por fmsf 27.01.2009 / 23:45
fonte
0

Se a largura das caixas de seleção ainda for um problema (depois de todas as respostas anteriores ...), você pode:

  • Coloque todas as caixas de seleção em uma caixa fixa com para que você saiba exatamente onde o texto está começando (e a próxima caixa de seleção de nível)
  • Use alguns cálculos jquery para obter a largura e definir as margens dos outros elementos dinamicamente (parece um pouco demais ...)

Usando a estrutura da lista aninhada sugerida, obviamente.

    
por jeroen 29.01.2009 / 02:05
fonte