Interface drag and drop com jQuery

Este artigo está desatualizado. Confira aqui a nova versão deste tutorial.

Customização é um dos principais conceitos da web 2.0 — deixar o usuário participar ativamente em seu website, seja com conteúdo ou com um visual personalizado. Neste artigo você confere como montar uma interface drag and drop (arrastar & soltar) utilizando a biblioteca javascript jQuery, permitindo ao usuário montar uma página a seu gosto, escolhendo a disposição dos boxes de conteúdo disponíveis no site.

Este tutorial é voltado para quem domina HTML e CSS e tem, pelo menos, uma relativa noção de javascript.

jQuery

Antes de começar, vamos falar um pouco da jQuery. Trata-se de um framework que visa facilitar a programação em javascript, tornando o código mais simples, flexível e infinitamente mais elegante. Além de ser bem leve, a jQuery conta ainda com uma comunidade bastante ativa e uma vasta gama de plugins (seu ponto forte).

Veja a seguir um exemplo de código escrito em javascript convencional e uma versão do mesmo código com jQuery.

javascript:

document.getElementById(box).style.backgroundColor = '#ff0000';

jQuery

$('#box').css('backgroundColor', '#ff0000');

Notaram a diferença? Vamos entender o código jQuery, por partes:

$(‘#box’) — o símbolo do dólar, seguido por parênteses, é o construtor da jQuery. Graças a ele você não precisa mais perder tempo digitando document.getElementById toda vez que precisar capturar um objeto HTML pelo id. O seletor $(‘#box’) representa o elemento HTML de id box. Alguns outros exemplos de seletores são:

  • $(‘a’) — pega todos os elementos a do documento html;
  • $(‘div.caixa’) — pega todos os elementos div que possuem a classe caixa;
  • $(‘a[@title]’) — todos os elementos a que possuem o atributo title declarado;
  • $(‘input[@type=checkbox]’) — todos os inputs do tipo checkbox.

.css() — a propriedade .css funciona para exibir ou atribuir um valor CSS de um elemento HTML. No exemplo acima, atribuímos a cor “#ff0000” à propriedade background-color do nosso elemento de id box. Para exibir a propriedade ao invés de atribuir um valor, basta omitir o segundo parâmetro, por exemplo:

$('#box').css('backgroundColor')

Confira algumas outras propriedades dos seletores jQuery:

  • .attr() — retorna/manipula os atributos de um elemento, como title, src, rel, href etc.;
  • .val() — retorna/manipula o campo value de elementos input;
  • .html() — semelhante à propriedade innerHTML do javascript convencional;
  • .text() — retorna somente o texto, sem as tags HTML, de um elemento.

É claro que isso não cobre quase nada do que a jQuery tem a oferecer. Para conferir a lista completa de seletores, comandos, propriedades e métodos visite o wiki do projeto em docs.jquery.com.

Arquivos necessários

A interface drag an drop que iremos desenvolver aqui utiliza os plugins de interface disponíveis neste link e nosso código será baseado no módulo Sortables.

O primeiro passo é declarar os arquivos javascript no head de nosso documento HTML.

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="interface.js"></script>

Observação: neste exemplo, estamos utilizando uma versão completa do arquivo interface.js, com todos os seus módulos, mas nada impede (e eu até recomendo) que você utilize no seu projeto final uma versão compactada apenas com os módulos necessários. Você pode conferir a lista de dependências do Sortables acessando este link.

Elementos da interface

Agora vejamos o que é necessário, esquecendo um pouco a parte de javascript, para a construção de uma interface drag and drop. Que elementos vamos utilizar?

  • No mínimo uma área para soltar os elementos arrastados;
  • Alguns elementos que serão arrastados e soltos nas áreas disponíveis no site;
  • Uma área temporária de ajuda, para indicar onde o elemento que está sendo arrastado pode ser solto;
  • Todo elemento arrastável deve possuir um handle, ou seja, alguma área dentro do próprio elemento onde o usuário irá clicar para arrastá-lo.

Sendo assim, definiremos as seguintes classes CSS para identificar os elementos de nossa interface:

  • recebeDrag
  • itemDrag
  • dragAjuda

No caso do handle não utilizaremos uma classe, mas sim um elemento específico: a tag h2.

Todos os elementos com a classe recebeDrag servirão de base para os elementos arrastáveis. Estes, por sua vez, virão acompanhados da classe itemDrag. O elemento de ajuda (dragAjuda) não precisa ser declarado no documento HTML — o plugin de interface da jQuery se encarrega de fazer isso, apenas o CSS precisa ser criado. Todo itemDrag deve ainda possuir um elemento H2 para arrastá-lo. Nossa estrutura ficou assim:

Elementos da interface drag and drop

E o CSS:

div.recebeDrag {
  min-height: 50px;
}
div.itemDrag {
  margin: 7px;
  border: 1px solid #ccc;
  padding: 7px;
}
div.itemDrag h2 {
  cursor: move;
  background-color: #ff3300;
  color: #fff;
  padding: 3px;
}
.dragAjuda {
  border: 3px dashed #0066cc;
  width: auto !important;
}

Monte um documento HTML com o CSS acima e inclua quantas áreas e ítens quiser para efetuarmos nosso teste.

document.ready

Com nosso HTML pronto, agora resta apenas instanciar o elemento Sortable da jQuery. Ao invés de utilizar o bom e velho onload no body, vamos adotar uma abordagem mais elegante e garantida, já que espera todo o DOM ser carregado antes de ser executada. Ao final de nosso HTML, antes de fecharmos o , utilizaremos o seguinte código javascript:

$(document).ready(
  function () {
    $('div.recebeDrag').Sortable(
      {
        accept      : 'itemDrag',
        helperclass    : 'dragAjuda',
        activeclass   : 'dragAtivo',
        hoverclass     : 'dragHover',
        opacity      : 0.7,
        handle      : 'h2',
        onChange     : function()
        {
          serialEsq = $.SortSerialize('drop-esquerda');
          serialDir = $.SortSerialize('drop-direita');
        },
        onStart : function()
        {
          $.iAutoscroller.start(this, $('body'));
        },
        onStop : function()
        {
          $.iAutoscroller.stop();
        }
      }
    );
  }
);
  

O comando/seletor $(document).ready funciona, de certa forma, como o . Estamos solicitando que, ao carregar o documento, os elementos div com a classe recebeDrag sejam inicializados como objetos Sortables. Logo em seguida declaramos os parâmetros aceitos na nossa interface drag and drop, bem como as ações a serem executadas quando o elemento é mudado de lugar, quando tem início a ação de arrastar e quanto termina esta mesma ação.

O bloco dos parâmetros é bem simples. Primeiro, utilizando o parâmetro accept, definimos que os divs com a classe recebeDrag aceitarão apenas elementos com a classe itemDrag arrastados dentro deles. Os quatro parâmetros seguintes são referentes às classes e estilos CSS. helperclass define a classe do elemento que demarca áreas em que os divs itemDrag podem ser soltos. hoverclass e activeclass são as classes de estado active e hover para o elemento recebeDrag. Já o parâmetro opacity define o nível de transparência de um elemento, podendo variar de 0 a 1.

handle, conforme explicado anteriormente, define o elemento HTML que servirá como “link” para arrastar os divs itemDrag.

E, por fim, as ações:

  • onChange — qualquer função declarada neste parâmetro será executada quando um elemento for arrastado e solto, e sua posição inicial for alterada. É ideal para executar comandos AJAX para gravar a posição em banco de dados ou arquivo, ou simplesmente setar um cookie ou uma variável de sessão. No nosso exemplo estamos apenas executando a função nativa $.SortSerialize, que retorna as posições de cada elemento do recebeDrag, sem gravar nada.
  • onStart — é executada quando tem início o movimento de arrastar o div. Em nossa interface estamos executando o plugin de autoscroll da jQuery, para que o movimento de arrastar e soltar não fique limitado a área visível do navegador.
  • onStop — executada quando o movimento de arrastar é interrompido.

Sugestões de funcionalidades

  • Opção para fechar e minimizar as janelas arrastáveis;
  • Possibilidade para adicionar ou remover boxes de conteúdo da interface.

Referências

Atualização [06/12/2008]

Muita gente me pergunta sobre isso, então aí vai um exemplo utilizando cookies para salvar o posicionamento e ordenação definidos pelo usuário.

Os cookies serão gerenciados diretamente via javascript, através do plugin jCookie: http://plugins.jquery.com/project/cookie.

Confira o que mudou no código. Na propriedade onchange do elemento sortable adicionamos o método $.cookie() para criar um cookie relacionado à cada coluna:

onChange     : function()
{
  serialEsq = $.SortSerialize('drop-esquerda');
  serialDir = $.SortSerialize('drop-direita');
  // salva cookie com posicionamento
  $.cookie('coluna1', serialEsq.hash, {expires: 7});
  $.cookie('coluna2', serialDir.hash, {expires: 7});
}

E, após carregar o documento, verificamos se o cookie existe e definimos o posicionamento dos boxes nas colunas:

$(document).ready(
  function () {
    if ($.cookie('coluna1') != null) {
      // formata string do cookie
      var coluna1 = $.cookie('coluna1').replace(/drop-esquerda[]=/g, '');
      var coluna1 = coluna1.split('&');
      var div_id = '';
      for (var x = 0; x < = coluna1.length; x++) {
        div_id = coluna1[x];
        $('#drop-esquerda').append($('#'+div_id));
      }
    }
    if ($.cookie('coluna2') != null) {
      // formata string do cookie
      var coluna2 = $.cookie('coluna2').replace(/drop-direita[]=/g, '');
      var coluna2 = coluna2.split('&');
      var div_id = '';
      for (var x = 0; x <= coluna2.length; x++) {
        div_id = coluna2[x];
        $('#drop-direita').append($('#'+div_id));
      }
    }
  }
);

Simples, não? :)