Search code examples
javascripthtmlcssflexboxcodemirror

Using CSS display:flex (flexbox) and/or JavaScript to make adjustable page


I'm making a simple IDE for a educational programming language similar to Karel the Dog. And I have trouble with making base html page.

I have 4 areas:

  • Toolbox (for buttons like open, save as, run etc.)
  • Field (canvas for drawing executor that can move on the field and do some stuff)
  • Code (CodeMirror editor for writing executor's commands)
  • Console (place where IDE can print messages like compilation errors or runtime debug output)

I wrote what I want from every area in my code so I'll say only what is not working now:

  1. The page should fill 100% screen's height.
  2. Cannot set CodeMirror to fill all available to its parent height. And when its size is greater than parent's height, scrollbars should appear.
  3. The same problem with canvas - but only on vertical.
  4. Is there a way to make a separator between code and field areas that can be used to redistribute horizontal space between areas?

There is another difficulty. If the item number 4 requires JavaScript, then I'll ask to help me with solving it with WinJS 3.0 library because I won't add to the project jQuery or other heavy stuff only for this resize capability.

So, can anyone help me?

I loaded my code to jsfiddle.net and pasted it here:

var ce = CodeMirror(document.getElementById('b-codemirror'), {
  value: "\n\n\nIt is CodeMirror element. [PARAMS ALL] " +
    "width: 100% of parent element, height: always 100% of" +
    " parent element + both scrollbars if needed\n\n\n",
  lineNumbers: true
});
var cc = document.getElementById("canvas").getContext("2d");
cc.font = "16px Helvetica";
cc.fillText("It is canvas. Can be resized from", 10, 30);
cc.fillText("JS. If it is larger than parent element,", 10, 60);
cc.fillText("corresponding scrollbar should appear.", 10, 90);
@import url("http://codemirror.net/lib/codemirror.css");

/* overriding default codemirror.css */
.CodeMirror {
  font-family: monospace;
  height: 100%;
}
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}
.b-section {
  margin: 2px;
  padding: 4px;
  border-radius: 4px;
}
#b-fieldcode {
  min-height: 640px;
  display: -webkit-flex;
  display: flex;
  -webkit-flex-flow: row;
  flex-flow: row;
}
#b-toolbox {
  background: #ffeebb;
}
#b-console {
  height: 100px;
  background: #ffeebb;
}
#b-field {
  background: #ccccff;
  overflow: auto;
  -webkit-flex: 1 1 40%;
  flex: 1 1 40%;
  -webkit-order: 1;
  order: 1;
}
#b-code {
  background: #dddd88;
  -webkit-flex: 1 1 60%;
  flex: 1 1 60%;
  -webkit-order: 2;
  order: 2;
}

@media all and (max-width: 1024px) {
  #b-fieldcode, #page {
    -webkit-flex-flow: column;
    flex-flow: column;
    flex-direction: column;
  }
  #b-code, #b-field {
    -webkit-order: 0;
    order: 0;
  }
  #b-field, #b-code {
    height: 500px;
  }
}
<script type="text/javascript" src="http://codemirror.net/lib/codemirror.js"></script>
<div id="b-toolbox" class="b-section">
  Here comes the space for buttons.
  [PARAMS ALL] width: 100% of screen, height: sized to content
</div>
<div id="b-fieldcode">
  <div id="b-field" class="b-section">
    Here comes canvas wrapper.<br />
    [PARAMS landscape] width: flex 40% of screen, height:
    100% of screen minus b-toolbox and b-console.
    <br />[PARAMS portrait] width: 100% of
    screen, height: fixed 400px.<br />
    <canvas width="300" height="300" id="canvas"
            style="background-color: green" />
  </div>
  <div id="b-code" class="b-section">
    Here comes CodeEditor wrapper.<br />
    [PARAMS landscape] width: flex 60% of screen, height:
    100% of screen minus b-toolbox and b-console.<br />
    [PARAMS portrait] width: 100% of
    screen, height: fixed 500px.
    <div id="b-codemirror"></div>
  </div>
</div>
<div id="b-console" class="b-section">
  Here comes output console.
  [PARAMS ALL] width: 100% of screen, height: fixed 120px.
</div>


Solution

  • First of all, you need to split styles for portrait and landscape. For portrait part, that's simple easy, so let's skip it.

    For landspace part, you need a fluid height header (buttons) and a fixed height footer (console). This is a typical use case of css flex - all spare space are going to main part. So you just set display: flex; flex-direction: column; to <body> and flex: 1; to the main part (#fieldcode in your snippet).

    Then #field occupies 40% width of #fieldcode, and #code ocuppies 60%. So again, you set display: flex; flex-direction: row; to #fieldcode, and flex: 4; to #field, flex: 6; to #code, so that spare space of #fieldcode is separated as 4:6. But please note the difference from previous. Yes, the flex-direction value is different. That tells the browser either to separate horizontally, or vertically.

    html, body {
      margin: 0;
      padding: 0;
    }
    #toolbox {
      background: #feb;
    }
    #field {
      background: #ccf;
      overflow: auto;
    }
    #code {
      background: #dd8;
      overflow: auto;
    }
    #codemirror {
      min-width: 100%;
      min-height: 100%;
    }
    #console {
      background: #feb;
      height: 120px;
    }
    
    @media screen and (orientation: portrait) {
      #field {
        height: 400px;
      }
      #code {
        height: 500px;
      }
    }
    
    @media screen and (orientation: landscape) {
      html, body {
        height: 100%;
      }
      body {
        display: flex;
        flex-direction: column;
      }
      #fieldcode {
        flex: 1;
        display: flex;
        flex-direction: row;
      }
      #field {
        flex: 4;
      }
      #code {
        flex: 6;
      }
    }
    <div id="toolbox">buttons here</div>
    
    <div id="fieldcode">
      <div id="field">
        <canvas></canvas>
      </div>
    
      <div id="code">
        <div id="codemirror"></div>
      </div>
    </div>
    
    <div id="console">console here</div>