3

I have a react component that renders a div with some containing menu item elements - inline-block (all horizontally aligned)

with text "Toy Store", "Configure your Toy", "About Us"

So how does one accomplish this: When the window size changes i dont want the items to stack upon each other, but first change the manu text to "Toys", "Configure" and "About" just at the point when the parent element couldn't keep them in one line because its not wide enough anymore (not on a fixed window breakpoint). When space gets even too narrow for that change text to "T", "C", "A" (dont question why, it should be like this)

So since this is not only a style change but also content, how can this be done with a react element?

render () {

    const { theme } = this.props;
    const { classes } = this.props;

    return(
        <div style={{flex: 1}}>
            <Button color="inherit" variant={this.state.btnVar0} onClick={(e) => this.toggleMenuBtns(0,e)}><span className = {classes.menuButtonMed}  >T{this.state.squeeze > 1 ? null : <span      >oy</span>}</span>{this.state.squeeze > 0 ? null : <span> Store</   span>}</Button>
            <Button color="inherit" variant={this.state.btnVar1} onClick={(e) => this.toggleMenuBtns(1,e)}><span className = {classes.menuButtonTreat}>C{this.state.squeeze > 1 ? null : <span>onfigure</span>}</span>{this.state.squeeze > 0 ? null : <span> your Toy</span>}</Button>
            <Button color="inherit" variant={this.state.btnVar2} onClick={(e) => this.toggleMenuBtns(2,e)}><span className = {classes.menuButtonCom}  >A{this.state.squeeze > 1 ? null : <span    >bout</span>}</span>{this.state.squeeze > 0 ? null : <span> Us</      span>}</Button>
        </div>

    );
}
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
haemse
  • 3,971
  • 5
  • 28
  • 40

3 Answers3

1

If you do not want to rely on preset breakpoints (as appears to be the case), then your only solution is to compute the width of your menu elements and compare it to the width of your client window. If the global width of your menu elements exceeds that of your window, update the content of the menu elements with their first shorter alternative, then their second shortest one.

You would need to do that on initial render, and on any window resize event.

A way to do the width comparison could be using a combination of React Refs, Element.clientWidth and window.innerWidth.

As for switching from one text alternative to the other, I can think of several ways to do it (using CSS, directly updating text content, using state and/or props, etc.).

I will let you work out the implementation details on your own.

Thomas Hennes
  • 9,023
  • 3
  • 27
  • 36
  • Nice explanation, I think the important keywords are all mentioned. I was also studying this question, so I'll add some references below. – Marson Mao May 22 '18 at 03:27
  • https://stackoverflow.com/questions/7138772/how-to-detect-overflow-in-div-element this might be another idea and is implemented by jQuery, but I guess it is similar to the resize-event way; one needs to clone the original component and compare the cloned one (which never become less in width) with the current one. – Marson Mao May 22 '18 at 03:29
  • By the way I'm not suggesting to use jQuery, but one could still try to clone the element using React API and compare the elements. – Marson Mao May 22 '18 at 03:32
  • The resize-event way is also very convicing. Hope OP could find a nice solution. – Marson Mao May 22 '18 at 03:33
  • Prob. is, in order to have a standalone React component you cant really calculate the containers "childrens - wrapping breakpoint" without knowing the page structure - cause who knows whats left and right to that component ... probably a better way is using the parent elment of the react element ... since this is all quite a workarount Im not sure if I should go that way. However I see a bit of a systematic problem here, since you will alway rely on preset css breakpoints that will have to be tailored to your content in that case, if you really want to change content on the real threshold. – haemse May 22 '18 at 04:49
  • If the parent element has a set width (and by that I do not mean fixed, I mean a width that is set independently of that of its children), then yes, you could make the component standalone in the way I described. Obviously if the children are setting the width of the parent element, there is no solution since it becomes a circular problem (children set parent width, which is then used to compute children's width). – Thomas Hennes May 22 '18 at 05:03
  • And you do not have to rely on set breakpoints. You could use a flexbox as the parent of the parent element, and set the parent element to have the 'flex-grow' css attribute to occupy available space. – Thomas Hennes May 22 '18 at 05:07
  • I think the requirements are 1. child must have a percent-based width so it would resize based on parent 2. the "original" or the "break-point" width is somehow tricky to decide, since you must render the element with desired content once to record the proper width, after that you can compare the current width with recorded width, however how to make sure you have enough space to render the desired content for the first time is...impossible? Because eventually, you'll have not enough width even if you make it 1920px wide with super long content, then the first-time render would fail. – Marson Mao May 22 '18 at 05:47
  • @MarsonMao Why? No I disagree, flexbox items do not need to be set with 'hard' values and have the ability to shrink/grow based on whether space is available or not. You and haemse are making this much more complicated than it needs to be. – Thomas Hennes May 22 '18 at 08:25
0

enter image description hereYou can achieve it using bootstrap. enter image description here Working code:

<ul className="nav navbar-nav">
        <li>
          <a>
            <span className="hidden-lg">Toys</span>
            <span className="hidden-xs">Toy Store</span>
          </a>
        </li>
        <li>
          <a>
            <span className="hidden-lg">Configure</span>
            <span className="hidden-xs">Configure your Toy</span>
          </a>
        </li>
        <li>
          <a>
            <span className="hidden-lg">About</span>
            <span className="hidden-xs">About Us</span>
          </a>
        </li>
      </ul>
Johnson Samuel
  • 2,046
  • 2
  • 18
  • 29
  • isnt that tied to predefined breakpoints, rather than the event of "doesnt fit horizontally anymore"? – haemse May 21 '18 at 07:21
  • I think we can always use media queries and customise it according to our needs. – Johnson Samuel May 21 '18 at 07:40
  • yes, but thats my point - depending on the widht of the content of the items, i dont want to have it directly dependent on the window size, I'd like to have it as a standalone react element. who knows at which window size at the individual website layout configuration the items will start to wrap? do you know what i mean? – haemse May 21 '18 at 08:04
0

Here is my solution JSFiddle,

Check the JSFiddle by resizing the output window.

Its not perfect, but it will give u a starting point

Basically I used css content:attr(attib-name) for this.

I made 3 attibutes data-small, data-med, data-big

 <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>

and I use

.inline:after {
  content: attr(data-big);
  display: block;
  text-align: center;

}

@media(max-width:768px) {
  .inline:after {
    content: attr(data-med);
  }
}

@media(max-width:500px) {
  .inline:after {
    content: attr(data-small);
  }
}

To set different values.

* {
  box-sizing: border-box;
}

body {
  padding: 20px;
}

.parent {}

.inline {
  border: 1px solid black;
  display: inline-block;
  min-width: 50px;
  position: relative;
  padding: 5px 10px;
}

.inline:after {
  content: attr(data-big);
  display: block;
  text-align: center;
}

@media(max-width:768px) {
  .inline:after {
    content: attr(data-med);
  }
}

@media(max-width:500px) {
  .inline:after {
    content: attr(data-small);
  }
}
<div class="parent">
  <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>
  <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>
  <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>
  <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>
  <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>
  <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>
  <div data-small="T" data-med="Toys" data-big="Toys Stuff" class="inline"></div>
</div>
Gautam Naik
  • 8,990
  • 3
  • 27
  • 42
  • thats a neat css only approach. however id like to have it dependent on a single value only - when the parent element would be to small in width so the children would beginn to wrap vertically. Who knows when this will happen on an individual website layout?Would like the react element to be a standalone solution. – haemse May 21 '18 at 08:09