0

I have a PHP message system that automatically scrolls to the bottom of the div every 200ms to show new message. My issue is that if a user tries scrolling up they are forced to the bottom every 200ms. What is a method I can use to prevent this?

HTML and JavaScript:

        <div id="tab3">
          <h2>Chat Room</h>

                <div id="c-input_wrap">
                    <div id="chatlog">
                        Loading chat please wait...
                    </div>

                <div id="chatwrap">
                    <div id="chatinput">
                    <form name="chatbox" class="userchat">
                        <input class="userchat" name="message" type="text" autocomplete="off" onkeydown="if (event.keyCode == 13) document.getElementById('chatbutton').click()" autofocus/><br>
                        <input class="userchat" id="chatbutton" name="submitmsg" type="button" onclick="submitChat()" value="Send" />
                    </form>
                    </div>
                </div>
            </div>
        </div>

<script>    
    var form = document.querySelector('form[name="chatbox"]');
    form.addEventListener("submit", function (event) {
        event.preventDefault();
    });

    function submitChat() {
        if(chatbox.message.value == '') {
            alert('Error: Missing Fields.');
            return;
        }
        var message = chatbox.message.value;
        var xmlhttp = new XMLHttpRequest();

        xmlhttp.onreadystatechange = function() {
            if(xmlhttp.readyState==4&&xmlhttp.status==100) {
                document.getElementById('chatlog').innerHTML = xmlhttp.responseText;
            }
        }

        xmlhttp.open('GET','chat.php?message='+message, true);
        xmlhttp.send();
        chatbox.reset();
    }

    $(document).ready(function(e) {
        $.ajax('logs.php', {
            ifModified: true,
            complete: function(jqxhrResponse) {
                $('#chatlog').html(jqxhrResponse.responseText);
                // scroll to the bottom here
                scrollToNewMessage();
            }
        })
    });

    var allowScroll = true;
    $('#chatlog').scroll(function() {
        allowScroll = isNearBottom();
    });

    function isNearBottom() {
        var leeway = 10;
        $(window).scroll(function() {
            return $(window).scrollTop() + $(window).height() > $(document).height() - leeway;
    });
}

function scrollToNewMessage() {
  if (allowScroll) {
    $("#chatlog").animate({ scrollTop: $('#chatlog').prop("scrollHeight")}, 1000);
  }
}
</script>

chat.php:

<?php
    error_reporting(E_ALL & ~E_NOTICE);
    session_start();

    if  (isset($_SESSION['id'])) {
        $userId = $_SESSION['id'];
        $username = $_SESSION['username'];
        $userLabel = $_SESSION['nickname'];
    }

    $connect = mysqli_connect("", "", "", "");
    mysqli_select_db($connect, "root");
    $message = $_REQUEST['message'];
    $date = date('m/d/y');
    $time = date('H:i:s');

    mysqli_query($connect, 
    "INSERT INTO chat (name, message, time, date) VALUES ('$userLabel', '$message', '$time', '$date')"
    );

    $result1 = mysqli_query($connect, "SELECT * FROM chat ORDER by id");
    while ($extract = mysqli_fetch_assoc($result1)) {
        echo $extract['name'] . ": " . $extract['message'];
    }
?>

logs.php:

<?php
    error_reporting(E_ALL & ~E_NOTICE);
    session_start();

    if  (isset($_SESSION['id'])) {
        $userId = $_SESSION['id'];
        $username = $_SESSION['username'];
        $userLabel = $_SESSION['nickname'];
    }

    $connect = mysqli_connect("", "", "", "root");
    mysqli_select_db($connect, "root");
    $day = date('m/d/y');
    $result1 = mysqli_query($connect, "SELECT * 
                                       FROM (SELECT * 
                                             FROM chat 
                                             WHERE date = '$day' 
                                             ORDER BY id DESC 
                                             LIMIT 100) x                                      
                                       ORDER BY id ASC
                                       ");

    while ($extract = mysqli_fetch_assoc($result1)) {
            echo "
            <div class='usermessage'>
                <div class='mheader'>
                    <h1 style='color:".$color."'>" . $extract['time'] . "</h1>
                    <h2 style='color:".$color."'>" . $extract['date'] . "</h2>
                    <h3 style='color:".$color."'>" . $extract['name'] . "</h3>
                </div>
                <p>" . $extract['message'] . "</p>
            </div>";
        }
?>
Gavin Youker
  • 330
  • 2
  • 6
  • 21

1 Answers1

1

1. When to Scroll

You don't need to scroll every 200ms, you only need to scroll when there is a new message. Doing this with load is a bit more difficult but jQuery's ajax function can help you out:

$.ajax('logs.php', {
  ifModified: true,
  complete: function(jqxhrResponse) {
    $('#chatlog').html(jqxhrResponse.responseText);
    // scroll to the bottom here
    scrollToNewMessage();
  }
})

1 and a half: If ifModified doesn't work for you, declare a variable somewhere (in your $(document).ready should be fine).

var currentMessageText = "";

Now do your own check whether there is anything modified and, if so, update accordingly:

complete: function(jqxhrResponse) {
  if (currentMessageText != jqxhrResponse.responseText)
  {
    currentMessageText = jqxhrResponse.responseText;
    $('#chatlog').html(currentMessageText);
  }
}

2. When not to scroll

Now you need to figure out when not to scroll. If the user scrolls up, then we want not to scroll. If the user scrolls back down though, we do. We can use jQuery's scroll event to figure out when the user has scrolled and test how close to the bottom s/he has gone.

var allowScroll = true;
$('#chatlog').scroll(function() {
  allowScroll = isNearBottom();
});

/* modified from http://stackoverflow.com/a/3898152/123415 */
function isNearBottom() {
  var leeway = 10;
  $(window).scroll(function() {
    return $(window).scrollTop() + $(window).height() > $(document).height() - leeway);
  });
}

I've given the user a bit of leeway not to be at the very bottom but maybe you want to adjust this.

3. Scroll to new Message

Now we just have to scroll when appropriate:

function scrollToNewMessage() {
  if (allowScroll) {
    $("#chatlog").animate({ scrollTop: $('#chatlog').prop("scrollHeight")}, 1000);
  }
}

disclaimer: I haven't tested the above code, if there's a problem, just leave a comment and I'll correct it.

jcuenod
  • 55,835
  • 14
  • 65
  • 102
  • Okay so I'm not sure if I have correctly implemented the code or not. But I am running into issues. First of all, syntax. I believed I have fixed this but I just want to make sure.. Check your code I have modified. My bigger issue is this.. The chat.php stores the messages in the database. The logs.php simply extracts all of the messages from the database. Therefor, I don't believe the ajax solution will work, because the logs.php will never be modified. Any ideas? Thanks for your help. – Gavin Youker Dec 01 '15 at 02:58
  • It seems that you are trying to load chats with this: `$('#chatlog').load('logs.php');`. Every time a request goes to logs.php it will be executed and whatever it generates is what is in question when it comes to `ifModified` but it's possible your `logs.php` won't send the right header for `ifModified` option to work - in that case, just compare the result you receive with what you already have inside the `complete` function. – jcuenod Dec 01 '15 at 05:17
  • I am having trouble following what you are saying. I apologize. Would it help if I posted my php code? – Gavin Youker Dec 01 '15 at 06:18
  • @GavinYouker how much of your own code do you understand? – jcuenod Dec 01 '15 at 06:38
  • I am sorry for the frustration. I understand most of it. I am just new to JavaScript and AJAX. I understand most of it. What confuses me is what to change in the complete function. – Gavin Youker Dec 01 '15 at 07:02
  • I don't think you understand the issue entirely. Following your script.. when typing a message into the chat nothing new loads. When sending a new message it will not appear until refreshing the page. I have update my question. Please look at it to view the PHP files and HTML. Thank you sir. – Gavin Youker Dec 01 '15 at 07:52
  • @GavinYouker well you've taken out your timer - look at your old code and put that `.ajax` call inside the function you had for in `setInterval` (replacing `.load`). It's not helpful to change your question as you have here because it's not clear how this answer helps you anymore (for future readers). Also, you need to look up "SQL Injection"... – jcuenod Dec 01 '15 at 15:47
  • @GavinYouker are you having success now? – jcuenod Dec 02 '15 at 19:22
  • Not really. Same issue persists. This is very frustrating. Im sure this is my fault. – Gavin Youker Dec 04 '15 at 23:27
  • @GavinYouker so all I've done is copied your code above into a jsfiddle with the setinterval: http://jsfiddle.net/aoevLvzw/ it doesn't work right now because of CORS though but is that roughly what you have? – jcuenod Dec 05 '15 at 00:29
  • Yes. The chatbox wont update new messages until I refresh the page. – Gavin Youker Dec 05 '15 at 00:39
  • Okay, set a breakpoint at the start of that complete function and see whether it's ever getting there. Maybe you're sending badly formatted json. Then, if it does get there, just type out `jqxhrResponse` and `jqxhrResponse.responseText` on the console and see what comes out. – jcuenod Dec 05 '15 at 18:30
  • So i fixed the issue and the chat is now updating properly. The console and giving me an error saying the function `scrollToNewFunction` is undefined. This is true (new messages do not scroll to bottom) and it gives the this same message log hundreds of times a second. Thus, taking me back to my initial problem. – Gavin Youker Dec 06 '15 at 09:51
  • Also, it loads messages very slow at some points and very slow at somepoints and I get the console message `net::ERR_NETWORK_CHANGED` which slows my whole browser to a crawl. – Gavin Youker Dec 06 '15 at 09:53
  • when you get an error like `scrollToNewFunction is undefined` you should just use the find function to see whether you've actually defined it. I immediately notice that it's probably just a typo: `scrollToNewMessage`. The reason it's giving the error to you so often is that your setInterval is set to such a short time try making that poll every 1000ms. As for `net::ERR_NETWORK_CHANGED`, is your php server local (on your computer) or remote - accessed through the network? – jcuenod Dec 08 '15 at 05:01
  • You were correct about the `scrollToNewFunction` simple fix. How can I set it to pull 1000ms (where would be the correct spot to put `setInterval`)? even accessing the chat takes a toll on my CPU, but I'n guessing this is due to setInterval being so short. And no the php file is local, so I don't understand why I would be getting this error. – Gavin Youker Dec 08 '15 at 05:39
  • what is calling `scrollToNewFunction`? you must have it in a timer or a loop. If it's in a loop, that explains the cpu toll. If it's in a timer, it's probably something like what you originally had above `setInterval(function(){...}, some_number);` - change `some_number` – jcuenod Dec 08 '15 at 05:46