Tips for positioning a chat box at the bottom of a v-card's visible area

My goal is to create a chat app using Vuejs 3 and Vuetify 3, but I'm encountering an issue aligning the chatbox with the v-card component. Instead of being positioned at the bottom of the v-card, the chatbox (green) appears at the bottom of the page. You can see a visualization of this problem in the following screenshot: https://i.stack.imgur.com/ZAlXE.png

Please refer to the GitHub link for reproduction instead: https://github.com/samzhangjy/observerx-web

I am looking for a solution to have the chatbox aligned inside the v-card and pinned at the bottom even when scrolling, essentially keeping it within the v-card. Additionally, I would like to know if there is a built-in way to make the v-toolbar (the orange header) sticky at the top.

Thank you so much in advance!


If you prefer not to use CodeSandbox, here is the code related to this:

  • MainContainer.vue:
<script lang="ts" setup>
import { useTheme } from "vuetify";

const theme = useTheme();
const changeTheme = () =>
  (theme.global.name.value = theme.global.current.value.dark
    ? "light"
    : "dark");
</script>

<template>
  <v-container class="h-screen py-md-12 py-sm-5">
    <v-row justify="space-around" class="h-100">
      <v-card max-width="1000" height="100%" width="100%" class="overflow-auto">
        <v-toolbar color="tertiary-container" class="position-sticky">
          <template v-slot:prepend>
            <v-btn icon="$menu" color="on-tertiary-container"></v-btn>
          </template>

          <v-toolbar-title class="text-h6"> ObserverX </v-toolbar-title>

          <template v-slot:append>
            <v-btn
              icon="mdi-theme-light-dark"
              color="on-tertiary-container"
              @click="changeTheme"
            ></v-btn>
          </template>
        </v-toolbar>

        <v-card-text class="overflow-auto">
          <slot />
        </v-card-text>
      </v-card>
    </v-row>
  </v-container>
</template>

<style scoped></style>
  • ChatPage.vue:
<template>
  <v-container class="h-100">
    <v-responsive class="align-center text-center h-100">
      <MainContainer class="container h-100">
        <div class="messages">
          <p class="text-body-1" v-for="i in 100">Line #{{ i }}</p>
        </div>
        <div class="message-input-container">
          <v-textarea
            variant="solo"
            base-color="surface"
            bg-color="surface-variant"
            color="on-surface-variant"
            class="rounded-t-0 message-input"
            rows="3"
            no-resize
            rounded="0"
            v-model="currentMessage"
          >
            <template v-slot:append-inner>
              <v-btn
                icon="mdi-send"
                variant="tonal"
                size="small"
                color="primary"
                @click="sendMessage"
              ></v-btn>
            </template>
          </v-textarea>
        </div>
      </MainContainer>
    </v-responsive>
  </v-container>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import MainContainer from "./MainContainer.vue";

const currentMessage = ref("");

const sendMessage = () => {
  console.log(currentMessage.value);
};
</script>

<style scoped>
.container {
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  padding-bottom: 100px;
  max-height: 1000px;
  height: 100%;
}

.messages {
  width: 100%;
  height: 100%;
  overflow: auto;
}

.message-input {
  max-width: 1000px;
  width: 100%;
  height: 100px;
  position: fixed;
  bottom: 0;
  left: 0;
  resize: none;
}

.message-input-container {
  position: absolute;
  max-width: 1000px;
  width: 100%;
}
</style>

Answer №1

Utilizing the position: fixed; property will ensure that the element remains fixed on the screen; you may want to experiment with using position: absolute; instead.

To revise your code, consider the following adjustments:

.message-input {
  max-width: 1000px;
  width: 100%;
  height: 100px;
  position: absolute;
  bottom: 0;
  left: 0;
  resize: none;
}

Additionally, there seems to be an issue with the CodeBox link provided, as I have encountered difficulty accessing your project.

Answer №2

If you want to create a layout where the messages list dynamically takes up the space between the toolbar and message input, you can achieve this by using flexbox and setting a fixed height for the container. This way, the messages will be displayed in an overflow-auto container within the card.

<v-card height="..." class="d-flex flex-column">
  <v-toolbar></v-toolbar>

  <div class="overflow-auto">
    <v-list :items="messages" dense></v-list>
  </div>
        
  <div class="message-input-container">...</div>
</v-card>

Check out the example code snippet:

const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = {
  setup(){
    return {
      messages: ref(Array(50).fill(null).map((_,i) => `Message ${i}`))
    }
  }

}
createApp(app).use(vuetify).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="62040d0c1622574c1a">[email protected]</a>/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app">
  <v-app>
    <v-main>
      <v-card height="200px" class="ma-1 d-flex flex-column">
        <v-toolbar color="amber">
          <v-toolbar-title class="text-h6"> ObserverX </v-toolbar-title>
        </v-toolbar>

        <div class="overflow-auto">
          <v-list :items="messages" dense></v-list>
        </div>
        
        <div class="bg-green pa-3">Placeholder for Message Input</div>
      </v-card>
    </v-main>
  </v-app>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Text fields do not show a cursor icon

On the website http://www.legrandclub.net, there are two text fields. Everything works fine in all web browsers except for Internet Explorer. When I click on one of the text fields, the cursor is not displayed, although it is possible to write text. What c ...

Material-inspired Design Device Compatible DIV slide with JS, JQuery, and CSS

My goal is to achieve something similar to this: Desired Live Website I am looking for a feature where clicking on the div will slide in content from the right, load an external page inside it, and close when prompted. The slider div should be device c ...

How to create a bottom border in several TD elements using a combination of CSS and jQuery

Check out my Website: Search Page I have written some CSS that functions well when searching by Name, but not by Specialty: .displayresult { display: block; } #fname, #lname, #splabel, #addlabel, #pnlabel { border-width: 4px; border-bottom-st ...

Is there a way to personalize the appearance of Static File in nextjs?

Is there a way to customize the display of static files, particularly images? For instance, when accessing a static file on a website like this: Currently, it just shows a basic img element. Is it possible to dynamically add additional elements to that d ...

Count Scroller - Finding a Solution for _.debounce

Issue with Counter I have a counter that should increase or decrease by one each time the user scrolls up or down. The problem I'm facing is that my counter $('html').on('mousewheel', function (e) { var delta = e.originalEve ...

Issue in Angular: Attempting to access properties of undefined (specifically 'CustomHeaderComponent')

I have encountered a persistent error message while working on a new component for my project. Despite double-checking the injection code and ensuring that the module and component export logic are correct, I am unable to pinpoint the issue. custom-header ...

Guide on serializing an object containing a file attribute

I'm currently working on creating a small online catalog that showcases various housing projects and allows users to download related documents. The data structure I am using is fairly straightforward: each project has its own set of properties and a ...

Create a VueJs component and globally register it with a unique custom style

I am looking to integrate Vue-Select into my VueJs project by creating a WebPack configuration that exports separate files for vendors and pages. Vue-Select offers a lot of customization options, and I want to centralize these settings in a single file for ...

Having trouble with NVM not working correctly in Ubuntu 21.04 Terminal?

Lately, I've been facing challenges with updating my Node.js version, and one method I tried was using node version manager. After downloading the install_nvm.sh file with the command curl -sL https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/insta ...

Node.js command-line interface for chat application

Can someone help me figure out the best method for creating a command line interface chat app using nodejs? I'm considering using http and possibly phantomjs to display it in the terminal, but I have a feeling there's a more efficient approach. A ...

Creating a read-only DIV using Angular - a step-by-step guide

Is there a simple way to make all clickable elements inside a div read only? For example, in the provided HTML code, these divs act like buttons and I want to disable them from being clicked. Any tips or shortcuts to achieve this? Thank you. #html < ...

Guide to crafting a javascript variable from MySQL through the power of PHP and AJAX

I am not very experienced in working with AJAX and javascript. I am currently trying to pass longitude and latitude values from a MySQL database to javascript, but it doesn't seem to be working as expected. Can anyone help me figure out what I might b ...

When an SVG image is embedded, its color may not change even after being converted to an inline SVG

I've inserted an SVG using an img tag. When hovering over it, I want the fill color of the SVG to change. I attempted to convert the SVG to inline SVG following this method, but it doesn't seem to be working as expected. No console errors are b ...

Nested CSS selector within a parent element

How can I change the color of both elements inside a custom button when the button is active? The first color is defined by the class of the button, and the second color is defined by the class of the span inside that button. CSS: .buttonClass{ color:b ...

Is there a comparable Javascript alternative to the Ruby gem "site_prism" for Page Objects design?

Is there a JavaScript framework for Protractor in NodeJS that provides a clean way to define Page Object Elements similar to site_prism? I've explored astrolabe but it doesn't quite meet the requirements. This is an example of how Page Objects a ...

JavaScript Countdown Timer Programming

I've scoured countless resources, but most of them only provide code for counting down to a specific day. I'm reaching out in the hopes that someone can assist me by crafting some JavaScript for the following HTML structure. This section of my w ...

Steering clear of Vuex store, here's how to effectively utilize a global server side variable in Nuxtjs v2

Is there a way to create a global server-side variable without using Vuex store in order to avoid polluting the window._NUXT_ object during hydration? The data I need is around 300kb and will only be used for rendering components, so it becomes obsolete af ...

Tips for correctly deleting a duplicate ref Object ID following the removal of a Document

Coming from a background in relational databases, I'm encountering a challenge with a pattern in Mongoose. Let's say we have SchemaA and SchemaB (for example, pets and people): const Person = new Schema({ name: String, pets: [{ ref: ...

Explore additional sub-categories by clicking on them with the help of jQuery or alternate techniques

I am currently working on a Magento store that has Categories with an overwhelming amount of sub-categories, taking up almost half of the page. I want to enhance user experience by implementing a feature that displays a "Load more" button when there are mo ...

When clicked, the onClick feature will reduce the number each time instead of initiating the timer

Currently, I am working on a meditation application using React. As a starting point, I implemented a 25-minute countdown feature. The challenge I am facing is that the timer starts counting down each time the button is clicked, rather than triggering it ...