Viste backbone e DOM parte 2

Nell'ultimo articolo, abbiamo trattato le nozioni di base delle Viste Backbone e come abilitare jQuery a manipolare senza problemi lo stesso DOM manipolato da Backbone.

In questo articolo, esploreremo come possiamo fare in modo che Backbone funzioni bene con d3.js. I concetti in questo articolo dovrebbero applicarsi a situazioni in cui si intende utilizzare Backbone con la maggior parte delle altre librerie che manipolano anche il DOM.

Backbone + d3.js Lavorare sullo stesso DOM

d3.js, scritto da Mike Bostock, è un'altra libreria ampiamente utilizzata che manipola il Document Object Model, principalmente per visualizzare i dati. In effetti, puoi diventare davvero creativo con il modo in cui i dati possono essere visualizzati.

Al livello più basso, d3.js interagisce con gli elementi HTML e / o SVG e ne modifica gli attributi associando i dati al modello di oggetto documento.

Ecco un rapido esempio di come d3 opera:

var numericalData = [1,2,3,4,5,6]; d3.select ("body"). selectAll ("p") .data (numericalData) .enter () .append ("p") .text (function (d) return "I'm number" + d + " ! ";); 

Il codice precedente fa quanto segue:

  1. Seleziona e crea un riferimento al corpo elemento
  2. All'interno di quella selezione, seleziona tutto il p elementi attualmente nel DOM
  3. Aggiunge ogni elemento in data numerica a un selezionato p elemento
  4. Per ciascuno p elemento che deve ancora esistere (ad esempio, alcuni elementi in data numerica che deve ancora essere aggiunto), crea a p elemento e lo aggiunge il DOM
  5. Imposta il nodo di testo in ogni nuovo creato p elemento per contenere del testo (incluso il numero pertinente in data numerica)

Un primo tentativo di rendere Backbone e d3.js Gioca bene

Sfruttando ciò che abbiamo imparato nel precedente articolo, ecco una implementazione di una manipolazione DOM condivisa.

var CoolView = Backbone.View.extend (rendering: function () var html = "

Io non sono un numero!

"; questo. $ el.html (html); return this;, renderVisualization: function (arrayOfData) d3.select (" body ") .selectAll (" p ") .data (arrayOfData) .enter () .append ("p") .text (function (d) return "I'm number" + d + "!";);); var coolView = new CoolView (); coolView.render (); var myData = [10, 9, 4, 2, 1]; coolView.renderWithD3 (myData);

Houston, abbiamo un problema!

Supponendo che il nostro obiettivo sia preservare l'esistente p elemento e aggiungere l'altro p elementi al DOM, quando eseguiamo il codice precedente, ci imbattiamo in un grosso problema.

Il .render () inserti un p elemento con il testo "Io non sono un numero" nel DOM. Ma .renderVisualization () seleziona tutto esistente p elementi nel DOM e inserisce il contenuto in questi elementi. Questo sovrascrive il testo nell'originale p elemento, nonostante il nostro aggiungendo al DOM usando d3.append ().

Soluzioni per ottenere d3.js e Backbone per giocare bene insieme

In questo semplice esempio esistono almeno due soluzioni semplici.

  1. Usa un selettore CSS più specifico
  2. Utilizzare un tag diverso del tutto

Separazione di una porzione del DOM

Per esempi più complicati, potremmo aver bisogno di strategie alternative. Una possibile soluzione è quella di isolare una porzione del DOM che sarà gestita da una delle librerie. Per esempio:

var CoolView = Backbone.View.extend (... renderVisualization: function (arrayOfData) d3.select ("# visualizzazione") .selectAll ("p") .data (arrayOfData) .enter () .append ("p") .text (function (d) return "I'm number" + d + "!";);); var coolView = new CoolView (); var myData = [10, 9, 4, 2, 1]; coolView.renderVisualization (myData); 

Nel caso precedente, Backbone continua a gestire la vista da creare. Tuttavia, diciamo che c'è un elemento da qualche parte nel DOM (all'interno o al di fuori del DOM gestito dalla vista Backbone) il cui id è "visualizzazione". Possiamo capitalizzare questo fatto esaminando tutte le nostre manipolazioni DOM relative a d3 a questo elemento. Di conseguenza, tutti i metodi con d3 concatenati, inclusi .selectAll ( "p") e .append ( "p"), sono eseguiti nel contesto di #visualization.

Incapsulare la manipolazione DOM differenziata con le sottoview

Infine, un altro approccio alla gestione della funzionalità di visualizzazione di terze parti è l'utilizzo di visualizzazioni secondarie. Ecco come potrebbe essere.

var CoolView = Backbone.View.extend (rendering: function () var subViewA = new SubViewA (); var subViewB = new SubViewB (); // imposta il contenuto html per coolview this. $ el.html (subViewA.render () .el); // aggiungi altri contenuti html per rinfrescarlo. $ el.append (subViewB.render (). el); return this;); // Altrove, forse nel tuo router ... var coolView = new CoolView (); $ ( 'App') html (coolView.render () el.).; 

In questo scenario, subViewA potrebbe contenere contenuto non di visualizzazione, e subViewB potrebbe contenere contenuto di visualizzazione.

Un altro approccio di manipolazione DOM condiviso per Backbone + d3.js

Ci sono momenti in cui è necessario assicurarsi che la manipolazione del DOM d3 avvenga nello stesso contesto della vista Backbone stessa. Ad esempio, è sufficiente isolare una parte del DOM con #visualization non fornisce alcuna garanzia che l'elemento esista all'interno o all'esterno della parte del DOM rappresentata dalla vista Backbone.

Un'alternativa è garantire che la selezione iniziale di d3 faccia riferimento allo stesso elemento di quello a cui punta questo. $ el. Tuttavia, se tendi a non impostare EL o uno qualsiasi dei suoi attributi direttamente, sarebbe difficile orientare sufficientemente la vista corrente usando CSS.

Fortunatamente, nella libreria d3.js esiste un metodo che abilita la selezione diretta del nodo. Lasciami presentarti d3.select (nodo). Secondo la documentazione, questo metodo:

Seleziona il nodo specificato. Ciò è utile se si dispone già di un riferimento a un nodo, ad esempio d3.select (this) all'interno di un listener di eventi o globale come document.body. Questa funzione non attraversa il DOM.

Questo metodo ci consente di scrivere il seguente codice e assicurare che le manipolazioni d3 si verifichino nel contesto della vista Backbone.

var CoolView = Backbone.View.extend (// nota che "el" non è impostato direttamente render: function () var html = "

Io non sono un numero!

"; questo. $ el.html (html); restituisce this;, renderVisualization: function (arrayOfData) d3.select (this); // non funziona," this "fa riferimento a un oggetto Backbone d3.select (questo .el) // passa in un nodo di riferimento .selectAll ("p") .data (arrayOfData) .enter () .append ("p") .text (function (d) return "I'm number" + d + "!";););

Conclusione

In sintesi, ci sono una serie di considerazioni quando si rende il Backbone compatibile con altri manipolatori DOM.

  1. Assicurati di selezionare l'elemento giusto da manipolare. L'utilizzo di classi e ID CSS specifici può semplificare la vita.
  2. Gli usi intelligenti dei vari metodi di manipolazione DOM in entrambe le librerie possono fare molto. Assicurati di chiarire l'azione del DOM (aggiungi, anteponi, inserisci, rimuovi) di cui hai veramente bisogno.
  3. Le operazioni di looping nel DOM possono essere complicate. Assicurarsi che gli elementi corretti siano influenzati.

Ci sono anche un certo numero di alternative da considerare, a seconda del risultato che si cerca:

  1. Separare una porzione del DOM
  2. Applicare la manipolazione DOM dell'altro libreria al contesto Backbone
  3. Utilizzo di viste secondarie per incapsulare l'impatto di altri manipolatori DOM