0

I have two dimensions of data, one is the purchase itself and the second is the referral reference. Orders are produced with PDO and taken from a MySQL database:

<div rfrnc="joe" id="1" class="order"><div>1 Laptop $220</div><div class="time">10.25</div></div>
<div rfrnc="bill" id="2" class="order"><div>1 Phone $520</div><div class="time">10.10</div></div>
<div rfrnc="joe" id="3" class="order"><div>1 Headset $220</div><div class="time">9.20</div></div>
<div rfrnc="bill" id="4" class="order"><div>1 Laptop $220</div><div class="time">9.02</div></div>
<div rfrnc="joe" id="5" class="order"><div>1 Laptop $220</div><div class="time">11.02</div></div>

Using ajax the above html would be parsed directly into a live feed on my website, sorted desc by orderid or date purchased, through 3 types of api calls of the same function (depending on the parameters to identify each operation):

  1. initial load (5 entries) & load more button (+5 each time)

    $(".wrapper").append(html);  
    
  2. websocket ping which only triggers the same api call and retrieval from mysql (returning just one order - live feed)

    $(".wrapper").prepend(html); 
    
  3. websocket update when the order is already on the live feed but the status changes

    $("#" + orderid).replaceWith(html);
    

The problem I have is how to convert the html divs to display and group it per reference as below.

<div rfrnc="joe">
<div id="5" class="order">...</div>
<div id="3" class="order">...</div>
<div id="1" class="order">...</div>
</div>
<div rfrnc="bill">
<div id="4" class="order">...</div>
<div id="2" class="order">...</div>
</div>

I tried all sort of things, like rewriting the source php html, rewriting the sql query to use group_concat as well as using wrapAll and similar jquery stuff to modify the html after it already comes back from ajax call but no luck. There is further ajax action when clicking on either the order to change the status of it. I need the same on the group level for all the orders having the same reference. Sorry if I did not explain it well.

jq1080
  • 47
  • 6
  • At this point I am even open to switching to JSON data instead of parsing html and move all the loops and html to jQuery. – jq1080 Dec 29 '21 at 21:17

2 Answers2

1

This solution loops the existing entries and groups them by the data-rfrnc property:

const people = {};

const html = `<div data-rfrnc="joe" id="1" class="order"><div>1 Laptop $220</div><div class="time">10.25</div></div>
<div data-rfrnc="bill" id="2" class="order"><div>1 Phone $520</div><div class="time">10.10</div></div>
<div data-rfrnc="joe" id="3" class="order"><div>1 Headset $220</div><div class="time">9.20</div></div>
<div data-rfrnc="bill" id="4" class="order"><div>1 Laptop $220</div><div class="time">9.02</div></div>
<div data-rfrnc="joe" id="5" class="order"><div>1 Laptop $220</div><div class="time">11.02</div></div>
`
const domParser = new DOMParser();
const doc = domParser.parseFromString(html, "text/html")


Array.from(doc.querySelectorAll('body > div')).forEach(div => {
  const person = div.getAttribute('data-rfrnc');
  if (!people[person]) {
     people[person] = [];
  }
  people[person].push(div)
})


const docFrag = document.createDocumentFragment();
for(let [person, elements] of Object.entries(people)) {
  const personElement = document.createElement('div');
  personElement.setAttribute('data-rfrnc', person);
  elements
    .sort((a,b) => a.getAttribute('id') < b.getAttribute('id'))
    .forEach(element => personElement.appendChild(element));
  docFrag.appendChild(personElement);
}

document.body.appendChild(docFrag)
first last
  • 396
  • 2
  • 5
  • Thanks a lot, i like the fact that it dynamically creates group div elements. I think it works to an extent, but would it be possible to use jQuery in order to manipulate and sort the html from ajax before attaching it to DOM? It just needs to check the divs in success(html) and then decide if it's gonna be attached to an existing group element or a new group element will be added (in case it doesn't exist or it's too old). – jq1080 Dec 30 '21 at 09:02
  • The better solution would be to return JSON and create the DOM from the JSON. Returning HTML from the server is sub-optimal if you are going to be manipulating it before it's added to the DOM. I think using a reactive front-end library like Vue, React, or Preact makes a lot of sense for you. These are data-driven libraries that can easily change how you render DOM based on changes in your data. – first last Dec 30 '21 at 11:26
  • I edited the solution to use an HTML string instead of DOM. – first last Dec 30 '21 at 11:40
0

I fully support @firstlast in his comment that sending JSON data and processing it in the front end would be the better solution.

Just in case it is of interest to anyone, here is another way of converting the received html string back to a JavaScript array of arrays:

const html=`<div rfrnc="joe" id="1" class="order"><div>1 Laptop $220</div><div class="time">10.25</div></div>
<div rfrnc="bill" id="2" class="order"><div>1 Phone $520</div><div class="time">10.10</div></div>
<div rfrnc="joe" id="3" class="order"><div>1 Headset $220</div><div class="time">9.20</div></div>
<div rfrnc="bill" id="4" class="order"><div>1 Laptop $220</div><div class="time">9.02</div></div>
<div rfrnc="joe" id="5" class="order"><div>1 Laptop $220</div><div class="time">11.02</div></div>`;

const dom=document.createElement("div");
dom.innerHTML=html
  
const data=[...dom.children].map(d=>[d.id,d.getAttribute("rfrnc")].concat([...d.children].flatMap(c=>c.textContent.split(" ")))
);

// show the result:
console.log(["id","rfrnc","qty","descr","price","time"]);
console.log(data);
Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
  • I would definitely consider the ramifications of using `innerHTML`, when receiving HTML from the server: https://stackoverflow.com/questions/37249170/advantages-to-domparser-vs-template-innerhtml – first last Jan 08 '22 at 14:09
  • @firstlast, thanks for sharing the link comparing `new DOMParser()` with `innerHHTML`. Where would be the concrete danger in using `innerHTML` in this particular use case? No scripts will be triggered in any conceivable way. Therefore I cannot imagine any "bad" things happening here. It is definitely always worth looking at possible vulnerabilities but I also don't believe in being scared of things that aren't even there. – Carsten Massmann Jan 08 '22 at 20:48
  • 1
    `innerHTML` will accept anything and never produce an error. It's simply unpredictable when presented with malformed or invalid HTML. Producing HTML strings on the server is often error-prone. DOMParse at least allows you the opportunity to catch malformed HTML errors. – first last Jan 09 '22 at 00:39