1

First, I want to say if anyone can think of a better question title for this, let me know and I will edit.

What I am trying to do

I have text I am storing in a <div>{read_from_javascript_file}</div></div>. I want to be able to capture what the height of this div will be, and based upon a height size, a single button(SHOW MORE or COLLAPSE) will be displayed or hidden.

Also, the parent div <div><div>{read_from_javascript_file}</div></div> will expand/collapse depending on the height of the child div and the state of the button. For example...

  1. If the height of the child div is greater than 92px, the button will show, and the parent div will be set to max-height: 69px by default.
  2. If the height of the child div is less than 92px, the button will not show, and parent div will be set to max-height: none by default.
  3. If the state of the button is true(COLLAPSE is showing), then the parent div will be set to max-height: none.

If you want to see the full code sandbox, it can be reviewed here: https://codesandbox.io/s/accordian-with-draftjs-to-html-piy6z

Note: I've made it so clicking on the COLLAPSE/COLLAPSE button will console.log() the height of the current child div.


Below in the code on line 97, I need to replace true with something like (childDiv < 92)

Once that is done, I can use the same (childDiv < 92) condition to set the parent div to show as max-height: none and hide the button.

The code...

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";

// Globals
import {
  defaultSelectedPaginationTab,
  getAvailablePaginatedTabs
} from "../Globals/Index.js";

const PaginationArticles = (props) => {
  // Variables
  const { articles } = props;
  const numOfTotalArticles =
    articles.articlesData.length * articles.articlesPerPage;
  const numOfTotalTabs = Math.floor(
    numOfTotalArticles / articles.articlesPerPage
  );
  const lastPage = numOfTotalTabs;
  // const articleDescLinesHeight = 92; // px
  const [selectedPaginationTab, setSelectedPaginationTab] = useState(
    defaultSelectedPaginationTab
  );
  const [availablePaginatedTabs, setAvailablePaginatedTabs] = useState(
    getAvailablePaginatedTabs(numOfTotalTabs, selectedPaginationTab)
  );
  const [isArticleMoreShown, setIsArticleMoreShown] = useState(
    Array(numOfTotalArticles).fill(false)
  );
  const [
    resetIsArticleMoreShownButton,
    setResetIsArticleMoreShownButton
  ] = useState(false);
  // const [arrArticlesDescHeights] = useState(Array.from(Array(articles.articlesData[selectedPaginationTab-1].length).fill(0)));
  // console.log(arrArticlesDescHeights);

  // Functions
  const changePaginationTab = (ev, num) => {
    ev.preventDefault();

    if (selectedPaginationTab !== num) {
      setSelectedPaginationTab(num);
      setAvailablePaginatedTabs(getAvailablePaginatedTabs(numOfTotalTabs, num));
      setIsArticleMoreShown(Array(numOfTotalArticles).fill(false));
    }
  };
  const changeArticleMoreShownButton = (
    divId,
    index,
    isArticleMoreShownStatus
  ) => {
    isArticleMoreShown[index] = isArticleMoreShownStatus;
    setResetIsArticleMoreShownButton(true);
    // console.log('divId', divId);
    console.log(document.getElementById(divId).clientHeight);
  };

  // Switch "READ MORE"/"COLLAPSE" buttons
  useEffect(() => {
    if (resetIsArticleMoreShownButton) {
      setResetIsArticleMoreShownButton(false);
    }
  }, [resetIsArticleMoreShownButton]);

  // Render
  return (
    <div>
      {articles.articlesData[selectedPaginationTab - 1].map(
        (article, index, articlesOnPage) => (
          <div
            key={index}
            className="div-pagination-article"
            style={
              index === articlesOnPage.length - 1
                ? {}
                : { borderBottom: "1px solid #cfcfd0" }
            }
          >
            {article.img && <img src={article.img} alt="Announcement" />}
            <div
              className="div-pagination-article-content"
              style={article.img ? { paddingTop: "20px" } : {}}
            >
              <h2>{article.title}</h2>
              <h4>{article.date}</h4>
              <div
                className={
                  isArticleMoreShown[article.orderNum - 1]
                    ? "div-pagination-article-desc-window show"
                    : "div-pagination-article-desc-window hide"
                }
              >
                <div
                  id={`naa-article-description-${index}`}
                  dangerouslySetInnerHTML={{ __html: article.description }}
                />
              </div>
              {true && isArticleMoreShown[article.orderNum - 1] ? (
                <button
                  onClick={() =>
                    changeArticleMoreShownButton(
                      `naa-article-description-${index}`,
                      article.orderNum - 1,
                      !isArticleMoreShown[article.orderNum - 1]
                    )
                  }
                >
                  COLLAPSE&nbsp;&nbsp;∧
                </button>
              ) : (
                <button
                  onClick={() =>
                    changeArticleMoreShownButton(
                      `naa-article-description-${index}`,
                      article.orderNum - 1,
                      !isArticleMoreShown[article.orderNum - 1]
                    )
                  }
                >
                  READ&nbsp;MORE&nbsp;&nbsp;∨
                </button>
              )}
              {article.url &&
                (article.isInternalSite ? (
                  <Link to={article.url}>LINK&nbsp;HERE</Link>
                ) : (
                  <a
                    href={article.url}
                    target="_blank"
                    rel="noopener noreferrer"
                  >
                    LINK&nbsp;HERE
                  </a>
                ))}
            </div>
          </div>
        )
      )}
      <div className="div-pagination-tabs-container">
        <div className="div-pagination-tabs">
          {selectedPaginationTab === 1 ? (
            <button className="btn-pagination-disabled-arrow-tab">
              &#171;
            </button>
          ) : (
            <button
              className="btn-pagination-enabled-arrow-tab"
              onClick={(ev) => changePaginationTab(ev, 1)}
            >
              &#171;
            </button>
          )}
          {selectedPaginationTab === 1 ? (
            <button className="btn-pagination-disabled-arrow-tab">&#60;</button>
          ) : (
            <button
              className="btn-pagination-enabled-arrow-tab"
              onClick={(ev) =>
                changePaginationTab(ev, selectedPaginationTab - 1)
              }
            >
              &#60;
            </button>
          )}
          {availablePaginatedTabs.map((num) =>
            selectedPaginationTab === num ? (
              <button className="btn-pagination-selected-tab" key={num}>
                {num}
              </button>
            ) : (
              <button
                className="btn-pagination-unselected-tab"
                key={num}
                onClick={(ev) => changePaginationTab(ev, num)}
              >
                {num}
              </button>
            )
          )}
          {selectedPaginationTab === lastPage ? (
            <button className="btn-pagination-disabled-arrow-tab">&#62;</button>
          ) : (
            <button
              className="btn-pagination-enabled-arrow-tab"
              onClick={(ev) =>
                changePaginationTab(ev, selectedPaginationTab + 1)
              }
            >
              &#62;
            </button>
          )}
          {selectedPaginationTab === lastPage ? (
            <button className="btn-pagination-disabled-arrow-tab">
              &#187;
            </button>
          ) : (
            <button
              className="btn-pagination-enabled-arrow-tab"
              onClick={(ev) => changePaginationTab(ev, lastPage)}
            >
              &#187;
            </button>
          )}
        </div>
      </div>
    </div>
  );
};

export default PaginationArticles;
Fiddle Freak
  • 1,923
  • 5
  • 43
  • 83

2 Answers2

1

You actually want to know if the container - the parent div - is overflown by the child div which contains the text... if it is - show button and max-height the parent div, if it is not - don't show button and don't max-height

In order to do that , check micnic's answer here that nicely explain how to do that.

EDIT

Make sure you check if parent divs are overflown AFTER the render stage, means in the componentDidMount. Till then show no buttons and max-height all parent divs. In your case you should use useEffect hook, because your component is function and not class.

Now two things I wanna add here:

  1. Think about a shorter and smoother way to organize this code, especially regarding this show or hide buttons state handling, because this is waayyy to complicated, and you won't be able to remember it in the future.

  2. Keep those elements' classnames and Ids short, for your convenience.

Good luck!

Eldshe
  • 723
  • 6
  • 19
  • So I replaced `true` with `isOverflown(document.getElementById(`naa-article-description-${index}`))`, and I get the following error: ``. (btw, ty for the suggestion on code cleanup, I'm new with wysiwig, so I had to do what I could to get it to work coming in from a json file(later http request). – Fiddle Freak Dec 10 '20 at 22:13
  • You passed the HTML element wrong. Maybe forgot curly braces around the Id referrer? – Eldshe Dec 10 '20 at 22:24
  • if I replace ``isOverflown(document.getElementById(`naa-article-description-${index}`))`` with `console.log(\`naa-article-description-${index}\`)`, the console log is showing `naa-article-description-0`, `naa-article-description-1`, and `naa-article-description-2` (which is correct). The markup didn't handle the `` `` `` from the first comment well, so I'm trying to use the escape this time... markdown fixed – Fiddle Freak Dec 10 '20 at 22:40
  • Honestly my guess is it's because the jsx of react is probably trying to read that `id` before it completes rendering the element to the DOM. Which would mean it isn't really a usable solution. – Fiddle Freak Dec 10 '20 at 22:48
  • 1
    So eventually I think you should start the page (render) without buttons and with parent divs max-heighted, and in componentDidMount you check overflowness and take actions. Because before text is rendered to the child div - you won't have any idea about the div's height – Eldshe Dec 10 '20 at 23:02
  • componentDidMount is for state components and since I'm using hooks my guess is I have to use `useEffect()` in some way that will not only check to see once the component has completed rendering, but add the button(or not) with an onClick and with styling. My guess is I can probably hold a boolean variable to do this somehow, but my experience with hooks is low and This will probably turn into a weekend of trial and error for me. – Fiddle Freak Dec 10 '20 at 23:22
  • 1
    Well, this is your time to learn. Meanwhile, the equivalent to componentDidMount in functional React component is 'useEffect(() => { //do code });' – Eldshe Dec 11 '20 at 05:44
  • I gave your answer and your comments +1's, but below is where I think users will want to see the answer marked since it matches the solution code. Thanks for at least pointing me in the right direction. (now to clean the code... grumble... mumble... mumble...) – Fiddle Freak Dec 12 '20 at 04:57
  • nice dude great – Eldshe Dec 12 '20 at 19:59
1

Below is the ENTIRE solution code...


add 3 lines at the top inside the function PaginationArticles = (props) => {..}...

const articleDescLinesHiddenHeight = 92; // px
const [arrArticlesDescHeights] = useState(Array.from(Array(articles.articlesData[selectedPaginationTab-1].length).fill(0)));
const [isBtnNeededChecked, setIsBtnNeededChecked] = useState(false);

inside the function changePaginationTab = (ev, num) => {...} and inside the if statement, add the following line at the bottom...

setIsBtnNeededChecked(false);

add the following useLayoutEffect()

Note: you will also need to import useLayoutEffect

useLayoutEffect(() => {
  if (!isBtnNeededChecked) {
    for (let i = 0; i < arrArticlesDescHeights.length; i++) {
      arrArticlesDescHeights[i] = document.getElementById(`naa-article-description-${i}`).clientHeight;
    }
    setIsBtnNeededChecked(true);
  }
}, [isBtnNeededChecked, arrArticlesDescHeights]);

replace...

<div
  className={
    isArticleMoreShown[article.orderNum - 1]
      ? "div-pagination-article-desc-window show"
      : "div-pagination-article-desc-window hide"
  }
>
  <div
    id={`naa-article-description-${index}`}
    dangerouslySetInnerHTML={{ __html: article.description }}
  />
</div>

with

{(arrArticlesDescHeights[index] > articleDescLinesHiddenHeight) ? 
  <div className={isArticleMoreShown[article.orderNum - 1] ? "div-pagination-article-desc-window show" : "div-pagination-article-desc-window hide"}>
    <div id={`naa-article-description-${index}`} dangerouslySetInnerHTML={{ __html: article.description }}/>
  </div> : 
  <div className="div-pagination-article-desc-window show">
    <div id={`naa-article-description-${index}`} dangerouslySetInnerHTML={{ __html: article.description }}/>
  </div>
}

replace...

{true && isArticleMoreShown[article.orderNum - 1] ? (
  <button
    onClick={() =>
      changeArticleMoreShownButton(
        `naa-article-description-${index}`,
        article.orderNum - 1,
        !isArticleMoreShown[article.orderNum - 1]
      )
    }
  >
    COLLAPSE&nbsp;&nbsp;∧
  </button>
) : (
  <button
    onClick={() =>
      changeArticleMoreShownButton(
        `naa-article-description-${index}`,
        article.orderNum - 1,
        !isArticleMoreShown[article.orderNum - 1]
      )
    }
  >
    READ&nbsp;MORE&nbsp;&nbsp;∨
  </button>
)}

with...

{(arrArticlesDescHeights[index] > articleDescLinesHiddenHeight) &&
  ((isArticleMoreShown[article.orderNum - 1]) ?
    (
      <button onClick={() => changeArticleMoreShownButton(`naa-article-description-${index}`, article.orderNum - 1, !isArticleMoreShown[article.orderNum - 1])}>COLLAPSE&nbsp;&nbsp;∧</button>
    ) : (
      <button onClick={() => changeArticleMoreShownButton(`naa-article-description-${index}`, article.orderNum - 1, !isArticleMoreShown[article.orderNum - 1])}>READ&nbsp;MORE&nbsp;&nbsp;∨</button>
    )
  )
}

(whew) all done.

Fiddle Freak
  • 1,923
  • 5
  • 43
  • 83