Como carregar "widgets" de sites externos sem carroçar sua página...

Não sei vocês, mas no meu caso, a maioria do tráfego do meu site vem "redes sociais" de blogs, como o tecnocrati, linkk, rec6, linkTo, stumbleUpon, etc... E manter os widgets desses caras sempre é útil! Quem vai votar no meu site depois de ter fechado a página original com o link? Acho que ninguém se daria esse trabalho.

Só tem um porém: Esses widgets são pedaços de javascript externo, que carregam arquivos e imagens do site de origem, e nem sempre carregam numa velocidade razoável. Resultado? Meu blog demorava milênios pra carregar.

Mas seus problemas se acabaram-se (ou quase).

Com o javascript delayator tabajara você agora já pode forçar os widgets a carregar apenas DEPOIS QUE TUDO já foi carregado :). E não se assuste, mas é uma gambiarra faminta! 🙂

Uso de iFrame

O primeiro passo pra isso é usar iFrame. Com um iFrame eu posso carregar um conteúdo qualquer num espaço separo que não vai impactar a carga do resto do meu site. Basicamente eu uso um iFrame inicialmente só pra reservar o espaço, e uso javascript para carregar o conteúdo. Um iframe fica mais ou menos assim:

<iframe src=""></iframe>

Que é um iFrame vazio. Depois a gente usa um javascriptzinho pra "encher" o cara. E é aí que vem a manha: O campo de "src" do iFrame pode ser preenchido com código javascript! Você pode gerar uma página inteira usando javascript e carregar ela no iframe, olha só:

<iframe id="myFrame" src="javascript:'XXXX'"></iframe>

E depois usar um script como o seguinte para preencher o iFrame:

<script language="JavaScript">
function getConteudo() {
    return "<h1>TESTE</h1>";
}

var frame = document.getElementById('myFrame');
frame.src = "javascript:'" + getConteudo() + "'";

</script>

Onde "getConteudo()" pode ser reescrita para retornar o que você bem entender. Claro que por enquanto não evitamos que a iFrame tenha o conteúdo carregado no momento em que o script for lido, e com isso só "adiamos" por algumas linhas a carga do nosso widget. O pulo do gato e forçar o javascript a só rodar DEPOIS do final da carga da página. Como? Simples, usando a propriedade de "onload" do HTML:

<head>
<script language="JavaScript">
function getConteudo() {
    return "<h1>TESTE</h1>";
}
function carregaiFrame() {
    var frame = document.getElementById('myFrame');
    frame.src = "javascript:'" + getConteudo() + "'";
}
</script>
<body onload="carregaiFrame()">

<iframe id="myFrame" src="javascript:'XXXX'"></iframe>

</body>

Agora sim! Nosso iFrame carrega o conteúdo que a gente quer, só depois que a página carregar. Mas ainda falta um problema...

"Mas, tio, desse jeito eu só tenho direito a um único iFrame?"

E é aí que vem o pulo do gato! Javascript é seu amigo, ele vai te ajudar. Se eu usar o javascript pra "guardar" o nome e o conteúdo de todos os frames numa lista, depois da página carregada eu só preciso percorrer a lista de iFrames e preencher cada um com o valor correto. Pra isso, precisamos de um scriptzinho logo abaixo de cada iFrame, só pra "contar" o que vai ser o conteúdo dele:

<iframe id="frame1" src="javascript:'XXXX'"></iframe>
<script>
frame_contents['frame1'] = '<script>document.write("teste1");</script>';
</script>

Vejam que pra ilustrar melhor eu usei um script javascript no exemplo. E vejam um detalhe importante: dentro da variável eu usei </script> ao invés de apenas </script>. Se não fizer isso, o browser fica doidão porque acha que você fechou o seu script antes da hora. Aí não...

Enfim, esse código sozinho não faz nada. A gente precisa agora "chamar" esse cara no nosso onLoad... Pra isso, vamos modificar o método carregaiFrame() pra dizer:

// nosso vetor precisa ser declarado, já que ele vai ser usado mais tarde:
var frame_contents = {};
function carregaiFrame() {
    for (name in frame_contents) {
        frame = document.getElementById(name);
        var src_value = "javascript:'" + frame_contents[name] + "'";
        frame.src = src_value;
    }
}

Tcharam! Temos um lindo código que carrega minhas iFrames somente APÓS a carga de toda a página, assim os elementos "lentos" podem ser todos carregados em iFrames desse tipo.

Problemas pendentes

Claro que nem tudo são flores... Sempre sobram um ou outro probleminha pendentes. No meu caso, o problema ficou por conta do rec6. Enquanto os outros widgets usam um link que abre em uma nova janela, o widget do rec6 tem um link normal, que abre na mesma janela. Assim, a página de login ou de votação abre pro sujeito dentro de um iFrame com tamanho pra caber apenas o widget. Impossível de usar, não é? Mas eu estou batalhando numa solução. Quem sabe um jeito de copiar o conteúdo do iFrame pra dentro de um div. Ainda tenho de testar o que vai dar certo.

Nesse meio tempo, deixo vocês brincarem com as dicas que acabei de dar. E lembrem-se: no Internet Explorer, pra tirar as bordas de um iFrame, não adianta usar CSS... (Mas essa dica quem for esperto pega direto do código fonte dessa página).

Como "consertar" o position:fixed no IE.

Que era minha pergunta de ontem, mas ninguém respondeu... Apelei, claro, pro labirinto de Falken, que me respondeu em charadas, mas por fim respondeu. A dica que me foi útil veio daqui, e é uma gambiarra sem tamanho: Já que essa melda não funciona, use javascript e boas!

E foi o que fiz. Agora minha barra lateral de propagandas (aleatória, como sempre) funciona que é uma maravilha. Até no Internet Explorer (argh!). Então aqui vai o que eu fiz no wordpress pra funcionar:

Propagandas que ficam em posição fixa, fora da área útil da tela, no wordpress (e que claro, funcionem no Internet Explorer, senão era moleza).

Pois tudo começou assim: CSS é manha! basta então eu criar uma "div" com minha propaganda, e tacar um CSS pra colocar ela onde eu quero. E foi o que eu fiz: Acrescentei no "header.php", logo abaixo do <body> o seguinte trecho:

<div id="banner_vertical">
        <!-- ... Trecho contendo minha propaganda, favor consultar seu provedor de propagandas ... -->
</div>

Feita essa "moleza", fui atrás do CSS no arquivo "style.css" e lasquei-lhe um estilo correspondente à minha div:

#banner_vert {
        position: fixed;
        right: 1em;
        top: 2.5em;
        z-index: 0;
}
#page {
        z-index: 1 !important;
}

Tudo muito fácil, claro! E muito bom, no opera e no firefox a coisa tava maravilhosa! E eu lá lembrei de testar no IE? Claro que não... Até que fui testar outra coisa, sem a menor relação com isso e abri no IE. ARGH!!! A propaganda aparecia "centralizada" (sic, acho que o certo seria centrada, quem me corrige?), no alto da tela, jogando todo o resto pra baixo, fazendo até sumir tudo da tela! Desesperei! Tentei achar solução, e nada! O position:fixed simplesmente não funciona no IE. Pra poder ter tempo pra pensar, lasquei um "float: right" e torci pra dar certo. Até deu, mas o efeito não era nada do que eu queria. A página era "jogada" pra esquerda de uma distância correspondente à largura do banner. Mas eu não tinha o que fazer, era 1 da manhã, e eu resolvi ir dormir.

Dois dias depois, esperando que alguém respondesse meu post no blog, acabei desistindo. ninguém ia me dar essa dica, teria de descobrir sozinho. Mas pra isso que existe o gúgou, né verdade? E fui. Acabei achando uma página legal (ou pelo menos parece legal, não li tudo porque era grande e só copiei os exemplos, hehehehe): http://www.howtocreate.co.uk/fixedPosition.html.

Claro que os exemplos não eram aplicáveis to the foot of the letter... Mas juntando dois dos exemplos, tive exatamente o que eu queria. Que acabou se resumindo a acrescentar no meu "header.php" (de novo ele... mas é quem aparece em todas as páginas, então vai ele mesmo) o seguinte trecho de código:

<!-- hack for IE 5 fixed -->
<style type="text/css">
#banner_vert {
        /* Netscape 4, IE 4.x-5.0/Win and other lesser browsers will use this */
        position: absolute;
        right: 1em;
        top: 2.7em;
}
body > div#banner_vert {
        /* used by Opera 5+, Netscape6+/Mozilla, Konqueror, Safari, OmniWeb 4.5+, iCab, ICEbrowser */
        position: fixed;
}
</style>
<!--[if gte IE 5.5]>
<![if lt IE 7]>
<style type="text/css">
div#banner_vert {
        /* IE5.5+/Win - this is more specific than the IE 5.0 version */
        left: expression( ( -12 - banner_vert.offsetWidth
                             + ( document.documentElement.clientWidth
                                 ? document.documentElement.clientWidth
                                 : document.body.clientWidth )
                             + ( ignoreMe2 = document.documentElement.scrollLeft
                                             ? document.documentElement.scrollLeft
                                             : document.body.scrollLeft ) )
                           + 'px' );
        top: expression( ( 6 + ( ignoreMe = document.documentElement.scrollTop
                                            ? document.documentElement.scrollTop
                                            : document.body.scrollTop ) )
                         + 'px' );
}
</style>
<![endif]>
<![endif]-->

Uh! Mas esse trem é grande... o que ele faz??? Bom, vamos destrinchar! comecemos pelo que vocês já tinham visto antes...

#banner_vert {
        /* Netscape 4, IE 4.x-5.0/Win and other lesser browsers will use this */
        position: absolute;
        right: 1em;
        top: 2.7em;
}
body > div#banner_vert {
        /* used by Opera 5+, Netscape6+/Mozilla, Konqueror, Safari, OmniWeb 4.5+, iCab, ICEbrowser */
        position: fixed;
}

Cara, é igualzinho eu tinha tacado no CSS, nem precisava copiar aqui, podia continuar lá... Mas eu quis ser consistente com o exemplo, então taquei aqui e tirei de lá do CSS. Basicamente, tem umas diferencinhas: só usa uns recursos pra browsers antigos (position: absolute) que eu num quero nem ver como fica, e pra garantir que só browsers novos vão ler a coisa direito, usa o "body > div#banner_vert". Isso aí só "diz" que o conteúdo ali se aplica só no que estiver dentro do "body", for declarado como "div" e tiver o "ID" igual a "banner_vert". E como essa sintaxe não existe em browsers velhos, browsers velhos vão ignorar (espero eu, nem quero estar vivo pra testar isso). Essa parte então foi manha, né? Vamos pro resto...

<!--[if gte IE 5.5]>
<![if lt IE 7]>
 ...
<![endif]>
<![endif]-->

Pra quem entende de CSS multibrowser, isso aí ficou fácil: fala que o que tá no meio (que eu substitui por "...") só vai precisar rodar se minha versão for "gte" (maior ou igual) que "IE 5.5" e também se for "lt" (menor) que "IE 7". Manha né? Então, próximo...

div#banner_vert {
        /* IE5.5+/Win - this is more specific than the IE 5.0 version */
        left: expression( ( -12 - banner_vert.offsetWidth
                             + ( document.documentElement.clientWidth
                                 ? document.documentElement.clientWidth
                                 : document.body.clientWidth )
                             + ( ignoreMe2 = document.documentElement.scrollLeft
                                             ? document.documentElement.scrollLeft
                                             : document.body.scrollLeft ) )
                           + 'px' );
        top: expression( ( 6 + ( ignoreMe = document.documentElement.scrollTop
                                            ? document.documentElement.scrollTop
                                            : document.body.scrollTop ) )
                         + 'px' );
}

Ugh! isso aí nem eu li direito! Basicamente, isso aí diz: use o seguinte código em javascript para ajustar a posição "left:" (ponta mais à esquerda) e "top:" (ponta mais ao alto) do meu "div" que se chama "banner_vert". O que tem depois de "expression" e entre parentes é o código javascript que calcula a posição ideal na tela do que que eu quero. Vou tentar destrinchar...

-12 (ou no de baixo 6):

Duh! é a posição em pixels que eu quero o div. O "-" é porque eu quero à direita, e não à esquerda!

- banner_vert.offsetWidth

É o tamanho do banner. Eu subtraio ele pelo mesmo motivo acima: estou setando a posição mais a esquerda do banner, mas quero que ele apareça a direita. então calculo a posição do final da tela MENOS o tamanho do banner MENOS a margem que eu vou querer 😉

( document.documentElement.clientWidth
  ? document.documentElement.clientWidth
  : document.body.clientWidth )

A largura da tela. Se eu estiver dentro de um frame ou outra coisa assim (documentElement) eu pego ele, senão pego logo o body! Sacou?

( ignoreMe2 = document.documentElement.scrollLeft
              ? document.documentElement.scrollLeft
              : document.body.scrollLeft )

Isso daí é pra forçar o cara a calcular o tamanho do "scroll" que foi feito. Aí eu vou e "adiciono" isso. Nesse caso o "scroll" horizontal e no caso de:

( ignoreMe = document.documentElement.scrollTop
             ? document.documentElement.scrollTop
             : document.body.scrollTop ) )

o scroll vertical.

E é isso, então o javascript se resume a calcular a posição desejada em relação ao início da página, e somar isso com o scroll que venha a ser feito. Simples né? Vamos torcer pra que funcione sempre em qualquer situação.