1

Context

I am making an application for showing a synchronized HTML5 slideshow to about 50 spectators in a wireless LAN with no internet access.

I run a Node.js server in one of the computers and connect with the 50 clients via Socket.IO (Btw, only one of them controlls the presentation).

The hardware is a domestic wireless 802.11b/g router and 50 mobile devices (tablets, netbooks, smartphones).

Problem

When the slideshow starts, it takes too long (about 10 minutes or more for a 5 MB slideshow) for the clients to see it, since the router has to send the complete slideshow to all the clients at the same time.

How my slideshow looks

<html>
  <head>
    <title>My Slideshow</title>
    <script src="javascripts/slidesplayer.js"></script>
    <link rel="stylesheet" href="/stylesheets/style.css">
  </head>

  <body>    
    <div id="slides-containter">

      <div class="slide" id="slide_1">
        <!--Contents such as images, text, video and audio sources -->
      </div>

      <div class="slide" id="slide_2">
        <!--Contents -->
      </div>

      <!--A bunch of slides here-->

    </div>
    <script>
      // Here I load the slides
    </script>
  </body>
</html>

What I would like to do

At the beginning, I would like to load the slides-container element completely empty.

Then, as I advance through the slideshow, I'd like to GET from the server the div representing the next slide, and append it to the DOM so that only when that is done, the client starts to download the pictures and othet stuff only for that slide (thus, decreasing significantly my network overload).

Another relevant fact is that the slideshow (including the slidesplayer.js) is automatically generated from an external software that parses PowerPoint presentations to this HTML5 format and that we will use a lot of presentations that are already made in PowerPoint.

My first impression is that I should accomplish this by using jQuery-ajax, but I don't know exactly how to do it the good way, since my idea is just copying the div.slide elements in separate files.

Update: This answer suggests using jQuery for DOM manipulation before displaying. It seems that jQuery requests the resources everytime you manipulate a DOM object, even if it is not inserted into your current DOM. So, one possible solution would be working only with strings. You can see more about this issue in this and this questions.

Community
  • 1
  • 1
Sam
  • 1,222
  • 1
  • 14
  • 45

3 Answers3

2

One solution would be to treat this as a front-end solution. The front-end should arguably only eat as much as it can take at any one time.

I'm assuming it's external resources (imagery etc) as opposed to the slideshow markup itself that's making up the most of those 5MB, in which case the DOM should not attempt to call those resources until they are necessary.

I would suggest serving the whole slide document to an ajax call but only introducing the markup to each slide as it is called. Something like this:

$.ajax('path/to/slides', {
  async:    false,
  complete: function ajaxCallback(slidesDOM){
    // Pull out the individual slides from your slideshow HTML 
    $slides = $(slidesDOM).find('.slide');

    // For each of these...
    $slides.each(function prepareSlide(){
      // Store a reference to the slide's contents
      var $slideContent = $($(this).html());
      // Empty the contents and keep only the slide element itself
      var $slideWrapper = $(this).empty();

      $slideWrapper
        // Put the slide where you want it
        .appendTo('.slidesContainer')
        // And attach some kind of event to it 
        // (depending on how your slideware works, you might want to bind this elsewhere)
        .on('focus', function injectContent(){
          // Put the content in — NOW external resources will load
          $slideWrapper.append($slideContent);

          // Unbind this function trigger
          $slideWrapper.off('focus', injectContent);
        });
    })
  }
});
Barney
  • 16,181
  • 5
  • 62
  • 76
  • Thanks! Your assumption is correct, the DOM isn't too heavy by itself. I'll give a try at your solution but I'm wondering how to avoid the wrapper step, since I don't want to modify the DOM structure (the uglified javascript slidesplayer.js, which I didn't make, surely depends of the DOM's structure). – Sam Dec 21 '12 at 15:28
  • 1
    Yes, I see. I've modified the code so that you're not adding any markup, and you're essentially working exclusively with the structure as it was (just scoop out each slide's contents and insert them empty). I think you'll need to make sure this AJAX callback happens before you run your slideware code if it needs to know where the slides are / modify them / etc – Barney Dec 21 '12 at 15:57
  • Great. That's just what I need. I'll cross my fingers and hope this will work with the slideware code. Thanks. – Sam Dec 21 '12 at 16:37
  • Tried it out today and the `$slides = $(slidesDOM).find('.slide');` won't work. I tried with `$slides = $(slidesDOM.responseText).find('.slide');` and it worked but unfortunately, all the GETs to the server (pictures, etc) are done just after that line. Btw, I'm doing all this inside a `$(document).ready()` callback. Am I missing something? – Sam Dec 26 '12 at 13:40
1

1) You shouldn't be streaming payloads with SocketIO. Socket is made for low-load. If you need to transmit en-masse, I'd recommend using a standard HTTP AJAX request. Then, you can use Socket.IO to control which slide you are on.

2) Try AngularJS. They've basically done all the thinking for you regarding view switching (which is essentially what you are doing). They have a great tutorial, which helps alot.

3) To simplify you Socket calls, I'd recommend using ConversationJS both client and server side.

Andrew Rhyne
  • 5,060
  • 4
  • 28
  • 41
  • 1
    1) I am not using for that. I just send messages to trigger the slide changes and sync in the clients. All the other stuff is handled by the Express server I'm running (which I forgot to mention). 2) I'll take a look at it, but I'd like to do the less changes possible to my automatically-generated HTML (that's why I though jQuery-ajax would be a good approach) 3) As I told you in #1, they are already simple. – Sam Dec 21 '12 at 15:13
  • My mistake. I made assumptions based on what you had written – Andrew Rhyne Dec 21 '12 at 15:14
  • No prob. I forgot to mention the Express server. – Sam Dec 21 '12 at 15:16
  • I would recommend looking into AngularJS and ConversationJS. They should help with your issue and Conversation should simplify your code base quite a bit – Andrew Rhyne Dec 21 '12 at 15:34
1

As I said in the question, manipulating DOM elements will cause the browser to download the resources, even if you don't insert the elements that use that resources in your DOM.

In my case, the best solution I could make was to use some sort of lazy loading at least for the img tags (but it could be easily extended for other tags, such as audio and video).

What I did was replacing replacing the src attribute with another name (xsrc in this case) and adding a custom empty src attribute to all img tags.

<img id="someImg" src="#" xsrc="foo.png"></img>

Then, with jQuery I changed the src attribute value to that of xsrc whenever I needed to dowload the image.

// When I want the image to be downloaded from the server
$('#someImg').attr( 'src' , $('#someImg').attr('xsrc') )

You can see more about the idea behind this in the questions I already mentioned (this and this).

Community
  • 1
  • 1
Sam
  • 1,222
  • 1
  • 14
  • 45