Implementarea trasării și căderii HTML5

Una dintre noile caracteristici din HTML5 este drag and drop nativ. Surprinzător, Internet Explorer a avut suport pentru drag and drop nativ de la versiunea 5.5; de fapt, implementarea HTML5 se bazează pe suportul IE. În tutorialul de astăzi, vom examina modul în care vom implementa drag and drop nativ pentru a construi o interfață simplă de coș de cumpărături.


Pasul 0. Ce facem

Iată ce vom construi: este un coș de cumpărături de bază, cu un panou de produse și un panou de coș. Pentru a "achiziționa" un produs, îl veți putea glisa din panou în coș; vom păstra o evidență a cantității și vom elimina elementele din panoul de produse atunci când vor ieși din stoc.

Rețineți că nu construim un coș de cumpărături aici; nu vom lucra astăzi cu nici o bunătate a serverului. Acesta este doar front-end; punctul este drag and drop HTML5.


Pasul 1. HTML-ul

Desigur, vom începe cu codul HTML; aici este shell-ul nostru:

     Trageți și aruncați coșul de cumpărături        

Destul de simplu: conectăm o foaie de stil și o jQuery; folosim doar jQuery pentru manipularea ușoară a evenimentelor și manipularea DOM; drag and drop va fi nativ. Cu toate acestea, suntem împotriva unui zid aici, deoarece drag-and-drop-ul HTML5 adaugă câteva proprietăți obiectului evenimentului. JQuery nu utilizează obiectul implicit al evenimentului; creează propriile sale pentru a egaliza problemele legate de evenimente. Din acest motiv, nu obținem proprietățile speciale cu obiectul evenimentului jQuery. Dar nu vă faceți griji; există un plugin pentru asta; noi tragem Dragul și Dropul Nativ pentru a face totul de lucru. În final, vom include scriptul nostru: dragdrop.js.

Acum suntem gata să adăugăm în lista noastră de produse; pentru imaginile produselor, folosesc icoane din Apple Icon Superpack, creat de SvenVath. (Am redenumit icoanele cu nume mai simple și le-am redimensionat la 180 de pixeli.)

Adăuga ul # produse ca primul copil din interiorul corpului. Odată ce ați făcut acest lucru, vom examina primul element de listă:

 
  • iMac

    Preț: $ 1199.00

    Cantitate: 10

  • Am ieșit din listă, cu o ancoră înăuntru; observați că fiecare element de ancorare va avea o clasă de articol (important când ajungem la CSS) și un id personalizat (important când ajungem la JavaScript). Apoi, ancora are și draggable = "true" atribut; acest lucru ar trebui să fie tot ceea ce avem nevoie pentru a face acest element draggable (vom vedea câteva avertismente în curând). Folosim aici o etichetă de ancoră, pentru a putea face ceva pentru browsere fără suport nativ drag and drop (deși nu vom face asta aici). Apoi, avem imaginea produsului și o div cu informații despre produs. Și da, este necesar să împachetați prețul și cantitatea cu deschidere

    Iată restul elementelor din listă:

     
  • iPhone

    Preț: $ 199.00

    Cantitate: 16

  • AppleTV

    Preț: $ 299.00

    Cantitate: 9

  • Afișare cinematografică

    Preț: $ 899.00

    Cantitate: 4

  • iPod Nano

    Preț: $ 149.00

    Cantitate: 20

  • macbook

    Preț: $ 1199.00

    Cantitate: 13

  • Mac Mini

    Preț: $ 599.00

    Cantitate: 18

  • Există o ultimă bucată de cod HTML: coșul de cumpărături:

     

    Cărucior de cumpărături

      Total: $0.00


      Drop aici pentru a adăuga în coș

      Și asta e HTML-ul nostru!


      Full Screencast



      Pasul 2. CSS

      În mod ideal, tot ce trebuie să faceți pentru a face un element draggable este setat ca atributul draggable la adevărat; totuși, există mai mult. Pentru ca lucrurile să funcționeze corect, trebuie să setați câteva lucruri în CSS. În primul rând, gândiți-vă la acest lucru: ce faceți clic și trageți pe elementul "normal" (de neegalat)? De obicei, selectează textul. Apoi, vrem să ne asigurăm că tragem elementul, și nu doar conținutul său. Pentru a face față, trebuie să utilizați următorul CSS:

       [draggable = true] -moz-user-select: nici unul; -webkit-user-select: nici unul; -webkit-user-drag: element; 

      Pentru comoditatea noastră, pluginul drag and drop pe care îl folosim, le stabilește pentru noi, astfel încât să le putem lăsa afară dacă vrem. Cu toate acestea, vom face acest lucru:

       [draggable = true] cursor: muta; 

      Să începem stilul:

       html înălțime: 100%;  corp background: #ececec; margin: 0; padding: 0; font: 13px / 1.5 helvetica, arial, san-serif; înălțime: 100%;  h1, h2 text-aliniere: centru;  h2 poziție: absolută; fund: 20px; Culoare: #fff; text-umbra: 0 0 10px rgba (0, 0, 0, 0.75); display: none;  p margine: 0; 

      Deoarece nu utilizăm o resetare completă, iată pseudo-resetarea noastră. Ar trebui să fie destul de auto-explicator. Setăm înălțimea la 100% pe html și corp elemente pentru că vrem #cart pentru a fi întreaga înălțime a ecranului; pentru a face acest lucru, fiecare element părinte trebuie să aibă înălțimea setată la 100%. De asemenea, observați că folosim rgba pentru a seta culoarea umbrei; dacă un browser nu acceptă acest lucru, nu va fi o umbră pe h2. Și, ascundem asta h2 prin setare afișare: niciuna. Amintiți-vă, h2 spune "Puneți aici pentru a adăuga în coș", așa că o să lăsăm să se estompeze atunci când tragerea începe și se estompează când se termină tragerea.

      Trecerea la lista produselor noastre ...

       #products float: left; list-style: none; lățime: 65%; padding: 0;  #products li display: inline; 

      Din nou, destul de evident. Lucrul important din acest fragment este că elementele din listă vor fi afișate în linie. De când ne vom stabili display: block; plutește la stânga pe ancore, IE va da elementelor din listă un efect "scări-trepte"; putem rezolva problema prin setarea afișare: inline pe elementul părinte al ancorei.

      Vorbind despre ancore, hai să le urmăm.

       .element display: bloc; plutește la stânga; lățime: 180px; height: 180px; margin: 10px; frontieră: 1px solid # 494949; text-align: center; text-decoration: none; culoare: # 000; overflow: ascuns;  .it img graniță: 0; margine: 10 pixeli automat; lățime: 160px; height: 160px;  .item div fundal: rgb (0, 0, 0); fundal: rgba (0, 0, 0, 0,5); Poziția: relativă; bottom: 69px; Culoare: # f3f3f3; padding: 5px 0; display: none; 

      Fiecare ancora va fi desenată ca o cutie de 180x180px; acest lucru va fi completat cu imaginea produsului. Divizia info div va fi poziționată deasupra imaginii, în partea de jos a pătratului. Observați că trebuie să setăm o culoare de fundal și apoi să o resetăm pentru browserele moderne, deoarece IE nu suportă RGBa. Suntem aici display: none pe această diviziune info, astfel încât să o putem distruge și să ieșim atunci când "clientul" se apleacă și se oprește, respectiv.

      Tot ce a rămas în stil este coșul de cumpărături; ne vom uita aici, dar, amintiți-vă, elementele din listă vor fi inserate mai târziu de jQuery, astfel încât să nu vedeți că acest lucru are loc încă.

       #cart float: right; culoare de fundal: #ccc; lățime: 25%; umplutură: 0 5%; înălțime: 100%;  #cart ul umplutura: 0;  #cart li style-style: none; frontieră de fund: 1px solid # 494949; padding: 5px;  #cart .quantity font-weight: bold; padding-dreapta: 10px; marja de-dreapta: 10px; frontieră-dreapta: 1px solid # 494949; display: inline-block; lățime: 15px; text-align: dreapta;  #cart .price float: right;  #total float: right; 

      Elemente cu clasele cantitate și Preț va fi în cadrul elementelor din listă introduse dinamic.

      Asta e pentru CSS; înainte de a trece la vedeta acestui spectacol, să ne uităm la munca noastră până acum.



      Pasul 3. JavaScript-ul

      Am ajuns la JavaScript; conform medicului HTML5:

      HTML 5 DnD se bazează pe implementarea originală a Microsoft, disponibilă încă din Internet Explorer 5! Actualmente acceptate în IE, Firefox 3.5 și Safari 4.

      Iată o listă a evenimentelor pe care ofertele de tragere și plasare HTML5 le oferă:

      • trage
      • dragstart
      • trage peste
      • dragenter
      • dragleave
      • dragend
      • cădere brusca

      Nu vom folosi toate astea, dar vom vedea cum acționează cei mai mulți dintre ei.

      În primul rând, vom lucra cu produsele noastre:

       $ ('. item') .bind ('dragstart', funcția (evt) evt.dataTransfer.setData ('text', this.id); .hover (funcția () $ ('div', aceasta) .fadeIn ();, funcția () $ ('div', aceasta).

      Începem prin a prinde toate elementele; apoi, legăm o funcțiune la dragstart eveniment; acest eveniment se va declanșa când începem să tragem evenimentul. Primul lucru pe care îl vom face când un obiect este tras este setat unele date; de fapt, dacă nu sunt setate date, Firefox nu va lăsa elementul să tragă. Special pentru a trage evenimente este o proprietate obiect la obiectul eveniment numit transfer de date; vom folosi două metode ale acestei proprietăți: setData și Obțineți date. Aici, folosim setData , care ia doi parametri: un format de date și datele. Vom folosi textul dat. Apoi, vom seta datele ca id-ul elementului pe care utilizatorul îl trage. Apoi, vom dispărea h2 ca un prompt pentru client.

      De asemenea, folosim metoda jQuery hover pentru a estompa informațiile despre produs atunci când ne mișcăm mouse-ul și se estompează atunci când vom mouseout. Observați că treceți nodul pe care îl plasăm în context, așa că obținem numai divul de produs potrivit.

      Acum, să adăugăm manipulatorii de evenimente la #cart. Vom acționa asupra trage peste, dragenter, și cădere brusca evenimente:

       $ 'cart') .bind ('dragover', funcția (evt) evt.preventDefault ();) .bind ('dragenter', function (evt) evt.preventDefault 'drop', funcția (evt) );

      Pentru a declanșa evenimentul drop, trebuie să anulați acțiunea implicită pe trage peste eveniment; acest eveniment se declanșează continuu pe ținta de cădere atunci când un element trasabil este tras peste el. Numai pentru IE, trebuie să anulați acțiunea implicită pe dragenter eveniment, care apare numai atunci când elementul draggable intră în țintă. Motivele care stau la baza anulării acțiunii prestabilite sunt oarecum limpede; pentru a fi sincer, eu nu le înțeleg cu adevărat. Iată ce spune Remy Sharp despre asta:

      Ceea ce spune browser-ului este că acest element este cel pe care dorim să-l capturăm, în caz contrar browserul merge mai departe, este o acțiune normală de tragere. Prin anularea evenimentului, acesta îi spune browserului că acesta este elementul care ar trebui să înceapă să se miște.

      Ar trebui să observ că jQuery ne ajută să ieșim puțin aici; în mod normal, ar trebui, de asemenea return false aceasta funcționează în IE; totuși, jQuery este fix preventDefault face asta pentru noi.

      Acum cădere brusca eveniment; acest eveniment este, de asemenea, tras pe țintă, care este div # coș în cazul nostru. Înainte de a ne uita la funcție, să vorbim despre ce ar trebui să facă această funcție:

      • obțineți produsul pe care l-am scăpat
      • dacă produsul a fost deja cumpărat, adăugați unul la cantitatea cumpărată; în caz contrar, adăugați articolul în coș
      • reduce cantitatea de produs
      • actualizați prețul total

      Iată prima parte:

       var id = evt.dataTransfer.getData ('text'), item = $ ('#' + id), cartList = $ ("# cart ul"), total = $ ("p: eq (1) span", element) .text (), prevCartItem = null, notInCart = (function () var lis = $ ('li', cartList), len = lis.length; (i = 0; i < len; i++ )  var temp = $(lis[i]); if (temp.data("id") === id)  prevCartItem = temp; return false;   return true;  ()), quantLeftEl, quantBoughtEl, quantLeft;

      Stiu; sunt o mulțime de variabile, dar le vom folosi pe toate. Să trecem peste ei; în primul rând, primim datele de text pe care le-am transferat cu evenimentul; am transferat id-ul de genul acesta deoarece nu există nimic în obiectul evenimentului care să ne spună care element a fost abandonat pe ținta noastră; vom primi id-ul și apoi îl vom folosi pentru a găsi elementul care a fost abandonat. Apoi, vom obține lista de coșuri și intervalul de preț total. Apoi, vom obține prețul elementului individual; știm că este intervalul de timp din al doilea paragraf al elementului, astfel încât să putem folosi elementul ca parametru de context. Vom fixa prevCartItem pentru nulă pentru moment, dar o vom folosi pentru a vedea dacă elementul atras în cărucior este deja acolo. Valoarea a notInCart este o funcție anonimă invocată de sine; se va loop peste fiecare element din listă din cartList (din nou, vom folosi parametrul context) și vom verifica dacă proprietatea datelor id este aceeași cu variabila id. Pentru a înțelege acest lucru, va trebui să știți că atunci când adăugăm elemente de listă în coșul de cumpărături, vom folosi jQuery date metodă pentru a seta id-ul de produs al magazinului cu elementul. În această funcție, verificăm un element de listă cu datele corecte; dacă găsim una, elementul este deja în cărucior, așa că am stabilit notInCart la fals; dacă nu este în cărucior, vom seta variabila la true. În cele din urmă, vom folosi quantLeftEl, quantBoughtEl, și quantLeft la actualizarea cantităților.

      Acum, pentru o acțiune:

       $ ( "H2") fadeOut ( 'rapid').; dacă (notInCart) prevCartItem = $ ('
    • ', text: $ (' p: primul ', element) .text (), date: id: id', ' clasa ':' cantitate ', text:' 0 ')) prepend ($ ('', ' class ':' price ', text: price)) appendTo (cartList);
    • În primul rând, vom dispărea h2 prompt. Apoi, dacă articolul nu este în coș, îl vom adăuga în cărucior. Pentru a face acest lucru, vom crea un element de listă; atunci putem trece un obiect literal ca al doilea parametru pentru a seta proprietățile noului nostru element de listă. Vom seta textul pe numele produsului; care provine de la primul paragraf din elementul de produs. Apoi, am stabilit datele despre care am vorbit mai sus.

      Apoi, vom prependa o deschidere la acest element de listă; îi vom da o clasă de "cantitate" (nu uitați să puneți clasa în citate, deoarece este un cuvânt rezervat) și setați textul la zero; da, știu că ar trebui să fie una, din moment ce au pus articolul în coș, dar vom crește mai târziu ... și veți vedea de ce.

      Vom adăuga o altă perioadă de timp la elementul de listă, de data aceasta pentru preț. Vom flota prețul la dreapta, așa că ar părea logic adăuga aceasta; dar acest lucru ar provoca un bug float în IE; spanul va pluti în mod efectiv chiar și sub elementul din listă.

      În cele din urmă, vom adăuga elementul listă în lista cu coșuri de cumpărături.

      Ultima piesă a acestei funcții se va executa dacă articolul este sau nu în cărucior sau nu:

       quantLeftEl = $ ('p: ultimul interval', element); quantLeft = parseInt (quantLeftEl.text (), 10) - 1; quantLeftEl.text (quantLeft); quantBoughtEl = $ ('cantitate', prevCartItem); quantBoughtEl.text (parseInt (quantBoughtEl.text (), 10) + 1); dacă (quantLeft === 0) item.fadeOut ('rapid') remove ();  total.text ((parseFloat (total.text (), 10) + parseFloat (price.split ('$') [1])). evt.stopPropagation (); return false;

      Mai întâi vom obține cantitatea din elementul de produs; aceasta este cantitatea rămasă când clientul a tras articolul în cărucior; atunci vom obține cantitatea care a mai rămas; dar este un șir, deci folosim funcția nativă parseInt pentru ao converti la un număr (utilizați 10 ca radix pentru a vă asigura că obținem un număr zecimal) și scădeați unul din acesta. Apoi resetați cantitatea folosind jQuery text metodă.

      Apoi, obținem cantitatea cumpărată de utilizator; acesta este elementul cu o clasă de "cantitate"; noi folosim prevCartItem ca context; aceasta funcționează pentru că dacă articolul era deja în coș, prevCartItem a fost stabilit în acea funcție anonimă; dacă nu era în coș, l-am setat când am creat intrarea cartului. Apoi, putem seta valoarea textului prin obținerea valorii curente, convertirea la un număr și adăugarea unei valori.

      Ce se întâmplă când cantitatea rămasă atinge zero? Dacă e zero, vom deconecta elementul și îl vom elimina.

      În cele din urmă, trebuie să actualizăm prețul total. Avem dimensiunea totală, astfel încât să putem reseta textul; ceea ce facem este obtinerea textului curent, convertirea lui la un numar (de data aceasta utilizam parseFloat pentru a păstra cenți), împărțind semnul dolarului de pe preț și convertindu-l la un număr și adăugând două valori. În cele din urmă, vom folosi toFixed pentru a vă asigura că afișăm întotdeauna valoarea corectă a cenților.

      În cele din urmă, nu vrem cădere brusca drop event la bubble, așa că vom opri propagarea și returnarea false;


      Etapa 4. Proiectul finalizat

      Bună treabă, am terminat; iată o lovitură a căruței noastre în acțiune:


      Dacă doriți să verificați ce alte evenimente de tragere și plasare fac, adăugați-le la script:

       $ ('# cart'). bind ('dragleave', funcția (evt) console.log ('dragleave');); ('dragstart', funcția (evt) console.log ('dragstart')). ;) .bind ('drag', funcția (evt) console.log ('drag'););

      În timp ce drag and drop-ul HTML5 nativ poate să nu fie complet pregătit pentru prima dată încă (Opera nu o acceptă), este cu siguranță interesant să vezi unde se întâmplă lucrurile!

      Cod