Mis elementos se crean a partir de datos en un CSV file que se actualiza cada 1 minuto.
Estoy tratando de actualizar estos elementos de la siguiente manera:
CSV fileCSV fileCSV fileEl archivo CSV se ve así:
label,value,market,numbergame A,www.siteA.com,www.webA.com,1 B,www.siteB.com,www.webB.com,2 C,www.siteC.com,www.webC.com,3 D,www.siteD.com,www.webD.com,4 Y un ejemplo de una actualización sería este (Desapareciendo B y D y apareciendo G , Z e Y ):
label,value,market,numbergame A,www.siteA.com,www.webA.com,1 G,www.siteG.com,www.webG.com,2 C,www.siteC.com,www.webC.com,3 Z,www.siteZ.com,www.webZ.com,4 Y,www.siteY.com,www.webY.com,5Para llamar a la función que actualiza cada 1 minuto los datos, uso este script:
<script id="auto-update-csv"> let interval_csv window.addEventListener('DOMContentLoaded', () => { interval_csv = setInterval(refresh_csv, 30000); // refresh every 60 secs }) function refresh_csv() { d3.csv("Lista_de_Jogos.csv", function(data){caixa_suspensa_5(data)}); } </script>El script general que contiene la función llamada por autoupdate para este trabajo (crear los elementos y actualizar) se describe así:
<body style="background-color:black;"> <div style="color:white;font-weight:bold;overflow:hidden;overflow-y:scroll;" class="grid games" id="Lista-de-Jogos-Lateral"> <script id="script-da-caixa-de-selecao-suspensa-5"> var select_5 = d3.select("#Lista-de-Jogos-Lateral") .append("div") .attr("id","select-box-5") .style("width","100%") .style("max-height","574px"); function valorparaiframe(iframevalue) { let link = iframevalue; let newurl = link.split("OB_EV")[1]; newurl = newurl.split("/")[0]; return "https://sports.staticcache.org/scoreboards/scoreboards-football/index.html?eventId=" + newurl; } function caixa_suspensa_5(data) { let update_5 = select_5.selectAll("div") .data(data); update_5.exit().remove(); const div = update_5.enter().append("div").attr("class","matches") div.append("iframe").merge(update_5) .attr("src",d => valorparaiframe(d.value)) .style("width","100%") .style("height","282"); div.append("form").merge(update_5) .attr("action",d => d.market) .attr("target","_blank") .style("width","100%") .append("input").merge(update_5) .attr("type","submit") .attr("target","_blank") .style("width","100%") .attr("value","Jogo Betfair") div.append("form").merge(update_5) .append("input").merge(update_5) .attr("type","text") .attr("id",d => "barra-de-texto-para-grafico-" + d.numbergame) .style("width","100%") div.append("img").merge(update_5) .attr("type","text") .attr("src","https://sitedeapostas-com.imgix.net/assets/local/Company/logos/betfair_logo_transp.png?auto=compress%2Cformat&fit=clip&q=75&w=263&s=c1691b4034fd0c4526d27ffe8b1e839c") .attr("name",d => "grafico-betfair-" + d.numbergame) } d3.csv("Lista_de_Jogos.csv", function(data){caixa_suspensa_5(data)}); </script> </div> </body>Como se ve, traté de trabajar con esta parte en la idea de tratar de actualizar los elementos:
let update_5 = select_5.selectAll("div") .data(data); update_5.exit().remove(); const div = update_5.enter().append("div").attr("class","matches")Pero cuando se activa el gatillo, se convierte en un gran lío con nuevos elementos que se crean y los antiguos no se eliminan correctamente.
¿Qué modelo debo usar para que funcione de acuerdo a mi necesidad?
Yo uso d3.js version 4 :
<script src="https://d3js.org/d3.v4.js"></script> La plantilla de página con varias columnas que se crean y actualizan se vería así:

Pero cuando se actualiza cada 1 minuto, le da la vuelta a esto, totalmente diferente de lo que esperaba, los otros elementos desaparecen y mantienen solo el <iframe> :

Aquí está el <style> usado dentro de <head> para aquellos que quieren más facilidad para recrear la página:
<style> { box-sizing: border-box; } .matches { text-align:center; float: left; width: 355px; border: 1px solid white; border-collapse: collapse; } .column { text-align:center; float: left; width: 355px; border: 1px solid white; border-collapse: collapse; } .grid { float: left; width: 1431px; } .row:after { content: ""; display: table; clear: both; } .button { background-color: #33ccff; color: black; font-weight: bold; } input[type=submit] { background-color: #33ccff; color: black; font-weight: bold; } html { overflow: scroll; overflow-x: hidden; } ::-webkit-scrollbar { width: 0px; /* remove scrollbar space / background: transparent; / optional: just make scrollbar invisible / } / optional: show position indicator in red */ ::-webkit-scrollbar-thumb { background: #FF0000; } </style>"se convierte en un gran lío". Sí lo hará. Veamos parte de su código. Recuerde que cuando usa agregar, devuelve una selección de los elementos agregados:
div // a selection of entered div elements .append("form") // append forms to the divs, return a selection of forms .merge(update_5) // merge a selection of forms with a the update selection of divs ... // style the selection of divs and forms .append("input") // append inputs to the selection of divs and forms .merge(update_5) // merge the update selection of divs with the inputs ... // style the inputs and the divsLo anterior claramente hará que se agreguen elementos en todos los lugares donde no desea.
En primer lugar, fusionamos diferentes tipos de elementos y, debido a esto, en segundo lugar, agregamos entradas a divs y formularios cuando probablemente no queramos. Esencialmente, estamos agregando cosas a todo tipo de cosas que no deberían tener cosas agregadas. Los divs que quedan en cada iteración se contaminan cada vez más con elementos en exceso, y no hay dónde eliminar esos elementos (no debe buscar eliminar esos elementos en exceso, debe buscar no agregarlos en primer lugar).
En lugar de arreglar el código que tiene tal como está, sugeriré un enfoque sugerido por Altocumulus en los comentarios : "En lugar de vincular datos por índice, que es el valor predeterminado, intente usar una función clave para hacer coincidir correctamente los registros de datos": Si los datos de una sola entrada no cambian, no hay necesidad de actualizarlos una vez que se agregan. En su lugar, solo necesitamos eliminar los elementos div para los que se han eliminado los datos, esto significa que solo necesitamos agregar y modificar elementos al ingresar:
let update_5 = select_5.selectAll(".matches") .data(data,d=>d.label); update_5.exit().remove(); // Enter new divs: const enter = update_5.enter() .append("div") .attr("class","matches") ... // Append the children to entered divs: enter.append("form") .attr("action",d => d.market) .attr("target","_blank") .style("width","100%") .append("input") .attr("type","submit") .attr("target","_blank") .style("width","100%") .attr("value","Jogo Betfair"); ...No hay necesidad de fusionar aquí ya que no estamos modificando los elementos existentes si los datos de esos elementos aún existen en la matriz de datos (independientemente del índice).
Aquí hay un ejemplo con todo excepto el iframe (ya que no ha incluido datos de ejemplo con rutas válidas):
let csv1 = d3.csvParse(d3.select("#csv1").remove().text()); let csv2 = d3.csvParse(d3.select("#csv2").remove().text()); let csv = [csv1, csv2]; let i = 0; var select_5 = d3.select("#Lista-de-Jogos-Lateral") .append("div") .attr("id","select-box-5") .style("width","100%") .style("max-height","574px"); function update() { let data = csv[i++%2]; let update_5 = select_5.selectAll(".matches") .data(data,d=>d.label); update_5.exit().remove(); // Enter new divs: const enter = update_5.enter() .append("div") .attr("class","matches") // Append the children to entered divs: enter.append("form") .attr("action",d => d.market) .attr("target","_blank") .style("width","100%") .append("input") .attr("type","submit") .attr("target","_blank") .style("width","100%") .attr("value","Jogo Betfair"); enter.append("form") .append("input") .attr("type","text") .attr("id",d => "barra-de-texto-para-grafico-" + d.numbergame) .style("width","100%") .attr("value", d=>d.label); enter.append("img") .attr("type","text") .attr("src","https://sitedeapostas-com.imgix.net/assets/local/Company/logos/betfair_logo_transp.png?auto=compress%2Cformat&fit=clip&q=75&w=263&s=c1691b4034fd0c4526d27ffe8b1e839c") .attr("name",d => "grafico-betfair-" + d.numbergame); } update(); setInterval(update,2000); { box-sizing: border-box; } .matches { text-align:center; float: left; width: 355px; border: 1px solid white; border-collapse: collapse; } .column { text-align:center; float: left; width: 355px; border: 1px solid white; border-collapse: collapse; } .grid { float: left; width: 1431px; } .row:after { content: ""; display: table; clear: both; } .button { background-color: #33ccff; color: black; font-weight: bold; } input[type=submit] { background-color: #33ccff; color: black; font-weight: bold; } html { overflow: scroll; overflow-x: hidden; } ::-webkit-scrollbar { width: 0px; /* remove scrollbar space / background: transparent; / optional: just make scrollbar invisible / } / optional: show position indicator in red */ ::-webkit-scrollbar-thumb { background: #FF0000; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.1.1/d3.min.js"></script> <div style="color:white;font-weight:bold;overflow:hidden;overflow-y:scroll;" class="grid games" id="Lista-de-Jogos-Lateral"> <pre id="csv1">label,value,market,numbergame A,www.siteA.com,www.webA.com,1 B,www.siteB.com,www.webB.com,2 C,www.siteC.com,www.webC.com,3 D,www.siteD.com,www.webD.com,4</pre> <pre id="csv2">label,value,market,numbergame A,www.siteA.com,www.webA.com,1 G,www.siteG.com,www.webG.com,2 C,www.siteC.com,www.webC.com,3 Z,www.siteZ.com,www.webZ.com,4 Y,www.siteY.com,www.webY.com,5</pre> </div>Lo anterior usa d3.csvParse() en lugar de d3.csv() para crear datos csv, lo que permite que el fragmento sea completamente autónomo, lo que requiere el uso de elementos pre para contener los datos csv .