chatgpt so hot? How does the front end implement a conversation page similar to chatgpt

Profile

  • About the author: Hello everyone, I am A Niu, a high-quality creator in the full-stack field
  • Personal homepage: owner Aniu
  • Support me: Like + Favorite + Leave a message
  • Series column: front-end practical small demo
  • Motto: So far all life has been marked with failure, but it does not prevent me from moving forward!

Please add a picture description

Table of Contents

    • Personal profile
  • foreword
    • final effect
    • Page Layout
      • flex layout one
      • flex layout 2 (focus)
    • js part
    • full code
  • epilogue

Foreword

Since chatgpt came out of the circle in November last year, its popularity has remained high, and there have been many domestic versions developed with the help of interfaces, so this blog will look at how the front-end implements a chatgpt-like dialogue function from the perspective of the front-end !

Final effect

The source code is available at the end of the article!


Because this is what I wrote in a project, the color matching effect may be different if it is proposed separately, but as long as we master the functions and methods, then we can write whatever we want!

Page layout

This part is relatively simple. Those who have analyzed chatgpt’s pages will know that its page layout adopts flex layout, and flex layout is really easy to use. Then I also completed the simple version of the dialogue function based on Bootsrap + jquery + flex layout! There are two main places where flex layout is used!

flex layout 1


The avatar and text here adopt a flex layout, and the text and the top of the picture are aligned to prevent the bug that more text is still aligned with the middle of the picture.
Need to set css:

display: flex;
align-items: flex-start;

The function of align-items: flex-start; is to align the text with the top of the picture!

flex layout two (key)

The second place where flex layout is used is this search box:

Many people think that this dialog box is very simple. It is really simple to realize the input box and the button on the same line in the flex layout, but you have to take a good look at the official website of chatgpt. There are small details, and there are still many knowledge points in it.

First of all, what I want to say is that this input box uses textarea instead of input. The difference is that the input content of input cannot wrap lines, but the textarea text box can, but the problem with using textarea is that the parameter rows is set to one line. This The height of the text box will be very low, which cannot meet the requirements of the chatgpt page. If the rows are set larger or the height of the text box is higher, there will be a problem that the cursor will not be in the middle of the height of the text box when typing, and the It is on the first line. We cannot vertically center the input cursor by other means, so this does not meet the requirements of the chatgpt page, so this is indeed a worthwhile learning point! After reading the practice of the chatgpt page, I realized that the following picture shows how chatgpt works:


As shown in the picture, you only need to cancel the textarea border, and then the focus pseudo-class will also cancel the border effect, and then set a div border outside to wrap the textarea text box and button inside!

.ipt{<!-- -->
   display: flex;
   align-items: center;
   position: absolute;
   bottom: 60px;
   margin: 0 15px;
   padding-right: 15px;
   border-radius: 15px;
   width: calc(100% - 30px);
   height: 50px;
   border: 1px solid #e7eaec;
}
.ipt textarea {<!-- -->
   resize: none;
   overflow-y: auto;
   border: none;
   box-shadow: none;
}
.ipt textarea:focus{<!-- -->
   border: none !important;
   box-shadow: none !important;
}

Finally, just position the input box at the bottom of the page!

js part

First of all, in the page part, we add messages to the page, including user questions and ai’s replies. When adding messages to the page, you need to scroll up:

//Add user message to window
function addUserMessage(message) {<!-- -->
  var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + userIcon + '"><p class=\ "message-text">' + message + '</p></div>');
  chatWindow.append(messageElement);
  chatInput.val('');
  chatWindow.animate({<!-- --> scrollTop: chatWindow.prop('scrollHeight') }, 500);
}

// add reply message to window
function addBotMessage(message) {<!-- -->
  var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + botIcon + '"><p class=\ "message-text">' + message + '</p></div>');
  chatWindow.append(messageElement);
  chatInput.val('');
  chatWindow.animate({<!-- --> scrollTop: chatWindow.prop('scrollHeight') }, 500);
}

After the message is added with a page, the content of the input box is cleared, and then a keyboard event needs to be added to the input box, that is, the message can also be sent by clicking the enter key!

// Handle the Enter key press
chatInput. keypress(function(e) {<!-- -->
  if (e. which == 13) {<!-- -->
    chatBtn. click();
  }
});

Finally, there is the part of sending and getting messages:

// handle user input
chatBtn.click(function() {<!-- -->
  var message = chatInput. val();
  if (message. length == 0){<!-- -->
    common_ops.alert("Please enter content!") // Pop-up window
    return
  }
  addUserMessage(message);
  
  chatBtn.attr('disabled',true) // Make the submit button unclickable after the message is sent

  // Send information to the background
  $.ajax({<!-- -->
    url: '/chat',
    method: 'POST',
    data: {<!-- -->
      "prompt": JSON. stringify(message)
    },
    success: function(res) {<!-- -->
      res = JSON. parse(res);
      addBotMessage(res. content);
      chatBtn.attr('disabled',false) // Make the submit button clickable again after successfully accepting the message
    },
    error: function(jqXHR, textStatus, errorThrown) {<!-- -->
      addBotMessage('<span style="color:red;">' + 'An error occurred! Please try again later!' + '</span>');
      chatBtn.attr('disabled',false)
    }
  });
});

These logics are very simple, and I won’t summarize them anymore. It should be noted that I am sending a message to the background and waiting for the corresponding process to make the button unclickable. The next question and answer can only be performed after the background returns a message! But here I didn’t handle the keyboard event, which means you can click enter to continue sending messages to the background. This is also a bug, but I didn’t handle it. If you don’t need it, you can remove this keyboard event. Of course, you can also send it The time period from the message to the answer is like disabling the send button, prohibiting the enter keyboard event or unbinding the keyboard event. You can complete this by yourself. I won’t talk about it here (always leave something for you to think and understand! )

Complete code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="../../static/css/bootstrap.min.css" rel="stylesheet">
    <title>chat</title>
    <style>
        .answer{<!-- -->
          width: 100%;
          position: relative;
          height: 70vh;
        }
    
        .ipt{<!-- -->
          display: flex;
          align-items: center;
          position: absolute;
          bottom: 60px;
          margin: 0 15px;
          padding-right: 15px;
          border-radius: 15px;
          width: calc(100% - 30px);
          height: 50px;
          border: 1px solid #e7eaec;
        }
        .ipt textarea {<!-- -->
          resize: none;
          overflow-y: auto;
          border: none;
          box-shadow: none;
        }
        .ipt textarea:focus{<!-- -->
            border: none !important;
            box-shadow: none !important;
        }

        #chatWindow {<!-- -->
          max-height: calc(70vh - 120px);
          height: auto;
          overflow-y: auto;
        }
    
        .message-bubble {<!-- -->
            padding: 10px;
            margin: 5px;
            display: flex;
            align-items: flex-start;
            border-bottom: 1px dashed #e7eaec;
        }
    
    
        .message-bubble p {<!-- -->
            font-size: 18px;
            margin-left: 15px;
        }
    
        .chat-icon {<!-- -->
            width: 30px;
            height: 30px;
            border-radius: 3px;
        }
    </style>
</head>
<body>
    <div>
      <div class="row">
          <div class="col-xs-12">
            <div>
              <h1 class="text-center m-b-lg">Chat with ChatGPT</h1>
            </div>
            <div class="answer">
              <div id="chatWindow" class="mb-3"></div>
              <div class="input-group ipt">
                <div class="col-xs-12">
                  <textarea id="chatInput" class="form-control" rows="1"></textarea>
                </div>
                <button id="chatBtn" class="btn btn-primary" type="button">Go !</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
</body>
<script src="../../static/plugins/jquery-2.1.1.js"></script>
<script src="../../static/js/bootstrap.min.js"></script>
<script src="../../static/plugins/layer/layer.js"></script>
<script src="../../static/js/common.js"></script>
<script>
    $(document).ready(function() {<!-- -->
        var chatBtn = $('#chatBtn');
        var chatInput = $('#chatInput');
        var chatWindow = $('#chatWindow');
        var userIcon = '/static/images/user/{<!-- -->{ current_user.avatar }}'
        var botIcon = '/static/images/aichat/chatgpt.png';
        

        // add user message to window
        function addUserMessage(message) {<!-- -->
          var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + userIcon + '"><p class=\ "message-text">' + message + '</p></div>');
          chatWindow.append(messageElement);
          chatInput.val('');
          chatWindow.animate({<!-- --> scrollTop: chatWindow.prop('scrollHeight') }, 500);
        }

        // add reply message to window
        function addBotMessage(message) {<!-- -->
          var messageElement = $('<div class="row message-bubble"><img class="chat-icon" src="' + botIcon + '"><p class=\ "message-text">' + message + '</p></div>');
          chatWindow.append(messageElement);
          chatInput.val('');
          chatWindow.animate({<!-- --> scrollTop: chatWindow.prop('scrollHeight') }, 500);
        }

        // handle user input
        chatBtn.click(function() {<!-- -->
          var message = chatInput. val();
          if (message. length == 0){<!-- -->
            common_ops.alert("Please enter content!")
            return
          }
          addUserMessage(message);

          // messages. push({"role": "user", "content": message})
          
          chatBtn.attr('disabled',true) // Make the submit button unclickable after the message is sent

          // Send information to the background
          $.ajax({<!-- -->
            url: '/chat',
            method: 'POST',
            data: {<!-- -->
              "prompt": JSON. stringify(message)
            },
            success: function(res) {<!-- -->
              res = JSON. parse(res);
              addBotMessage(res. content);
              chatBtn.attr('disabled',false) // Make the submit button clickable again after successfully accepting the message
            },
            error: function(jqXHR, textStatus, errorThrown) {<!-- -->
              addBotMessage('<span style="color:red;">' + 'An error occurred! Please try again later!' + '</span>');
              chatBtn.attr('disabled',false)
            }
          });
        });

        // Handle the Enter key press
        chatInput. keypress(function(e) {<!-- -->
          if (e. which == 13) {<!-- -->
            chatBtn. click();
          }
        });
  });
</script>
</html>

The layer.js used here is a pop-up window component, which can be found on Baidu. common.js is my own encapsulation of the layer.js method. In fact, you can do without these two js files on this page, because the entire page only has The following code uses popup windows:

if (message. length == 0){<!-- -->
 common_ops.alert("Please enter content!")
  return
}

In fact, it is simpler, an alert will do it!

Conclusion

If you think the blogger’s writing is not bad, you can subscribe to the following flask column, which is the only paid column for bloggers. This page I made is also part of a recent project. This project is also made with flask. I will He summarizes all open source in this flask column.

[flask from entry to actual combat] column 9.9 hot subscription, has included two projects, the unique scaffolding construction of the whole site, directly copying simple and brainless operations, the project structure is similar to Django, interested can take a look!

Series column
flask framework quick start

For other columns, please go to the blogger’s homepage to view!