19

I have a component with a div that accepts onClick and aria-disabled properties. Both of them come in as props, with disabled being an optional one. In my render method I do the following:

<div onClick={!this.props.Disabled ? this.props.Click : undefined} 
     aria-disabled={this.props.Disabled>My Div</div>

I use CSS to visualize the disabled state (opacity: 0.5, pointer-event: none blah blah). But I wonder if there's a better way to handle the actual onClick. Since it's a mandatory prop... so even when the item is disabled you still need to pass in an onClick to meet the condition. Is there a better way to go about this?

blankface
  • 5,757
  • 19
  • 67
  • 114

4 Answers4

41

Set pointer-events: none for the div[disabled] in CSS,

test.scss

div[disabled]
{
  pointer-events: none;
  opacity: 0.7;
}

The above code makes the contents of the div disabled, You can make the div disabled by adding the disabled attribute.

Test.js

<div disabled={this.state.disabled}>
  /* Contents */
</div>

Upon clicking the button you can change the state variable disabled as 'true'.

handleClick = () =>{
    this.setState({
      disabled: true,
    });
  }

Reference: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events

Pramod H G
  • 1,513
  • 14
  • 17
  • you might want to write it as pointerEvents or it might throw an exception because the syntax slightly differs for react dom div[disabled] { pointerEvents: none; opacity: 0.7; } – Wajdan Ali May 27 '19 at 07:14
6

Why Answer Now? A developer with whom I work intends to use these answers as a reference. I believe a better answer could benefit him and others.

What Does This Answer Provide? It provides both provide a solution that (a) is semantically accurate, (b) considers accessibility, and (c) uses existing answers; and review the existing answers.

Any Additional Notes? How should a <div> behave differently when "disabled"? It is a "generic container"… nothing to disable. I use the behavior of existing answers:

  • prevent mouse interaction with child elements
  • identify the <div> as "disabled" to certain software

Proposed Solution

Note: This solution uses Functional Components syntax.

div.is-disabled
{
  pointer-events: none;
  opacity: 0.7;
}
/**
 * Render a button and an element whose content it "disables"
 *
 * NOTE: The "disabling" prevents user interaction with its children and to reduce the opacity of the entire element.
 *
 * @prop {Object} props
 * @prop {*} props.children - Any React-parseable children as the content
 * @function
 */
function MyComponent({ children }) {
  /** Whether content is disabled */
  let isDisabled = false;

  /**
   * When user clicks on element that should trigger "disabling" a div
   * @param {UIEvent} event
   * @function
   */
  const onClick (event) => {
    if (!isDisabled) {
      isDisabled = true;
    }
  }

  render (
    <>
      <div
        aria-disabled={isDisabled}
        className={(isDisabled) ? 'is-disabled' : ''}
      >
        {children}
      </div>
      <button onClick={onClick}>
        Disable Content
      </button>
    </>
  );
}

Review of Existing Answers

pramod-h-g's Answer

Great. This suggests a meaning for "disable a <div>", and a solution.

But, the markup concerns me.

  • The <div> tag does not have a disabled attribute ^1 ^2.
  • Using other elements' standard attribute on an element without such an attribute can confuse new developers.
  • Custom attributes (last I checked) require a data- prefix.

jpsimon's Answer

Answers question accurately—no faults.

But, I think the aria-disabled attribute only "disables" the div for some user experiences. Visual browser users would not experience this <div> as being "disabled".

yts's Answer

I agree entirely.

But, this solution does not "disable" a div. The original poster may not be asking the question appropriately, and I support you leading him toward a <button>.

Wesley B
  • 125
  • 2
  • 5
2

I think you're intuition is right -- not ideal to be adding and removing event handlers all the time based on changing props. How about simply wrapping the function and returning as needed? As in the following E.g.

<div onClick={(e) => !this.props.Disabled && this.props.Click(e)}
  aria-disabled={this.props.Disabled}>
    My Div
</div>

Where the guard operator && is shorthand for if logic.

Daniela
  • 71
  • 1
  • 9
jpsimons
  • 27,382
  • 3
  • 35
  • 45
0

A button might be better suited for this. With a button element, the onClick will automatically not fire if the disabled value is true. So you could do:

<button onClick={this.props.Click} disabled={this.props.Disabled}>My button</button>

You also get the accessibility benefits of using a button over a div.

There is nothing wrong with the fact that the click prop is "extra" when the button is disabled, and I'd say it's better than changing the structure of the passed in props based on the disabled state.

yts
  • 1,890
  • 1
  • 14
  • 24
  • 1
    @ZeroDarkThirty is there a reason for that? Buttons are generally better for accessibility purposes anyway and you seem to be concerned about aria – yts Jul 27 '18 at 01:03