Creating a unique custom bottom position for the popover and ensuring it is fixed while allowing it to expand

Creating a customized popover similar to Bootstrap's popover, I have successfully implemented popovers in all directions except for the top direction. My challenge lies in fixing the popover at the bottom just above the button that triggers it, and ensuring that as the content inside the popover grows in height, it expands towards the top while keeping the bottom portion fixed.

let popoverBtn = document.querySelectorAll("[data-toggle='popover']");
let popover = document.querySelector(".popover-overlay");
let popoverHeading = document.querySelector(".popover-overlay-heading h4");
let popoverContent = document.querySelector(".popover-overlay-content p");
for (let i = 0; i < popoverBtn.length; i++) {
  if (popoverBtn[i]) {
    popoverBtn[i].addEventListener("click", function(e) {
      let top = e.target.offsetTop;
      let left = e.target.offsetLeft;
      let width = e.target.offsetWidth;
      let height = e.target.offsetHeight;
      let heading = e.target.getAttribute("title");
      let description = e.target.getAttribute("data-content");
      let popoverHeight = parseFloat(getComputedStyle(popover).height);
      let popoverWidth = parseFloat(getComputedStyle(popover).width);
      let dir = e.target.getAttribute("data-placement");
      popoverHeading.textContent = heading;
      popoverContent.textContent = description;
      popover.classList.add("active");
      popover.style.top = '0px';
      popover.style.left = '0px'

      switch (dir) {
        case "top":
          popover.style.top = top - popoverHeight - 20 + "px";
          popover.style.left = left + "px";
          popover.style.bottom = top;
          break;
        case "bottom":
          popover.style.top = top + height + 5 + "px";
          popover.style.left = left + "px";
          break;
        case "left":
          popover.style.top = top + "px";
          popover.style.left = left - popoverWidth - 20 + "px";
          break;
        case "right":
          popover.style.top = top + "px";
          popover.style.left = left + width + 30 + "px";
          break;
        default:
          popover.style.top = top + "px";
          popover.style.left = left + width + 30 + "px";
          break;
      }



    })
  }
}
body {
  margin: 0px;
  padding: 0px;
  font-family: sans-serif;
}

h3,
h4,
p {
  margin: 0px;
}

.container {
  width: 100%;
}

.bg-gray {
  background-color: rgba(0, 0, 0, 0.3);
}

.bg-light-gray {
  background-color: rgba(0, 0, 0, 0.1);
}

section {
  min-height: 400px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
}

section button {
  display: inline-block;
  border: none;
  background-color: #dc3545;
  color: #fff;
  padding: 0.5rem 1rem;
  font-size: 1.25rem;
  line-height: 1.5;
  border-radius: 0.3rem;
}

.popover-overlay {
  position: fixed;
  z-index: 100;
  border-radius: 5px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
  display: none;
  width: 280px;
  height: 121px;
}

.popover-overlay.active {
  display: block;
}

.popover-overlay-heading {
  background-color: #f7f7f7;
  border-bottom: 1px solid #d3d3d3;
  padding: 1rem;
}

.popover-overlay-content {
  background-color: #fff;
  padding: 1rem;
}
<div class="popover-overlay">
  <div class="popover-overlay-heading">
    <h4></h4>
  </div>
  <div class="popover-overlay-content">
    <p></p>
  </div>
</div>
<div class="container">
  <section class="bg-gray">
    <button data-placement="top" type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="Popover title" data-content="It's very engaging. Right? And here's some amazing content. It's very engaging. It's very engaging. Right? And here's some amazing content. It's very engaging.">Popover Top</button>
    <button data-placement="left" type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="Popover title" data-content="And here's some amazing content. It's very engaging. Right?">Popover Left</button>
    <button data-placement="right" type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="Popover title" data-content="And here's some amazing content. It's very engaging. Right?">Popover Right</button>
    <button data-placement="bottom" type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="Popover title" data-content="And here's some amazing content. It's very engaging. Right?">Popover Bottom</button>
  </section>
</div>

Answer №1

The height of the top popover element needs to be redefined

let popoverBtn = document.querySelectorAll("[data-toggle='popover']");
      let popover = document.querySelector(".popover-overlay");
      let popoverHeading = document.querySelector(".popover-overlay-heading h4");
      let popoverContent = document.querySelector(".popover-overlay-content p");
      for (let i = 0; i < popoverBtn.length; i++) {
        if (popoverBtn[i]) {
          popoverBtn[i].addEventListener("click", function (e) {
            let top = e.target.offsetTop;
            let left = e.target.offsetLeft;
            let width = e.target.offsetWidth;
            let height = e.target.offsetHeight;
            let heading = e.target.getAttribute("title");
            let description = e.target.getAttribute("data-content");
            let popoverHeight = parseFloat(getComputedStyle(popover).height);
            let popoverWidth = parseFloat(getComputedStyle(popover).width);
            let dir = e.target.getAttribute("data-placement");
            popoverHeading.textContent = heading;
            popoverContent.textContent = description;
            popover.classList.add("active");
            popover.style.top = "0px";
            popover.style.left = "0px";

            switch (dir) {
              case "top":
                popover.style.height = popoverHeight + "px";
                popoverHeight = parseFloat(getComputedStyle(popover).height);
                popover.style.top = top - popoverHeight - 1 + "px";
                popover.style.left = left + "px";
                popover.style.bottom = top;
                break;
              case "bottom":
                popover.style.top = top + height + 5 + "px";
                popover.style.left = left + "px";
                break;
              case "left":
                popover.style.top = top + "px";
                popover.style.left = left - popoverWidth - 20 + "px";
                break;
              case "right":
                popover.style.top = top + "px";
                popover.style.left = left + width + 30 + "px";
                break;
              default:
                popover.style.top = top + "px";
                popover.style.left = left + width + 30 + "px";
                break;
            }
          });
        }
      }
 body {
        margin: 0px;
        padding: 0px;
        font-family: sans-serif;
      }

      h3,
      h4,
      p {
        margin: 0px;
      }

      .container {
        width: 100%;
      }

      .bg-gray {
        background-color: rgba(0, 0, 0, 0.3);
      }

      .bg-light-gray {
        background-color: rgba(0, 0, 0, 0.1);
      }

      section {
        min-height: 400px;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 20px;
      }

      section button {
        display: inline-block;
        border: none;
        background-color: #dc3545;
        color: #fff;
        padding: 0.5rem 1rem;
        font-size: 1.25rem;
        line-height: 1.5;
        border-radius: 0.3rem;
      }

      .popover-overlay {
        position: fixed;
        z-index: 100;
        border-radius: 5px;
        border: 1px solid rgba(0, 0, 0, 0.1);
        box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
        display: none;
        width: 280px;
        /* height: 121px; */
        height: fit-content;
      }

      .popover-overlay.active {
        display: block;
      }

      .popover-overlay-heading {
        position: relative;
        background-color: #f7f7f7;
        border-bottom: 1px solid #d3d3d3;
        padding: 1rem;
      }

      .popover-overlay-content {
        position: relative;
        background-color: #fff;
        padding: 1rem;
      }
  <div class="popover-overlay">
      <div class="popover-overlay-heading">
        <h4></h4>
      </div>
      <div class="popover-overlay-content">
        <p></p>
      </div>
    </div>
    <div class="container">
      <section class="bg-gray">
        <button data-placement="top" type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="Popover title"
         data-content="It's very engaging. Right? And here's some amazing content. It's very engaging. It's very engaging. Right? And here's some amazing content. It's very engaging.">
          Popover Top
        </button>
        <button
          data-placement="left"
          type="button"
          class="btn btn-lg btn-danger"
          data-toggle="popover"
          title="Popover title"
          data-content="And here's some amazing content. It's very engaging. Right?"
        >
          Popover Left
        </button>
        <button
          data-placement="right"
          type="button"
          class="btn btn-lg btn-danger"
          data-toggle="popover"
          title="Popover title"
          data-content="And here's some amazing content. It's very engaging. Right?"
        >
          Popover Right
        </button>
        <button
          data-placement="bottom"
          type="button"
          class="btn btn-lg btn-danger"
          data-toggle="popover"
          title="Popover title"
          data-content="And here's some amazing content. It's very engaging. Right?"
        >
          Popover Bottom
        </button>
      </section>
    </div>

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

Tips for resolving issues related to Nodemailer configuration

I am currently working on a script that sends form data to an email address. I am using Express and Nodemailer with Node.js, but as a beginner, I am struggling to understand why the email authorization stops and prevents the letters from being sent. Here ...

What is the best way to show my button only when within a specific component?

Is there a way to display the Logout button on the same line as the title only when the user has reached the Home component? In simpler terms, I don't want the logout button to be visible all the time, especially when the user is at the login screen. ...

What is preventing the listener from activating?

I came across some HTML code that looks like this: <form id="robokassa" action="//test.robokassa.ru/Index.aspx" method="post"> <input type="text" id="OutSum" name="OutSum" value="" placeholder="Сумма пополнения"> ...

Updating the content within a div following the submission of a contact form 7

I'm struggling with my contact form. I want the content of my contact form div to change after clicking submit on the form. I've been searching for a solution, but so far, no luck. Here is what I have: Form (div1) with input fields, acceptance c ...

Implementing external JavaScript files such as Bootstrap and jQuery into a ReactJS application

Just diving into ReactJs, I've got static files like bootstrap.min.js, jquery.min.js, and more in my assets folder. Trying to incorporate them into my ReactJs App but running into issues. Added the code below to my index.html file, however it's ...

Is it possible to enclose an image with two <div> tags in a single line?

Can anyone help me with this layout issue I am facing? I have two <div>s wrapping an image in the same row, but for some reason, the right <div> keeps dropping below. I've attempted using float, display:inline-block, and tweaking the styl ...

What is the best way to translate this code into JQuery ajax?

I have some code here that is currently using Javascript AJAX. I'm interested in converting it into jQuery AJAX, can someone help me with this? var mine = new XMLHttpRequest(); mine.onreadystatechange = function() { if (mine.readyState == 4 &am ...

Impressive javascript - extract file from formData and forward it

Presented here is my API handler code. // Retrieve data. const form = formidable({ multiples: true }); form.parse(request, async (err: any, fields: any, files: any) => { if (!drupal) { return response.status(500).send('Empty ...

Delete the following td when the mouse hovers over it

How can I modify my code to remove the next td on mouseenter of a particular td? Currently, my code is removing the first occurrence of mouseenter on any td and the current mouseover td. Here is the snippet of my code. Can anyone help me identify what&apos ...

Does the input password typically submit on its own?

When attempting to create a form, I encountered an unexpected behavior. <form> <input type="text" autocomplete="username" style="display:none;"> <label for="password">password:</label><input type="password" autocomplete="c ...

Merge the movements of sliding a block along with the cursor and refreshing a sprite displayed on the block

Confronted with the challenge of combining 2 animations, one to move the block behind the cursor inside the container and the other to update the sprite on the block. Let me elaborate further on my issue. The block should only move when the cursor is insi ...

Sending Information within Controllers with AngularJS

I have a unique scenario in my application where I need to pass input from one view to another. I have set up a service as shown below: .service('greeting', function Greeting() { var greeting = this; greeting.message = 'Default&ap ...

How many addresses are stored in the JSON file?

After verifying the JSON data and trying to determine the number of addresses, I encountered the following result when accessing the information: var location = req.body; The output obtained was: { AddressValidateRequest: { '-USERID': &ap ...

Inquiries regarding jQuery's functionality and efficiency

I was wondering about the best way to improve performance? Would it be more efficient to have two images written in HTML, one visible and one hidden, and then use jQuery to toggle their display (hiding the visible one and showing the hidden one)? <img ...

JavaScript automatically arranges child elements within their parent container in a random distribution without any overlapping

I am experimenting with creating a dynamic layout of circles (divs with border-radius) within a container without any overlap. Check out my progress here - https://jsbin.com/domogivuse/2/edit?html,css,js,output var sizes = [200, 120, 500, 80, 145]; var m ...

Tips for inserting an HTML element within an exported constant

I need help formatting an email hyperlink within a big block of text. Here is the code snippet: const myEmail = '<a href="mailto:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2e4b564f435e424b6e4b564f435e424b004d41 ...

Downloading a zip file using PHP works successfully when initiated directly, but encounters errors when attempted through a web application

I have been working on a PHP script that generates a zip file and allows it to be downloaded from the browser. The download function in the code snippet below: download.php // ensure client receives download if (headers_sent()) { echo 'HTTP head ...

Issue with Vue3: The imported module is not defined

Update: I recently downgraded vue-cli to version 4.5.19 and now the code is functioning properly. It seems like there might be an issue related to vue-cli or its dependencies. I encountered a problem while working on a simple vue.js project using vue-cli. ...

Is there a way to postpone the mouseover event for vertical tabs?

While this may seem like a simple question to some, my programming skills are not quite up to par. I am seeking help from someone who can assist me. My goal is to delay the mouseover event on vertical tabs so that users can jump directly from tab 1 to ta ...

Guide on capturing JSON objects when the server and client are both running on the same system

I created an HTML page with a form that triggers JavaScript when submitted using the following event handler: onClick = operation(this.form) The JavaScript code is: function operation(x) { //url:"C:\Users\jhamb\Desktop\assignmen ...