Înapoi la site
Blog

Hartă GOOGLE responsivă și încărcată cu Lazy Loading

În zilele de azi când necesitatea unui site responsive și rapid nu mai e un lux, dezvoltatorii încearcă pe toate căile să minimizeze timpii de încărcare în acord cu diferitele rezoluții ale terminalelor pe care site-urile rulează.Una din secțiunile potențial optimizabile este zona de contact, informatii, formular, și da, ați ghicit cunoscuta hartă Google care arată utilizatotului care este localizarea ta geografica, și cum (eventual) se poate ajunge la tine.

O informație folositoare și care susține credibilitatea celui pe care site-ul îl reprezintă. Numai că...

Preluarea 'oarba' a codului de hartă din pagina Google Maps nu e ok, se copiază un iframe static de fapt, cu dimensiuni fixe, și care de cele mai multe ori nu se potriveste nicicum cu layout-ului secțiunii de contact. De asemenea poziționarea aproape de footer-ul siteului face ca nu toți utilizatorii să ajungă până acolo, făcând din încarcărea hărtii o risipă inutilă de resurse și o reducere notabilă a vitezei de încărcare a paginii. (Din Lighthouse și PageSpeed Insights se vede bine de tot acest bottleneck...)

Deci cum procedam ca să optimizam lucrurile. Pe rând.

A. RELATIV LA RESPONSIVITATE
Definim un container (o sectiune division) ca și depozitar al hărții, și cu ceva reguli CSS  se poate rezolva problema responsivității. De regulă ce preluăm cu 'copy&paste' de la https://www.google.ro/maps este un cod care arată cam așa:

<!-- Height=450px; Width=600px -->
<iframe src="https://www.google.com/maps/embed?pb=!1m14!1m12!1m3!1d7098.94326104394!2d78.0430654485247!3d27.172909818538997!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385710909804" width="600" height="450" frameborder="0" style="border:0"></iframe>

Dupa cum se vede, lățimea și lungime implicite sunt  fixe pentru iframe. Dacă dorim să redefinim aceasta hartă statica într-una dinamică, responsiva, tot ce avem de facut e să definim un wrapper, un division container, la care să atașam ceva reguli CSS și să punem codul cu iframe inauntru.
Arată cam așa:

Codul CSS


<style>
      .google-map-container {
          position: relative;
          padding-bottom: 75%; /* aceasta este rata aspectului hartii și se calculează asa (450/600 = .75, aka 75% ).
Dacă doriți harta la dimensiuni diferite pur și simplu refaceți calculul la noile valori */
          height: 0;
          overflow: hidden;
      }
      .google-map-container iframe {
          position: absolute;
          top: 0;
          left: 0;
          width: 100% !important;
          height: 100% !important;
      }
</style>

Codul HTML


<div style='width:50%; margin-auto;'>
<div class="google-map-container">
<iframe src="https://www.google.com/maps/embed?pb=!1m14!1m12!1m3!1d7098.94326104394!2d78.0430654485247!3d27.172909818538997!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385710909804" width="600" height="450" frameborder="0" style="border:0"></iframe>
</div>
</div>


Implementați acest cod și modificați mărimea ferestrei. Se vede cum dimensiunile ferestrei se modifica. Tehnica poate fi folosita și la embedded videos, și la imagini, dar la acestea exista o metodă mult mai simplă.

 

B. RELATIV LA LAZY LOADING
Problema 'incărcarii târzii' e un pic mai complicată. In esență dorim ca încarcărea harții Google să se facă numai atunci când utilizatorul a ajuns la ea, adică făcand scroll, containerul
care găzduiește harta a ajuns să fie văzut de utilizator. In acel moment ar trebui ca browserul să apeleze Google si să aduca informația local.
Sunt doua metode de realizare.


1.Old School way.
Se implementează un EventListener pentru un event de tip scroll. Adică ori de cate ori utilizatorul face scroll, listener-ul declanșează un set de operații:
se verifică (calculează) dacă 'cantitatea de scroll' (scuze, n-am gasit o sintagma mai bună) este egală sau mai mare decat poziția (offsetul) containerului față de top-ul documentului curent.
Dacă da, atunci prima linie de pixeli a containerului hărții a intrat în vizualizarea utilizatorului, prin urmare declanșează încarcărea. Apoi anuleaza EventListenerul pe scroll, nu mai avem nevoie de el.
Dacă nu, continuă și calculează.
Evident metoda presupune o încărcare destul de serioasî a firului de execuție și unele browsere mai vechi, sau mai puțin 'potente' pot afișa un scroll sacadat, nu prea good user experience.


2. Mai aproape de zilele noastre.
Se foloseste IntersectionObserver API o interfață modernă cu care se pot manageria acțiuni pe o pagină de web în funcție de cum și ce elemente de pe aceasta pagină interacționează în diverse moduri.
Definiția dupa MDN:
"Intersection Observer API oferă o modalitate de a observa în mod asincron intersecția unui element target cu un element părinte, sau cu fereastra de vizualizare a documentului respectiv (viewport)."
In esență ce avem aici ?

  • Avem un observant, sau root element, sau element 'rădacină' al observației; implicit viewport, explicit orice element care poate fi afișat în mod block, adică are granițe definite, dreptunghiulare, sau poate fi încadrat in acest pattern,
  • Avem un observat 'child of observant', adica în DOM, observantul este părintele observatului,
  • Avem un obiect care defineste cum se configurează actul observației și cine participă
  • Avem un obiect care este instanța clasei IntersectionObserver.

Iata un exemplu generic cu comentariile de rigoare:

//obiectul care defineste configurarea observației
const config = {
root: null, //observant, elementul folosit pentru observașie; daca este null, implicit va fi definit ca viewportul browserului, altfel poate fi  orice element block din DOM
rootMargin: '0px', //dacă în jurul elementului root vrem să definim o arie 'activă' pentru mai multa flexibilitate; similar cu definitia CSS margin
treshold: 0 //procentajul de 'intersecție' a elementelor care declansează acțiunea, între 0 - la primul pixel din observat, 1 - la ultimul pixel din observat; poate fi o valoare sau un array de ex. [0,.5, 1]
           //actiunea se declansează la fiecare valoare;
}
//mai jos este obiectul instanțiat; parametrul pasat este o funcție care la rândul ei are obligatoriu un vector cu referinte la elementele observate ca parametru, și o auoreferință (SELF)
let observer = new IntersectionObserver(function(entries, SELF){
            //se traverseză vectorul de observați (entries); daca observatul este observat (:-), okay, am notat și fac unobserve pe dansul;
            //de notat ca toata greutatea calcului, inclusiv continuitatea actului de observare cade în sarcina motorului browserului, ceea ce face                procesul foarte puțin obtruziv.
             entries.forEach( function(ivalue,index){
                   if( ivalue.isIntersecting){
                         console.log("EXISTĂ O INTERSECTIE !");
                         SELF.unobserve(ivalue.target);
                   }
            });
}, config);
//observat este referinta din DOM a unui element children al observantului; unde observantul este, așa cum am zis fie viewportul, fie alt element din DOM, parinte pentru observat
observer.observe( observat );

Simplu și direct.

Și iată o implementare completă pentru o harta Google.


<!doctype html>
<html>
<head>
<style>
          .google-map-container {
               position: relative;
               padding-bottom: 56.25%;
               height: 0;
               overflow: hidden;
           }
          .google-map-container iframe {
               position: absolute;
               top: 0;
               left: 0;
               width: 100%;
               height: 100%;
           }
</style>
</head>
<body>
<h3>Scroll down pana la hartă...</h3>
<div style='margin-top:1500px; margin-bottom:100px; width:50%; margin-left:auto; margin-right:auto;'>
<div id='observed' class="google-map-container"></div>
</div>
<script>
const config = {
           root: null,
           rootMargin: '0px',
           treshold: 0
}
const observed = document.getElementById('observed')
const observedContent = "<iframe src='https://www.google.com/maps/embed?pb=!1m16!1m12!1m3!1d42940.62353427634!2d23.919173549402906!3d47.72734475704244!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!2m1!1zSlVELiBNQVJBTVVSRciYLCBTQVQuIEJVREXImFRJLCBDT00uIEJVREXImFRJLCBCVURFyJhUSSwgTlIuOTc!5e0!3m2!1sro!2sro!4v1618473392986!5m2!1sro!2sro' style='border:none;' allowfullscreen=''></iframe> ";
let observer = new IntersectionObserver(function(entries, SELF){
                entries.forEach( function(ivalue,index){
                       if( ivalue.isIntersecting){
                           observed.innerHTML = observedContent;
                           SELF.unobserve(ivalue.target);
                       }
                });
}, config);
observer.observe( observed ); //argumentul poate fi doar DOM Element</script></body></html>

Se poate testa live, AICI.


Suportul pentru browsere este destul de okay.
Această tehnica deschide largi posibiltăți. Tot ce nu este ABOVE THE FOLD poate fi amânat și încarcat ON DEMAND. Imaginați-vă un carusel cu o mulțime de cod javascript scump în resurse și timp de inițializare/execuție.
Care generează valori mari pentru Total Time Blocking sau Speed Index in Lighthouse. Sau o galerie de imagini de zeci/sute de imagini. Toate pot fi încărcate doar la momentul vizualizării.
Sau un video care pornește / se operește cănd întră, respectiv când iese din view.

Cam atât, întrebări, obsevații, critici astept cu drag :-)

Migrare de la HTTP to HTTPS - pe scurt

undefined

Din anii 2018-2019 se pare că protocolul HTTP este ușor depășit. De ce ? Din mai multe motive. Din considerente de securitate. De viteza. De SEO. Și nu în ultimul rând din considerente de Google. Pe rând deci. migrare HTTP - HTTPS.

Se știe că protocolul de comunicare între browser (client) și server este HTTP. Simplist, se poate spune că informațiile care curg între cele doua entități și sunt vulnerabile la un sniffer, adică la o a treia entitate interpusă intre ele, care "ascultă" la ceea ce vorbesc cei doi în clar. Dacă dialogul intre browser și server ar fi codificat, snifferul n-ar mai fi capabil să ințeleaga nimic din informațile furate. Welcome HTTPS !
So far so good. HTTPS este deci un protocol HTTP codificat, criptat de fapt, și datele trimase via HTTPS sunt procesate prin TLS (Transport Layer Security), de aici rezultand mai multe avantaje:

  • Securitate, in lumea digitala de azi de siguranta datelor depind din ce in ce mai multe lucruri.
  • Viteză, HTTPS folosește o versiune optimizată de HTTP, numită HTTP/2 care poate mări viteza de transfer a informatiilor pâna de 8 ORI
  • Integritatea, informațiile criptate și comprimate sunt mult mai puțin vulnerabile la potentialele probleme de corupere a datelor, și mult mai ușor de verificat din punct de vedere al integrității.
  • Autentificarea, adica userul e mult mai sigur că, comunică chiar cu site-ul pe care il care dorește, și nu cu o clonă concepută să fure datele.

HTTPS nu face din site-ul dvs. o 'fortareata inexpugnabilă', in continuare acesta poate fi vulnerabil la atacuri de tip DOS, sau la vulnerabiltăți ale serverului, sau la SQL Injection, dar hei, este un pas înainte în materie de siguranță, și Google zice asa !

Și nu numai că zice, dar:

  • Se pare că mărește un pic rankingul site-urilor care folosesc protocolul HTTPS, și..
  • Scoate în evidență pe cele care nu-l folosesc, ceea ce din punct de vedere al imaginii (mai ales la magazine virtuale) este un mic dezastru...

Deci, care ar fi pasii de urmat ? Fără să intru în detaliile criptării (simetrice, asimetrice, chei publice, private, etc) procedura este după cum urmează:

1. Obtineți un certifcat SSL și instalați-l pe server. Aici orice firma respectabilă care se ocupă de hosting vă rezolvă problema. Cu o taxa oarecare anuală, sau gratuit după cum ați negociat contractul de găzduire. Odată setările făcute (inclusiv în serverul Apache sau IIS), site-ul se va vedea la adresa https://wwww.siteul-meu.ro, de ex.


2. Modificați toate referințele absolute din site, de la http la https. Referințele sunt în principiu linkuri la imagini, fisiere, scripturi etc.
Tricky part, va trebui in ordine, să modificați:

  • În baza de date (Word Press, Prestashop, sau oricare alta ce stă în spatele site-ului)
  • În fișierele sursă (.html, .php, .js, .css, .asp și care vor mai fi)
  • În eventualele template-uri (fișierele .tpl)
  • În eventualele plugin-uri, module, add-on-uri în vederea eliminării de conținut 'unsecure' (care folosete încă HTTP).

Dacă vă depășeste, apelați la un programator, eventual la echipa care v-a proiectat site-ul (sau de ce nu, la firma noastră :-) )
3. Site-ul dvs probabil este indexat in Google cu vechiul protocol HTTP. Va trebui să-i spunem marelui G ca 'ne-am mutat' la HTTPS, și pentru asta va trebui să 'facem' ceea ce se numeste un '301 redirect', o procedură prin care motorul de cautare află ca site-ul s-a mutat permanent de la HTTP la HTTPS, și să iși actualizeze indexul-ul in consecință. Pentru aceasta trebuie să scrieți câteva linii de cod in fisierul .htaccess. Faceți o cautare pe Google :-) și pe StackOverflow gasiti o multime de exemple cum sa redirectați în principiu
de la http://...., http://www....., https://....,
la https://www..... de exemplu.

Dacă vi se pare că vorbesc limbi straine :-) din nou apelați la un programator (firma noastră oferă servicii de migrare în condiții de minimum downtime), sau dacă sunteți in temă, cum spuneam, acolo e StackOverflow.com to help.


4. In fine după toate astea, navigați pe intreg site-ul și verificați toate linkurile interne, imaginile, formularele etc. In principiu totul ar trebui să fie ok. Rezultatele nu vor întarzia să apară, viteza sporită și un mai bun ranking în Google. Success !

PS. Daca site-ul vostru e inscris in Webmaster Tools, Analytics sau orica alt program Google sau non-Google, atunci modificati si aici referintele la https. De asemenea in portofoliul de backlink-uri luati legatura cu administratorii siteurilor respective sa va modifice referinta la site.
Macar pentru cele mai importante backlinkuri !

Google Chrome și reclamele intruzive

„Intenționăm să nu mai afișăm reclame în Chrome, inclusiv cele deținute sau difuzate de Google, pe site-uri care nu respectă raportul 'Standardele pentru reclame mai bune' (Better Ads Standards), începând cu anul 2018”, zice Google pe blogul oficial Chromium.
Chestia cu „inclusiv cele deținute sau difuzate de Google” e o dulce copilărie, un puseu de concentrat de marketing, dar zâmbind și trecând peste asta, să vedem...
Ce înseamnă „Better Ads Standards” ?
Toate reclamele de tip pop-up, reclamele care rulează automat clipuri video cu sunet, reclame care acoperă tot ecranul şi dispar după un anumit timp sau cele care acoperă mai mult de 30% din ecran NU se incadreaza in acest standard.
Chrome dă o lovitură grea pieței de advertising, care va fi resimțită cu atât mai mult, cu cât are o pondere în căutări mai mare de 50%, în condițiile în care deja programe de gen UBlock sau AdBlocker există, sunt populare și sunt foarte folosite.
Pâna una alta, Google mai dă o șansă celor care fac un ban din afacerea asta (și nu sunt puțini !) lansând programul Ad Experience Report care permite verificarea conformității unei reclame conform standardelor "Better Ads Standards".
Este o veste bună ?
Depinde cine întreabă. Pentru utilizatori în mod sigur că da. Pe internetul românesc abundă site-urile la care conținutul real este ascuns dupa multe pop-uri enervante, active la trecerea involuntară a cursorului peste ele, video-uri care ruleaza simultan (!), și câte năzbâtii aiurea, ferestre care iți acoperă ecranul când ți-e lumea mai dragă, toate astea probabil vor fi uitate în curând, dar... industria nu-i asa, merge înainte, trebuie să traiască. Așa că advertiserii și publisherii vor avea un pic de furcă pâna vor găsi noi soluții de a impinge în fața clientului marfa promovată. Și să nu uităm că și Google și Facebook tot din reclame trăiesc, deci...
Aștept deci cu interes ziua de 15 februarie, care 'pe surse', zice-se că ar fi momentul zero de când ar începe această campanie. Aștept să văd cum vor reacționa siturile noaste, ce tactică și tehnologie vor adopta. Sau pur și simplu, în spiritul clasic, mioritic vor ignora problema, pănă când nu-i va arde la buzunar scăderea încasărilor :-).
Problema e dură, pentru că se pare că o singură reclamă considerată neconformă, pe o pagină, duce la blocarea afișării și a celorlalte reclame pe acea pagină, o mișcare deșteaptă gen „divide et impera” a lui Google, mișcare ce va face ca publisherii să fie foarte selectivi cu reclamele advertiserilor afișate pe pagina lor.
Iar cele mai conforme reclame vor veni, :-) ați ghicit, de la Google...
Lista completă a reclamelor considerate neconforme, care vor fi blocate de Chrome, se poate accesa AICI.

Despre notiunea de closure - in JavaScript

Pentru ca nu prea am gasit mai nimic in limba ramana despre subiect, iata:
Prin closure (in romana cel mei apropiat cuvant ar fi 'apropiere' :-) ) se intelege o functie interna altei functii care are acces la variabilele functiei externe care o inglobeaza .
Daca sunteti familiarizati cu notiunea de scope (daca nu, notiunea de scope defineste 'orizontul' de vizibilitate a unor variabile dintr-o anumita zona de cod JavaScript), atunci o closure (pare a fi de genul feminin :-) ) are trei nivele de vizibilitate:
- scope-up propriu definit de parantezele {}
- variabilele externe apartinatoare functiei care o inglobeaza
- variabilele globale
De asemenea funtia interna - closure are acces si la parametrii functiei externe care o inglobeaza, dar atentie accesul la acestia se face prin referinta !, nu prin valoare.
Exemplu simplu de closure:


function showName (firstName, lastName) {

​var nameIntro = "Your name is ";
// this inner function has access to the outer function's variables, including the parameter​
​function makeFullName () {

​return nameIntro + firstName + " " + lastName;

}
​return makeFullName ();

}
​showName ("Michael", "Jackson"); // Your name is Michael Jackson

sau in jQuery
$(function() {

​var selections = [];
$(".niners").click(function() { // this closure has access to the selections variable​
selections.push (this.prop("name")); // update the selections variable in the outer function's scope​
});

});

 


Pana acum nimc prea spectaculos. Dar, o functie closure poate sa aiba acces la variabilele functiei externe chiar dupa aceasta isi termina executia ! Asta se intampla pentru ca in JavaScript orizontul de vizibilitate a variabilelor (scop chain in English) se pastreaza exact ca in momentul in care functia a fost creata. Sau mai in Romana o functie in Javascript se 'naste' intr-un ecosistem care se pastreaza pana cand garbage collector-ul decide sa o elimine din memorie. Un exemplu care ilustreaza aceasta:


function celebrityName (firstName) {
var nameIntro = "This celebrity is ";
// this inner function has access to the outer function's variables, including the parameter​
function lastName (theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}

​var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​

​// The closure (lastName) is called here after the outer function has returned above​
​// Yet, the closure still has access to the outer function's variables and parameter​
mjName ("Jackson"); // This celebrity is Michael Jackson



O functie closure poate stoca referinte la variabile externe, pe care le poate accesa in varii momente ale executiei codului JavaScript. Si devine interesanta atunci cand valoarea unei variabile externe se schimba inainte de apelul functiei closure. Iata un exemplu de cum poate fi implementata solutia unei veriabile private (apud Douglas Crockford):

 


function celebrityID () {
var celebrityID = 999;
// We are returning an object with some inner functions​
// All the inner functions have access to the outer function's variables​
return {
getID: function () {
// This inner function will return the UPDATED celebrityID variable​
// It will return the current value of celebrityID, even after the changeTheID function changes it​
return celebrityID;
},
setID: function (theNewID) {
// This inner function will change the outer function's variable anytime​
celebrityID = theNewID;
}
}

}

​var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
mjID.getID(); // 999​
mjID.setID(567); // Changes the outer function's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable

Problema apare atunci cand variabila externa accesata prin referinta de functia closure este modificata, de exemplu intr-un ciclu for. De ex.
// This example is explained in detail below (just after this code box).​
​function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function () {
return uniqueID + i;
}
}

return theCelebrities;
}

​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​var stalloneID = createIdForActionCelebs [0];

console.log(stalloneID.id()); // 103
Intentia e ca vectorul returnat sa aiba valorile 100, 101, 102, dar rezultatul obtinut este 103, 103, 103 ! De ce? Pentru ca variabila externa "i" accesata din closure, la momentul accesarii are aceasta valoare ! Nu uitati ca in closure se stocheaza referinte si nu valori ! Pentru a obtine rezultatul scontat vom folosi un alt concept interesant JavaScript, si anume functiile auto-invocabile.
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE​
return function () {
return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array​
} () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.​
} (i); // immediately invoke the function passing the i variable as a parameter​
}

return theCelebrities;
}

​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100​
​var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id); // 101

 


In acest fel se stocheaza o referinta la o functie cu parametrul aferent 'frozen in time' , care ulterior poate fi aplelata in contextul anterior si care va produce rezultetul dorit, adica vectorul 100,101,102.

Articol inspirat de aici: Javascript is sexy
Si nu e ??

Home ← Older posts
Înapoi la site