Fazer um menu com vários níveis. BD + PHP + JS + CSS

Posted by micox at Fevereiro 7th, 2008

Ae pessoal,

Há um tempo eu tinha criado um esquema e função PHP pra gerar um UL-LI para fazer um menu tipo pai e filhos, sendo que a quantidade de filhos e netos poderia ser infinita.

Hoje deu vontade de sair um pouco do javascript de sempre e postar essa parada aqui no blog. Desde a organização da tabela no Banco de Dados, função PHP pra gerar os UL-LI e finalizando com links pra estilizar este menu UL-LI.

Lá vai:

1) Para o menu (pai e filhos) será necessário apenas 1 tabela com o nome de ‘menus’. Só executar a seguinte SQL no Banco de dados:

CREATE TABLE `menus` (
`id` tinyint(3) unsigned NOT NULL auto_increment,
`pai_id` tinyint(3) unsigned NOT NULL default '0',
`ordem` tinyint(3) unsigned NOT NULL default '0',
`visivel` tinyint(3) unsigned NOT NULL default '1',
`nome` varchar(30) NOT NULL default '',
`link` varchar(150) default NULL,
PRIMARY KEY (`id`),
KEY `pai_id` (`pai_id`,`ordem`,`visivel`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT

O SQL acima manda criar uma tabela chamada ‘menus’ onde o truque está no campo pai_id.

Em cada registro, o campo pai_id irá apontar para o ID de outro registro na mesma tabela, indicando que ele é o seu pai.
Se a entrada não tiver pai, o campo pai_id deve ficar com 0 (zero).

Image Hosted by ImageShack.us - “Ó PÓOOOOI!!”

2) Preencha sua tabela lembrando de colocar visivel=1 para os menus que quer que sejam visíveis e colocando a ordem de cada menu.
O campo pai_id indica qual o ID do menu pai, se ele for um menu principal (pai) sem outro pai, este campo deve ficar com ‘0′.

Exemplo pra fazer o submenu:

comidas
- arroz
- feijao
guloseimas
- chocolate
- maquindonalds
---- sanduiche porco
---- sanduiche pequeno

Teremos:

comidas (pai_id=0)
- arroz (pai_id = id do 'comidas')
- feijao (pai_id = id do 'comidas')
guloseimas (pai_id=0)
- chocolate (pai_id = id do 'guloseimas')
- maquindonalds (pai_id = id do 'guloseimas')
---- sanduiche porco (pai_id = id do 'maquindonalds')
---- sanduiche pequeno (pai_id = id do 'maquindonalds')

3) Adicione a seguinte função à sua biblioteca de funções (você já tem que estar conectado ao bd antes de usar esta função né?):

function gera_menu($cod_ul_pai,$tabs,$id_do_pai){
    //gera um menu ul com submenus
    //by Micox - elmicox.blogspot.com - forum.ievolutionweb.com
    //exemplo: gera_menu("<ul>","    ",0)
    $recc = mysql_query("SELECT * FROM menus WHERE pai_id=$id_do_pai AND visivel=1 ORDER BY ordem");
    $ret = $cod_ul_pai."\r\n";
    if($recc==true){
        while($linha = mysql_fetch_array($recc,MYSQL_ASSOC)){
            if(isset($linha['link'])){
                $href = $linha['link'];
            }else{
                $href = '';
            }
            $ret .= $tabs."    <li><a href='$href'>".htmlentities($linha['nome'])."</a>";
            //testando se tem filhos
                $recfilho = mysql_query("SELECT * FROM menus WHERE pai_id=$linha[id] AND visivel=1 ORDER BY ordem");
                if(mysql_num_rows($recfilho)>0){
                    $ret .= "\r\n".$tabs."        ".gera_menu("<ul>",$tabs."        ",$linha['id'])."    ".$tabs;
                }
            //fim filhos
            $ret .= "</li>\r\n";
        }
    }
    $ret .= $tabs."</ul>\r\n";
    mysql_free_result($recc);
    return $ret;
}

4) Chame a função onde vc quer que gere o menu:

<?php echo gera_menu("<ul>","        ",0) ?>

5) Isso aí irá gerar um menu como este exemplo:

<ul>
  <li><a href='produtos.php?text=3'>Produtos e Serviços</a>
    <ul>
      <li><a href='produtos.php?Gravadores_Monolinha&cat=1&text=14'>Gravadores Monolinha</a></li>
      <li><a href='produtos.php?Gravadores_Multilinha&cat=2&text=15'>Gravadores Multilinha</a></li>
      <li><a href='produtos.php?Outros_produtos&cat=3&text=16'>Outros produtos</a></li>
    </ul>
  </li>
  <li><a href='conteudo.php?Revendas&text=4'>Revendas</a>
    <ul>
      <li><a href='conteudo.php?Brasil&text=17'>Brasil</a></li>
      <li><a href='conteudo.php?Internacional&text=18'>Internacional</a></li>
      <li><a href='conteudo.php?Seja_um_revendedor&text=19'>Seja um revendedor</a></li>
    </ul>
  </li>
  <li><a href='conteudo.php?Clientes&text=5'>Clientes</a></li>
  <li><a href='conteudo.php?Suporte&text=6'>Suporte</a></li>
</ul>

6) Pronto, agora é só estilizar com algum tutorial de menu, treemenu (menu em árvore), menu drop down horizontal, vertical, etc.

Té. Dúvidas, pergunta lá no fórum :)

Posted in funções, menus, php| 6 Comments | 

Desenhando pontos e linhas com javascript puro (sem canvas)

Posted by micox at Janeiro 31st, 2008

Bem, infelizmente javascript não tem muitas capacidades de gerar gráficos, linhas, pontos, círculos, etc a não ser com CANVAS (infelizmente não bem suportado por todos os navegadores) ou por POG.

Fazer usando o canvas não teria graça hehe, então fui pro que eu gosto: a velha gambiarra.

Bom, gerar o ponto é fácil:

<style>
.ponto {  background-color: #0000FF; position: absolute; overflow: hidden;}
</style>
<script>
//global que guarda a largura da linha. Quanto maior, mais rápido o processamento.
window['larguraPonto']=2;
function geraPonto(x, y, nome, elempai) {
    //by Micox - www.elmicox.com - 31/01/08
    var pixel = document.createElement('div');
    pixel.id = nome;
    pixel.className = 'ponto';
    pixel.style.width = pixel.style.height = window['larguraPonto'] + 'px'
    pixel.style.left = x + 'px';
    pixel.style.top = y + 'px';
    return elempai.appendChild(pixel);
}
//chamando a função e gerando uma reta vertical
window.onload = function(){
 geraPonto(10,4,'ponto1',document.body)
 geraPonto(10,6,'ponto2',document.body)
 geraPonto(10,8,'ponto3',document.body)
 geraPonto(10,10,'ponto4',document.body)
 geraPonto(10,12,'ponto4',document.body)
 geraPonto(10,14,'ponto4',document.body)
 }
</script>

Perceba que eu coloquei algumas características do ponto via CSS e a definição da largura do ponto em uma variável global. Quanto MENOR for o valor da largura do ponto, logicamente irá demorar mais a plotagem do ponto.

Agora pra gerar uma linha inteira terei que usar meus exímios e velhos conhecimentos de matemática geométrica aprendidos no ensino médio hauehea (Vê se eu lembro disso. Lógico que colei do David Betz e dei umas adaptadas).

function geraLinha(x1, y1, x2, y2, nome, elempai) {
    //by Micox - www.elmicox.com - 31/01/08 - adaptado de http://www.davidbetz.net/graphics/
    var longPixel = document.createElement('div');
    if(elempai.constructor==String){ elempai = document.getElementById(elempai);}

    if(typeof(window['MicoxLinhas'])=='undefined'){
        window['MicoxLinhas'] = Array(); //este cidadão global irá guardar todas as linhas
    }
    window['MicoxLinhas'][nome] = Array()

    var steep = Math.abs(y2 - y1) > Math.abs(x2 - x1);
    if (steep) {
        var t = y1;
        y1 = x1; x1 = t;
        t = y2; y2 = x2;
        x2 = t;    }

    var deltaX = Math.abs(x2 - x1);
    var deltaY = Math.abs(y2 - y1);
    var error = 0;
    var deltaErr = deltaY;
    var xStep, yStep, modulo;
    var x = x1, y = y1;

    if (x1 < x2) {
        xStep = window['larguraPonto'];
        modulo = +1;
    }else {
        xStep = -window['larguraPonto'];
        modulo = -1;
    }

    if(y1 < y2) { yStep = window['larguraPonto']; }
    else { yStep = -window['larguraPonto']; }

    var nomeP = nome + x + '-' + y;

    if(steep) { window['MicoxLinhas'][nome][x] = geraPonto(y, x, nomeP, elempai);    }
    else { window['MicoxLinhas'][nome][x] = geraPonto(x, y, nomeP, elempai);    }

    var fim = modulo * x2;

    while( modulo * x < fim) {
        x = x + xStep;
        error = error + deltaErr;
        if(2 * error >= deltaX) {
            y = y + yStep;
            error = error - deltaX;
        }
        nomeP = nome + x + '-' + y;
        if(steep) { window['MicoxLinhas'][nome][x] = geraPonto(y, x, nomeP, elempai);    }
        else { window['MicoxLinhas'][nome][x] = geraPonto(x, y, nomeP, elempai);    }
    }
    return window['MicoxLinhas'][nome];
}

//brincando de desenhar
window.onload = function(){
 geraLinha(20,120,30,100,'minhaLinha1',document.body)
 geraLinha(30,100,60,100,'minhaLinha2',document.body)
 geraLinha(60,100,90,80,'minhaLinha3',document.body)
 geraLinha(90,80,130,80,'minhaLinha4',document.body)
 geraLinha(130,80,160,100,'minhaLinha5',document.body)
 geraLinha(160,100,210,105,'minhaLinha6',document.body)
 geraLinha(210,105,220,120,'minhaLinha7',document.body)
 geraLinha(220,120,20,120,'minhaLinha8',document.body)
 //agora só faltam as rodas haehaheuhaeu
}

Pronto, agora só falta fazer funções pra elipses e curvas hauheuhae. Não tô afim de brincar disso hoje não. Algum dia eu ou alguém faz uma função pra isso.

Percebam, senhores, que coloquei a opção de nomes nas linhas e pontos e coloquei elas dentro de um pai qualquer. Pra quê? Ué, pra poder apagar e mover as ditas cujas.

As funções de apagar:

function removePonto(nomeOuRef){
    //recebe o ID do ponto ou a referencia ao objeto HTML
    if(nomeOuRef.constructor==String){    nomeOuRef = document.getElementById(nome);    }
    nomeOuRef.parentNode.removeChild(nomeOuRef)
    try { delete nomeOuRef; } catch(e) { nomeOuRef = null }
}
function removeLinha(nomeOuRef){
    //recebe o ID da reta ou a referencia ao objeto array
    if(nomeOuRef.constructor==String){ nomeOuRef = window[nomeOuRef]; }
    for(var i in nomeOuRef){
        removePonto(nomeOuRef[i])
        try { delete nomeOuRef[i]; } catch(e) { nomeOuRef[i] = null }
    }
    try { delete nomeOuRef; } catch(e) { nomeOuRef = null }
}

As funções de mover nem precisava colocar aqui pois é aquele básico esquema de mover Divs:

function move(quem){
 //by Micox - www.elmicox.com - 30/01/08
 if(quem.constructor==String){ quem = document.getElementById(quem); }
 //movendo
 window['move'+quem.id] = setInterval( function(){
   quem.style.left = (quem.offsetLeft + 5) + 'px'
  }, 50);
}
function para(quem){
 //by Micox - www.elmicox.com - 30/01/08
 if(quem.constructor==String){ quem = document.getElementById(quem); }
 if(typeof(window['move'+quem.id])=='number'){
  clearInterval(window['move'+quem.id])
 }
}

Agora veja um exemplo completo e meu opalão se movimentando aqui.

- “Porra mico, que mané você heim? Não é mais fácil abrir um programa de imagens qualquer e desenhar diboas?”

- Sim meu caro cidadão, porém, digamos que você queira fazer um fluxograma em javascript (aqui você vê o fluxograma finalizado).
E aí? Nunca se sabe quando vamos precisar de algo, como diria George Boole.

Té a próxima, malucada.

Posted in funções, graficos, javascript| 5 Comments | 

ajaxPost - submeter formulários via Post facilmente

Posted by micox at Dezembro 28th, 2007

Ae cidadãos e cidadoas,

Sei que ninguém vai ler essa parada hoje, mas Feliz Natal (atrazado, agora já era) e próspero ano novo. Pois é…

Bom, lembram-se quando eu fiz a velha e boa função pra ajax simples ajaxGet? Pois é…
Lembram-se que o malandro aqui prometeu fazer uma versão pra submeter formulários via ‘Post’ também? Pois é…

Passados mil anos, o atoa aqui ainda não fez. Mas teve uma boa alma, amigo da criançada, membro do Webly que fez: o Cráudio, ops Klawdyo.
A parada ficou beleza. Ainda não testei 100% pois resolvi usar este ânimo pra fazer logo uma nova função pra ajax ultra-simples.

Então quem quiser submeter formulários via post através de ajax já sabe: usem a ajaxPost do José Cláudio e do Micox. :)

Em breve uma nova função fácil pra ajax englobando submissão simples, de formulários, função de callback (retorno), etc, etc.
I’ll be back!

Terminator - I'll be back

Posted in ajax, funções, javascript| 9 Comments | 

Animação para div aparecer gradativamente (alterando dimensões)

Posted by micox at Dezembro 13th, 2007

Hopa.

Que tal ao invés de usar simplesmente o velho "display: block" pra deixar determinada div visível, usar uma animaçãozinha pra dar um efeito especial melhor no seu site.

Veja aqui um exemplo.

Este tipo de coisa está disponível em todo framework javascript que se preze. Mas se você não tá afim de mexer com frameworks, é só adicionar a função abaixo na sua biblioteca:

function aparece(quem,speed){
    //Aparece o 'quem' gradativamente na velocidade especificada em speed (high, normal, slow)
    //by Micox - http://www.elmicox.com/2007/animacao-para-div-aparecer-gradativamente/

    if(typeof(quem)=='string'){
        quem = document.getElementById(quem);
    }
    if(typeof(window['micoxApareceGradativamente'+quem.id])!='undefined'){
        //esta animação já está em execucao, saio
        return true
    }
     var sty = quem.style;
     sty.visibility = 'hidden'
     var pos_init = sty.position;
     sty.position = 'absolute';
     if(sty.display=='none'){ sty.display = 'block' }
     var alt_init = parseInt(quem.offsetHeight);
     var lar_init = parseInt(quem.offsetWidth);
     var over_ini = sty.overflow;
     sty.overflow = 'hidden';
     sty.visibility = 'visible';
     sty.height = '0px';
     sty.width = '0px';

     if(typeof(quem.timeAparece)!='undefined'){
         clearInterval(quem.timeAparece);
     }
     quem.timeAparece=null;

     var loop = function(){
         var alt_atu = parseInt(sty.height);
         var lar_atu = parseInt(sty.width);
         if(alt_atu < alt_init){
             sty.height = (alt_atu + 5) + 'px';
         }
         if(lar_atu < lar_init){
             sty.width = (lar_atu + 25) + 'px';
         }
         if(alt_atu == 0 ){
             sty.position = pos_init;
         }
         if(alt_atu >= alt_init && lar_atu >= lar_init){
             //acabou
             clearInterval(quem.timeAparece);
             delete window[’micoxApareceGradativamente’+quem.id];
             sty.height = (alt_init) + ‘px’;
             sty.width = (lar_init) + ‘px’;
             sty.overflow = over_ini;
         }
     }
     switch(speed){
         case ‘high’: speed = 1; break;
         case ‘normal’: speed = 20; break;
         case ’slow’: speed = 40; break;
         default: speed = 10;
     }
     window[’micoxApareceGradativamente’+quem.id] = true;
     quem.timeAparece = setInterval(loop,speed);
}

Exemplo de uso (passando o ID):

<span onclick='aparece("apa","slow")'>clica</span>
<div id='apa' style='display: none; border: 1px solid black;'>asdfçlaksdfçl alsjdfçlajsdçlfjaçsldjfçlajsdçflj açlsdfjksd
asdfaslçkdfjçlaj<br />aç jdfljaçsldjkfçlaskjd f<br />lça jçdfljaçsdl fjkçalsjdf çlkajsçdlf</div>

Ou passando a referencia do elemento:

<span onclick='aparece(document.getElementById("apa"),"slow")'>clica</span>
<div id='apa' style='display: none; border: 1px solid black;'>asdfçlaksdfçl alsjdfçlajsdçlfjaçsldjfçlajsdçflj açlsdfjksd
asdfaslçkdfjçlaj<br />aç jdfljaçsldjkfçlaskjd f<br />lça jçdfljaçsdl fjkçalsjdf çlkajsçdlf</div>

Posted in funções, javascript| 19 Comments | 

Evento body onReady sem o uso de libs (DOM)

Posted by micox at Novembro 13th, 2007

Aberta novamente a temporada de postagens heheh.
Um serviço que eu estava fazendo maneirou e acho que posso voltar a ser mais periódico aqui no blog.

Bora lá com o on ready. Leia até o final :)

Bom, grande parte da malandragem que lê este blog usa frameworks e, portanto, devem conhecer as facilidades de uma função/evento on ready. Quem não conhece, vai achar esta postagem muito útil.

Perceba que não estou falando do onreadystate do AJAX nem do onready de iframes e afins, mas de onready no javascript. Continue lendo que entenderá.

Vou copiar aqui algumas descrições do que é o ‘on Ready’ pra não ter que ficar
definindo tudo novamente:

[…] Ready é semelhante ao onload do body, porém ele não
espera carregar imagens e objects, ou seja, assim que estiver carregado todo o HTML ela é executada! Muito útil! […] - jQuery Introdução - Felipe Diesel

[…]onReady é um método automaticamente chamado quando o DOM estiver completamente carregado, garantindo que todos os elementos referenciados na sua aplicação estarão disponíveis quando o script rodar. […] ( Introdução à bibliteca EXTjs - Fábio Vedovelli )

Nós usamos constantemente o ‘window.onload’ ou o ‘body.onload’ pra dizer que devemos iniciar a execução do nosso script. Porém, o evento onload espera tooooooda a tonelada de imagens/objetos/etc serem carregados antes de ser disparado.
Carambola, na imensa maioria das vezes nós precisamos apenas que os elementos HTML estejam carregados. É isso que o on ready faz! Que legal, dãã!

Em muitas bibliotecas este evento já está incluso (EXTjs e jQuery por exemplo), porém quem não usa bibliotecas ou cujas bibliotecas não tem o onready ficam a ver navios.
Tadáaaa: o Mico tem a solução!

“Chega de papo mico, você nunca foi de papo. Mostra logo um exemplo e o maldito script pra onload pra quem não usa bibliotecas.”

<script>
//vai funcionar rapidão pois não precisa esperar a imagem, só o carregamento do HTML
bodyOnReady(function(){
	alert('bodyOnReady ' + document.getElementById('teste').innerHTML)
})

//vai funcionar beleza, mas só depois que tooodos os elementos forem carregados
window.onload = function(){
	alert('onload ' + document.getElementById('teste').innerHTML)
}

//vai dar erro pois o elemento 'teste' ainda não foi criado
	alert('alert simples ' + document.getElementById('teste').innerHTML)

</script>
<body>
<img src='http://img119.imageshack.us/img119/4050/no20matinhoue1.jpg' alt='imagem (relativamente) grande' />
<div id='teste'>conteúdo da div</div>
</body>

Entenderam a parada aí? Pois é.

“Mas cadê a função bodyOnReady Mico? A parada aí não funcionou pois tá faltando a função bodyOnReady” - Calma, está abaixo. É só adicioná-la em seu script e usar conforme a sintaxe passada no exemplo acima.

function bodyOnReady(func){
 //call the function 'func' when DOM loaded
 //Version 2.0 - 03/03/2008 - based on Jquery bindReady
 //by Micox - www.elmicox.com - elmicox.blogspot.com - webly.com.br
 //http://www.elmicox.com/2007/evento-body-onready-sem-o-uso-de-libs/

 if(document.addEventListener && navigator.appName.indexOf('Opera')<0){ //FF
	document.addEventListener( "DOMContentLoaded", func, false );

 }else if(navigator.appName.indexOf('Internet Explorer')>=0){ //IE
	try { // by Diego Perini - http://javascript.nwbox.com/IEContentLoaded/
		document.documentElement.doScroll("left");
		func();
	} catch( error ) {
		setTimeout( arguments.callee, 20 );
		return
	}

 }else if(navigator.appName.indexOf('Opera')>=0){
	document.addEventListener( "DOMContentLoaded", function () {
		for (var i = 0; i < document.styleSheets.length; i++){
			if (document.styleSheets[i].disabled) {
				setTimeout( arguments.callee, 0 );
				return;
			}
		}
		func();
	}, false);
 }

}

Várias tentativas de fazer o onready também estão disponíveis na net se
quiserem testar. Esta aqui é só a minha versão (meio gambiarra pra variar).

Só testei no IE6, FF2 e OP9 e em poucas situações. Em caso de bug, não esqueçam de me avisar. Em caso de melhorias, manda ae.

Posted in DOM, funções, javascript| 8 Comments | 

Next Postings »