Home > Blockchain >  Automatically disable a button once there's no associated element left
Automatically disable a button once there's no associated element left

Time:07-11

I have a list with several elements, and some controlling buttons that can hide/show these elements. Some buttons have control over just one element, while others have multiple elements. Do you maybe know how can I gray out a button automatically when all of its associated elements are already hidden by other buttons? For example:

Button01 hides/shows ElementX and ElementY,
Button02 hides/shows only ElementY.
What I want to do is, graying out Button02 automatically once ElementY is hidden by the other button.

for (let button of document.querySelectorAll(".filterbutton")) {
    button.addEventListener("click", filter);
}

let filters = new Set;

function toggleDisplay(selector, display) {
    let elems = document.querySelectorAll(selector);
    for (let elem of elems) {
        elem.style.display = display;
    }
}

function filter() {
    let filterSelector = this.dataset.filter;
    let show = filters.delete(filterSelector);
    this.style.color = show ? "" : "rgb(200,200,200)";
    if (!show) {
        filters.add(filterSelector); // toggle this filter
    } else {
        toggleDisplay(filterSelector, "");
    }
    if (filters.size) {
        toggleDisplay([...filters].join(","), "none");
    }
}
<div  data-filter=".filter01">Filter01</div>
<div  data-filter=".filter02">Filter02</div>

<div >ElementX</div>
<div >ElementY</div>

CodePudding user response:

You would need to compare the shown elements with the filters on each button when a button gets clicked. See the solution below

const btns = Array.from(document.getElementById( 'filters' ).children);
const els = Array.from(document.getElementById( 'elements' ).children);

document.addEventListener( 'click', function( event ) {
  const target = event.target.parentElement;

  if ( target.hasAttribute('data-filter') ) {

    const filter = target.getAttribute( 'data-filter' ).split(" ");

    /* If button is active remove matching elements else show */
    target.classList.contains( 'active' )
      ? filter.forEach( el => els[el - 1].classList.remove( 'show' ) )
      : filter.forEach( el => els[el - 1].classList.add( 'show' ) );

    btns.forEach( btn => {
      const filter = btn.getAttribute( 'data-filter' ).split(" ");
      
      /* Empty array to push true/false if buttons matching elements are visible */
      const matches = [];
      filter.forEach( match => matches.push( els[match - 1].classList.contains( 'show' ) ) );
      
      /* If any matches are visible button is active */
      matches.includes( true )
        ? btn.classList.add( 'active' )
        : btn.classList.remove( 'active' );
    });
  }
});
* { box-sizing: border-box } body { font-family: monospace; margin: 0 } hr { margin: 1em 0 }

:root { --transTime: .25s; --transFunction: ease-in-out }

#filters button, #elements article {
  --trans: opacity var(--transTime) var(--transFunction);
  transition: var(--trans); -o-transition: width var(--trans); -moz-transition: var(--trans); -webkit-transition: var(--trans);
}

#filters, #elements { display: flex; gap: 1em }

#filters button {
  border: 1px solid currentColor;
  border-radius: .375em;
  color: black;
  font-family: inherit;
  opacity: 0.5;
  padding: .5em 1em;
  position: relative;
}
#filters button.active { opacity: 1 }

#filters button span::before {
  border-radius: .375em;
  content: "";
  cursor: pointer;
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: 1;
}

#filters button span:first-of-type { display: none }
#filters button span:last-of-type { display: inline }

#filters button.active span:first-of-type { display: inline }
#filters button.active span:last-of-type { display: none }

#elements article {
  background: lightgrey;
  flex: 1 0 0%;
  opacity: 0;
  padding: 1em;
  text-align: center;
}
#elements article.show { opacity: 1 }
<div id="filters">
  <button  type="button" data-filter="1 2 3 4">
    <span>Hide</span><span>Show</span> All
  </button>
  <button  type="button" data-filter="1">
    <span>Hide</span><span>Show</span> 1
  </button>
  <button  type="button" data-filter="2 3">
    <span>Hide</span><span>Show</span> 2 & 3
  </button>
  <button  type="button" data-filter="4">
    <span>Hide</span><span>Show</span> 4
  </button>
</div>

<hr>

<div id="elements">
  <article >Element 1</article>
  <article >Element 2</article>
  <article >Element 3</article>
  <article >Element 4</article>
</div>

CodePudding user response:

Since you have not given a specific problem that can be expressed by code, the answer can only be given algorithmically:

Аdd class active to the visible elements. after clicking the filter button, remove this class from the filtered elements. at the end of filtering, go through all the buttons and if the result is document.querySelectorAll('.active.filter') empty set - disabled the filter button.

CodePudding user response:

You would need to do the following:

  • create function that will return the list of elements whose display would change by the click of a given button -- without actually changing anything.

  • Use the above function to actually perform the action on the click of a button.

  • At the same time update the status of each button by calling the first function above. If that function returns an empty list, then disable the corresponding button, otherwise enabled it.

For all styling use CSS classes. This makes it easier to use such styling in CSS selectors.

In the below example snippet, if you apply the filtering through the second and third button, the first button should be disabled.

for (const button of document.querySelectorAll(".filterbutton")) {
    button.addEventListener("click", filter);
}

const filters = new Set;

function getElementsToToggle(button) {
    const filterSelector = button.dataset.filter;
    const selector = filterSelector   (
        !filters.has(filterSelector) ? ":not(.hidden)"
        : filters.size <= 1 ? ".hidden"
        : ".hidden:not("   [...filters].filter(selector => selector != filterSelector).join(",")   ")"
    );
    return document.querySelectorAll(selector);
}

function filter() {
    if (this.classList.contains("disabled")) return; // No click handling allowed

    const elems = getElementsToToggle(this);
    const show = filters.delete(this.dataset.filter);
    if (!show) {
        filters.add(this.dataset.filter); // toggle this filter
    }
    this.classList.toggle("activefilter"); // Change button style
    for (const elem of elems) {
        elem.classList.toggle("hidden"); // Toggle display of selected elements
    }
    updateButtonStatus();
}

function updateButtonStatus() {
    for (let button of document.querySelectorAll(".filterbutton")) {
        button.classList.toggle("disabled", !getElementsToToggle(button).length);
    }
}

// On page load, initialise status of buttons:
updateButtonStatus();
.filterbutton { border: 2px solid; display: inline-block; background: lightblue; padding: 5px; cursor: pointer }
.activefilter { background: blue }
.disabled { cursor: not-allowed !important; background: grey; border-color: silver }
.hidden { display: none }
<div  data-filter=".filter01">Filter 01</div>
<div  data-filter=".filter02">Filter 02</div>
<div  data-filter=".filter03">Filter 03</div>

<div >This element has filter01, filter02 and filter03</div>
<div >This element has filter01 and filter02</div>
<div >This element has filter01 and filter03</div>
<div >This element has filter02 and filter03</div>
<div >This element has filter02 only</div>
<div >This element has filter03 only</div>

  • Related