Positioning <tr> elements with absolute precision within a intricate layout

I am faced with the challenge of developing a complex UI control. The structure consists of a left-side TreeTable component, implemented using a <table> element, and a right-side SVG component that displays data corresponding to each row of the left TreeTable. While both sides need to function independently as per requirements, they also need to synchronize scrolling behaviors.

The overall layout is structured as follows:

<div>
   <div id="leftComponent">
      <table>...</table
   </div>
   <div id="rightComponent">
      <svg>...</svg>
   </div>
</div>

To optimize performance for large datasets with over 1 million rows, I have implemented a feature where only the rows within the viewport on either side are rendered in the DOM.

However, this approach presents the following challenges:

  1. When scrolling on the right side, due to absolute positioning of elements, achieving synchronization between the two components poses difficulties. For example, part of a row may be visible at the top of the viewport, then all other rows, followed by another partial row at the bottom. How can I offset the content of the leftComponent table to match the rows displayed on the right side? Absolute positioning on the <tr> elements is ineffective. Although rasterized scrolling was considered, it was not deemed viable. Attempting to offset the entire <table> element using something like
    <table style="margin-top:1337px">
    affects the sticky header functionality. Are there alternative solutions?
  2. As both components operate relatively independently, having separate vertical scrollbars for each side disrupts the UI experience. Hiding the y-axis scrollbar by setting overflow-y: hidden disables scrolling on the left side. One potential solution could involve intercepting WheelEvents to simulate ScrollEvents for the rightComponent. Previous attempts to hide the y-axis scrollbar using CSS properties like scrollbar-width: none; affected both scrollbars. Keeping the x-axis scrollbar intact visually and functionally while hiding the y-axis scrollbar remains a challenge. Experimenting with techniques such as margin-right: -20px successfully hides the y-axis scrollbar but seems cumbersome.

In summary:

  1. Both components must maintain relative independence while exhibiting synchronized behavior.
  2. Visual removal of the y-axis scrollbar between components without compromising scrolling ability on the left side is necessary.
  3. Ability to offset the left <table> section based on arbitrary pixel values to align with the freely scrollable right side is essential.
  4. Avoidance of a complete overhaul of the left-side structure by converting the <table> into a <div> structure is preferred due to complexity.
  5. Preservation of performance enhancements for rendering only visible rows while ensuring correct alignment with the right side is crucial.

Seeking innovative solutions for these issues. Your insights and suggestions are appreciated.

EDIT: Added screenshots for clarity.

Description: The first image illustrates perfect alignment when scrolled to the top. Both sets of rows are perfectly matched. In contrast, the second image showcases partially scrolled rows, where the topmost row is barely visible. Aiming to achieve consistent alignment similar to the first image proves challenging due to restrictions of the <table> structure on the leftComponent. Additionally, eliminating the middle scrollbar, as specified earlier, while retaining horizontal scrolling functionality on both components remains a priority.

For further details, feel free to inquire.

Answer №1

This JS implementation features a scrollTop technique in combination with relative positioning on the rows, as suggested earlier.

The logic should be fairly self-explanatory. Feel free to ask any questions if needed.

const leftComponent = document.getElementById("leftComponent");
const rightComponent = document.getElementById("rightComponent");
const svg = rightComponent.querySelector("svg");

const theadHeight = leftComponent.querySelector("thead").offsetHeight;
const tbodyRowHeight = leftComponent.querySelector("tbody tr").offsetHeight;

const numRows = 100;    // Number of rows in th table
document.documentElement.style.setProperty('--table-height', (theadHeight + numRows * tbodyRowHeight) + 'px');


// Capture rightComponent scroll events
rightComponent.addEventListener('scroll', evt => {
  // Calculate top row index number based on scroll offset and row height
  const topRowIndex = Math.floor(rightComponent.scrollTop / tbodyRowHeight);
  // Work out how many pixels into the top row that the scroll position is
  const scrollFractionalAdjustment = - (rightComponent.scrollTop % tbodyRowHeight);
  // Set the position relative top value for the table rows.
  // This will adjust their vertical position on the page based on scroll fractional offset we just calculated.
  document.documentElement.style.setProperty('--scroll-fractional-adjustment', scrollFractionalAdjustment + 'px');
  // Update the table and graph based on out top row index
  populateTableRows(topRowIndex);
  populateGraph(topRowIndex);
});



function populateTableRows(topRowIndex)
{
  let n = topRowIndex;
  const rows = leftComponent.querySelectorAll("tbody tr");
  rows.forEach((row, i) => {
    const cols = row.querySelectorAll("td");
    cols[0].textContent = "row" + n + " with very long text";
    cols[1].textContent = "row" + n;
    n++;
  });
}


function populateGraph(topRowIndex)
{
}


// Initial setup
populateTableRows(0);
populateGraph(0);
:root {
  --scroll-fractional-adjustment: 0px;
  --table-height: 10000px;
}

html, body {
  margin: 0;
}

body {
  font-family: sans-serif;
  font-size: 20px;
}
th, td {
  height: 36px;
  line-height: 36px;
  padding-top: 0;
  padding-bottom: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
th {
  background-color: #aaa;
  color: white;
}

.system {
  display: flex;
  width: 100%;
  height: 100vh;
  overflow: hidden;
}

#leftComponent.
#rightComponent {
  height: var(--table-height);
}

#leftComponent {
  width: 30%;
  height: 100%;
  overflow: scroll hidden;
}
#leftComponent tbody {
  display: block;
  overflow: hidden;
}
#leftComponent tbody td {
  position: relative;
  top: var(--scroll-fractional-adjustment);
}
#leftComponent table th,
#leftComponent table td
{
  max-width: 150px;
}

#rightComponent {
  width: 70%;
  overflow: scroll;
}
#rightComponent .rightContainer {
  width: 100%;
  height: var(--table-height);
}
#rightComponent svg {
  position: fixed;
}
<div class="system">
   <div id="leftComponent">
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>ID Blub</th>
            <th>Foo</th>
            <th>Sort string</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>row0 with very long text</td>
            <td>row0</td>
            <td>Foo something</td>
            <td>r2u58</td>
          <tr>
            <td>row0 with very long text</td>
            <td>row0</td>
            <td>Foo something</td>
            <td>r2u58</td>
          </tr>
          <tr>
            <td>row0 with very long text</td>
            <td>row0</td>
            <td>Foo something</td>
            <td>r2u58</td>
          </tr>
          <tr>
            <td>row0 with very long text</td>
            <td>row0</td>
            <td>Foo something</td>
            <td>r2u58</td>
          </tr>
          <tr>
            <td>row0 with very long text</td>
            <td>row0</td>
            <td>Foo something</td>
            <td>r2u58</td>
          </tr>
        </tbody>
      </table>
   </div>
   <div id="rightComponent">
      <div class="rightContainer">
        <svg>
          <circle cx="50" cy="50" r="50"/>
        </svg>
      </div>
   </div>
</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

Fraudulent emails targeting my posted ads - beware!

Operating a classifieds website where anyone can view and contact posters without needing to be a member poses certain challenges. One of the main issues I face is the abundance of scam emails being sent to users, attempting to deceive them and steal their ...

Placing a small image on top of multiple images using CSS

I am facing a CSS issue and I need help with positioning a small image (using position absolute) like a warranty badge on top of larger images. The challenge is to ensure that the badge is fixed at the bottom left corner of each image, despite variations ...

The iPhone header position switches when an HTML5 input type number is selected

I am currently working on a project that involves jQuery Mobile and PhoneGap. One issue I am encountering is when using the iPhone version, the header position changes when I click on an input type number. NOTE: When I focus on the input type number in my ...

MTG Life counter. Display fluctuations in count

I am currently working on a fun project creating an MTG (Magic The Gathering) life tracker, even though the code is quite messy. Despite its flaws, it still gets the job done. Click here to view the MTG life tracker https://i.stack.imgur.com/Su17J.png ...

Tips for customizing the appearance of jscrollpane in EasyUI

I need help customizing the jscrollpane style in EasyUI Frozen Columns for DataGrid. I've tried changing it using the following CSS: ::-webkit-scrollbar { width: 12px; } However, it doesn't seem to have any effect. ...

What are the specifications for the opacity, width, and height of the background image in Bootstrap's

My current project has specific requirements that I need assistance with: I need the background image of the jumbotron to fit the width of the page and have its height scaled proportionally. The opacity of the jumbotron's background image should be ...

Modifying the color of a div based on a specified value using JavaScript

<div id="navigation"> Add your text here </div> <input type="text" id="newColor"><button onclick="modifyColor()">Update</button> <script> function modifyColor(){ var chosenColor = document.getElementB ...

Having trouble deciphering mathematical formulas while editing content on ckeditor

While using math formulas in CKEditor, I noticed that when I insert new content via textarea, the formulas are displayed correctly. However, when I go back to edit the content, the text formulas do not display as before. This is the source code I am using ...

Tips for creating a clickable textbox

Does anyone know how to make a textbox clickable even when it is set as readonly. I'm looking for a way to make my textboxes clickable like buttons because I have some future plans for them. <input type="text" readonly value="Click me" id="clickm ...

Adding multiple images to email HTML with Python made easy

Looking for a solution to embed 15 .jpg images from a single folder into the body of an email without them shrinking when stacked vertically. My initial approach was to create one long image with all photos but it reduced in size as more pictures were adde ...

The flask application encounters a 404 error when trying to access the favicon.ico file

Upon reviewing my logfile, I noticed numerous entries indicating attempts to load files like /favicon.ico GET - /favicon.ico GET - /apple-touch-icon.png GET - /apple-touch-icon-precomposed.png I have researched this issue extensively online, but have bee ...

Delayed response of text effects in JQuery on page load

Within my rails app, I have the following code snippet: window.onload = -> $("#mycontainer").typewriter() $("#div1").fadeIn("slow") This code snippet interacts with the following block of content: <blockquote class="pull-left"> < ...

Issue: Trouble with Rotating Tooltips in Javascript

I am facing a challenge with the tooltips on my website. I want to ensure that all tooltips have a consistent look and transition effects, but I am struggling to achieve this. The rotation and other effects applied using javascript are not functioning prop ...

What is the method for embedding the creation date of a page within a paragraph on Wagtail?

Whenever a page is created in the admin panel of Wagtail, it automatically displays how much time has elapsed since its creation. https://i.stack.imgur.com/6InSV.png I am looking to include the timestamp of the page's creation within a paragraph in ...

What steps can I take to ensure that the content remains intact even after the page is

Hey there, I hope you're having a great start to the New Year! Recently, I've been working on creating a calculator using HTML, CSS, and JavaScript. One thing that's been puzzling me is how to make sure that the content in the input field do ...

Issue with grid element not properly scaling using the transform:scale property

I have encountered an issue where a simple grid, with the gaps set to 0, displays a small line when I apply the transform:scale(1.5) css property. I am unsure if this is a bug or if I am doing something incorrectly. Interestingly, the result in Firefox dif ...

Phase 2 "Loading" visual backdrop

I'm attempting to include a loading animation GIF in my Cycle 2 plugin. Is there a way to ensure that the GIF loads before the images? Right now, I have set my loading.gif as a background image. The issue is that the loading.gif isn't displaying ...

Tips for maintaining consistent styles in CSS for multiple websites

I am currently working on developing a customizable chatbot widget using react. The goal is to create a chatbot widget that can be easily integrated into any website, similar to the functionality of rasa-webchat. During testing on some websites, I encount ...

Merge a dropdown menu with an alphabetically arranged list that is interactive with clickable options

I am still learning HTML and Javascript but I'm doing my best. Currently, I am facing a challenge where I need to create a button that, when clicked, opens a dropdown menu containing a table of data. The user should then be able to select a number fr ...

Increase transparency of scrollbar background

Is it possible to adjust the opacity of a scrollbar track? I attempted to use opacity:0.5, but it didn't have any effect. I am using material UI styled, although I don't believe that is causing the issue. const Root = styled('div')(({ ...