Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Long Polling to update DOM while conserving accumulated attributes from jQuery

EDIT: Everything works; the pushes work. The only problem is that on each push the #load_info div is reset to empty. How can I preserve the original content inside the div, though the content would be the updated version of itself as the XML file is re-pushed.

I have a PHP script for long polling an XML file and encoding it as a JSON array. It is called from frontend with the JSON as the parameter.

    $filename= dirname(__FILE__)."/people.xml";

    $lastmodif = isset( $_GET['timestamp'])? $_GET['timestamp']: 0 ;
    $currentmodif=filemtime($filename);

    while ($currentmodif <= $lastmodif) {
        usleep(10000);
        clearstatcache();
        $currentmodif =filemtime($filename);
    }

    $response = array();
    $xObj = simplexml_load_file($filename);

    // Loop for #loadMe.
    foreach($xObj as $person){
        $concat_buttons .= "<button class='mybutton' value='" . (string)$person->id . " '> " . (string)$person->fullName . "</button>";
    }

    // Loop for #toadMe.
    foreach($xObj as $person){
        $concat_info .= "<div class='div div_' data-person-id='" . (string)$person->id . "' id='" . (string)$person->id . "'><h1> " . (string)$person->job . "</h1></div>";
    }


    // Output for AJAX.
    $response['msg']                = $concat_buttons;
    $response['msg2']                = $concat_info;
    $response['timestamp']      = $currentmodif;
    echo json_encode($response);

Then I have a jQuery script for instancing the JSON object (msg2) for appending each node into a div called #load_data. My question is why is the jQuery below not working? My guess is either the $(window).find isn't working in the get_person(id) function and/or my functions are out of scope with the polling. To note, the PHP and JS were 100% working before I started trying to incorporate the show_person() and get_person() functions. Working as in, when a certain button inside the #load_button div was clicked it would toggle a piece of information's view with an id that matched the button's value attribute with .show(), which was initially hidden; then if another button was clicked the old info would be hidden with .hide() and the new data would be seen. This was my round-about solution for using long-polling to update DOM elements by just loading them all up in the beginning, however if a piece of information is shown while a new poll occurs (var timestamp gets updated), then the elements inside of #load_info will temporarily get lost from the DOM, therefore resulting in an empty #load_info div until the next button is clicked. So am trying to add some additional functions to store DOM data inside the var $person so that after a poll whatever was shown prior would reappear. What can be changed or added to get this jQuery script working as intended? Thanks in advance!

    var timestamp=null;
    function waitForMsg() {
        $.ajax({
            type: "GET",
            url: "getData.php?timestamp="+timestamp,
            async: true,
            cache: false,        
            success: function(data) {
                var json=eval('('+data+ ')');
                if (json['msg'] != "") {
                    $("#load_buttons").empty();
                    $("#load_buttons").append(json['msg']);

                    // Update any person divs that were already visible.
                    $('#load_info .person').each(function() {
                        // Grabs the ID from data-person-id set earlier.
                        var id = $(this).data('person-id');
                        show_person(id);
                    });

                }
                timestamp = json["timestamp"];
                setTimeout("waitForMsg()",1000);
            },
            error: function(XMLHttpRequest,textStatus,errorThrown) {
                setTimeout("waitForMsg()",15000);
            }                   
        });
    }

    $(document).on('click', '.mybutton', function() {
        $('#load_info').empty();
        show_person(this.value);
    });

    function show_person(id) {
        $('#person-detail-' + id).remove();
        get_person(id).appendTo('#load_info');
    }

    function get_person(id) {
        var $person = $(window).find('id:contains(id)');

        var $div = $('<div>', {
            'class': 'person',
            'data-person-id': id,
            id: id
        });

        $person.find(h1).text.appendTo($div);
        return $div;  
    }
like image 262
Jim22150 Avatar asked Feb 07 '14 06:02

Jim22150


2 Answers

It looks like you have an issue generating your JSON in the PHP script:

$response['msg']  = $concat;
$response['msg2'] = $concat2;

$concat and $concat2 aren't defined anywhere, did you mean to use $concat_buttons and $concat_info instead?

$response['msg']  = $concat_buttons;
$response['msg2'] = $concat_info;

In your jQuery, you reference #load_info .person, but it doesn't look the divs that PHP builds are of class person which might be why your loop isn't finding them.

like image 151
Emily Shepherd Avatar answered Nov 03 '22 02:11

Emily Shepherd


I found your question fairly difficult to understand, but I like a challenge. As a note, I haven't tested any of this code so it would need to be debugged.

Now, as I understand it, you have a bunch of buttons with this structure:

<div id="load_buttons">
  // repeater
  <button class="mybutton" value="$person['id']">$person['fullName']</button>
  // end repeater
</div>

And a bunch of detail divs with this structure, which are initially hidden:

<div id="load_info">
  // repeater
  <div class="div div_" data-person-id="$person['id']" id="$person['id']">
    <h1>$person['job']</h1>
  </div>
  // end repeater
</div>

And these should get updated/appended on every poll.

What you would like to achieve: when a button is clicked, the associated info is shown. I would do your success function like this:

success: function(data) {
    var json=eval('('+data+ ')');
    var $buttons;
    var $infos;
    if (json['msg'] != "") {
      addButtons($(json['msg']));
      addInfos($(json['msg2']));
    }
    timestamp = json["timestamp"];
    setTimeout("waitForMsg()",1000);
}

addButtons = function($buttons){
  // loop through the button json data
  $buttons.each(function(){
    // look to see if the button already exists
    var existing = $('[value='+$(this).attr("value")+']').length;
    if(!existing){
      // if not, add the button and assign a click handler
      $(this).appendTo('#load_buttons').click(function(){
        //hide all the children of info
        $('#load_info').children().hide();
        //show the info based on the button value
        $('#'+$(this).val).show();
      });
    }
  });
}
addInfos = function($infos){
  //loop through the info json data
  $infos.each(function(){
    // check to see if an info exists
    var existing = $('#'+$(this).attr("id")).length;
    if(!existing){
      //if not, add the info to the list and set it to hidden
      $(this).appendTo('#load_info').hide();
    }
  });
}
like image 1
amay0048 Avatar answered Nov 03 '22 02:11

amay0048