Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combined percent+pixel div layout

10 years ago I wrote a GUI layout engine in C++ and I'm curious how its functionality could be best approximated in the browser.

1. C++

In older GUI libraries (like Microsoft Windows'), the position and size of widgets is usually given by four numbers: left, top, width and height. My engine is different in that each of these numbers is doubled, so you have to specify eight numbers: relative left, absolute left; relative top, absolute top; relative width, absolute width; relative height and absolute height. The relative values should be given in percents of the width or height of the parent widget, and the absolute values should be given in pixels. In case of top-level windows, the "parent widget" is the desktop.

Examples:

a) 400 x 300 px window, centered on screen:

const int WINDOW_WIDTH = 400;
const int WINDOW_HEIGHT = 300;
CWindow mainWindow;
mainWindow.setPosition(0.5, -WINDOW_WIDTH / 2, 0.5, -WINDOW_HEIGHT / 2); 
mainWindow.setSize(0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);

b) 150 px wide button container panel at the right edge of the main window:

const int PANEL_WIDTH = 150;
CPanel buttonPanel(mainWindow);
buttonPanel.setPosition(1.0, -PANEL_WIDTH, 0.0, 0); 
buttonPanel.setSize(0.0, PANEL_WIDTH, 1.0, 0);

c) Three buttons (New, Open, Save) at the top of the container panel, and one button (Exit) at the bottom:

const int BUTTON_HEIGHT = 30;
CButton newButton(buttonPanel, "New");
newButton.setPosition(0.0, 0, 0.0, 0 * BUTTON_HEIGHT);
newButton.setSize(1.0, 0, 0.0, BUTTON_HEIGHT);
CButton openButton(buttonPanel, "Open");
openButton.setPosition(0.0, 0, 0.0, 1 * BUTTON_HEIGHT);
openButton.setSize(1.0, 0, 0.0, BUTTON_HEIGHT);
CButton saveButton(buttonPanel, "Save");
saveButton.setPosition(0.0, 0, 0.0, 2 * BUTTON_HEIGHT);
saveButton.setSize(1.0, 0, 0.0, BUTTON_HEIGHT);
CButton exitButton(buttonPanel, "Exit");
exitButton.setPosition(0.0, 0, 1.0, -1 * BUTTON_HEIGHT);
exitButton.setSize(1.0, 0, 0.0, BUTTON_HEIGHT);

Writing such layout code by hand may feel awkward when you’re new to it, but it’s easy once you get the hang of it.

2. HTML, CSS, JavaScript

Here is what I was able to hack together thus far:

a) The hierarchy of the main window, the button panel, and the buttons, is given in HTML:

<div id="mainWindow">
    <div id="buttonPanel">
        <div id="newButton" class="button"></div>
        <div id="openButton" class="button"></div>
        <div id="saveButton" class="button"></div>
        <div id="exitButton" class="button"></div>
    </div>
</div>

b) The divs' position is set to absolute in CSS:

* {
    -webkit-box-sizing: border-box; 
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
body { margin: 0; color: yellow; }
#mainWindow { position: absolute; background: red; }
#buttonPanel { position: absolute; background: green; }
.button { border: 1px solid cyan; }
#newButton { position: absolute; background: blue; }
#openButton { position: absolute; background: blue; }
#saveButton { position: absolute; background: blue; }
#exitButton { position: absolute; background: blue; }

c) The layout functionality is implemented in JavaScript, using jQuery:

function setPosition(widget, relLeft, absLeft, relTop, absTop) {
    $(widget)
        .css('left', relLeft * 100 + '%').css('left', '+=' + absLeft + 'px')
        .css('top', relTop * 100 + '%').css('top', '+=' + absTop + 'px');
}

function setSize(widget, relWidth, absWidth, relHeight, absHeight) {
    $(widget)
        .css('width', relWidth * 100 + '%').css('width', '+=' + absWidth + 'px')
        .css('height', relHeight * 100 + '%').css('height', '+=' + absHeight + 'px');
}

function setButtonText(button, text, height) {
    $(button)
        .text(text)
        .css('text-align', 'center')
        .css('vertical-align', 'middle')
        .css('line-height', height + 'px');
}

window.onresize = function () {
    var WINDOW_WIDTH = 400,
        WINDOW_HEIGHT = 300,
        PANEL_WIDTH = 150,
        BUTTON_HEIGHT = 30;

    setPosition('#mainWindow', 0.5, -WINDOW_WIDTH / 2, 0.5, -WINDOW_HEIGHT / 2);
    setSize('#mainWindow', 0.0, WINDOW_WIDTH, 0.0, WINDOW_HEIGHT);
    setPosition('#buttonPanel', 1.0, -PANEL_WIDTH, 0.0, 0);
    setSize('#buttonPanel', 0.0, PANEL_WIDTH, 1.0, 0);
    setButtonText('#newButton', "New", BUTTON_HEIGHT);
    setPosition('#newButton', 0.0, 0, 0.0, 0 * BUTTON_HEIGHT);
    setSize('#newButton', 1.0, 0, 0.0, BUTTON_HEIGHT);
    setButtonText('#openButton', "Open", BUTTON_HEIGHT);
    setPosition('#openButton', 0.0, 0, 0.0, 1 * BUTTON_HEIGHT);
    setSize('#openButton', 1.0, 0, 0.0, BUTTON_HEIGHT);
    setButtonText('#saveButton', "Save", BUTTON_HEIGHT);
    setPosition('#saveButton', 0.0, 0, 0.0, 2 * BUTTON_HEIGHT);
    setSize('#saveButton', 1.0, 0, 0.0, BUTTON_HEIGHT);
    setButtonText('#exitButton', "Exit", BUTTON_HEIGHT);
    setPosition('#exitButton', 0.0, 0, 1.0, -1 * BUTTON_HEIGHT);
    setSize('#exitButton', 1.0, 0, 0.0, BUTTON_HEIGHT);
};

onresize();

Note that the JavaScript code is very similar to the C++ code shown above!

My question

Is this JavaScript-based layout acceptable to build a "real" web application, or should I use CSS instead? Since I'm basically a desktop app developer, I need expert web developer opinion. Thanks!

=== UPDATE ===

Here is a pure HTML+CSS solution. I used the negative margin trick suggested by @Christoph. My only problem is that the same constants are copied to lots of places in the CSS. For example, button height (30px) is used in nine places, which makes maintenance much harder.

like image 444
kol Avatar asked Jul 01 '13 11:07

kol


3 Answers

In general, inline css (css attached via style="") is considered a bad choice. Since you generate this dynamically via a script it might be acceptable.

However, there are some problems:

  1. Generally, absolute positioning should be used as sparingly as possible.
  2. There is definitely no need to alter the layout in window.onresize. This event should be used with extreme caution. As you can see in the fiddle, your example UI is rather small, but already becoming unresponsive on resizing the window. (And those values in your example are static anyway)

So my hint is to:

  1. Use some kind of init() method where you do the initial layouting
  2. avoid window.onresize or at least use some timeout mechanism to reduce the triggering of those paint events (reduce number of calls for .resize and .scroll methods)
  3. perhaps instead of jQuery's css() calls, write the css for your elements in a separate stylesheet.
  4. best solution: generate the html and css markup serverside and serve the final html+css to the browser. advantages: faster rendering + maintainable + you get a cacheable stylesheet and html
like image 155
Christoph Avatar answered Oct 01 '22 06:10

Christoph


It will always be easier and, performance wise, faster to set initial values with CSS. Setting everything to absolute can work but, depending on the application, can become a layout nightmare since absolutely positioned elements are removed from the normal flow.

While I see where you're coming from, having once coded graphical apps for the desktop, you'll find it doesn't work as well for the web unless you're expecting a fixed layout where layout performance isn't crucial.

But then, I'm thinking along the lines of layout in today's world where you have a multitude of devices in all shapes and sizes you need to adapt to. On the one hand, it could be easier to control all that using javascript, and many times it is the best way, but, otoh, CSS can handle a lot of that with media queries to take the burden off you and your coding.

It's a lot to think about.

like image 40
Rob Avatar answered Oct 01 '22 06:10

Rob


Short answer :

THIS IS BAD ! NEVER DO THAT.

Long answer :

This is not the way the web is supposed to work. Your application won't perform great because css rules will wait for the js to be downloaded and interpreted ; but this is not the main concern here. The main concerne is about you.

You will never be able to maintain a code like this one. When you build a simple application, you can easily go to a few thousands css rules. Impossible to read if organized this way. And I don't even mention the media queries stuff. The complexity of an app build this way would be way too high.

If you wanna go for the web, you have to embrace the web ; not to try your old desktop pplication developper tricks (which are greats for desktop application). For instance, if you want to put something in the top right corner, your go for : right: 0. Not WINDOW_WIDTH; if your ant to center somethin, you have many ways to do so ; but you don't make math for the position. Never ever do that.


As a webapplication developper, my advice is this one :

Go use compass : http://compass-style.org/

Actually no, go use Yeoman : http://yeoman.io/

Thoses tools are a bit complex to fully understand ; but it worth it soooo much.


Btw, never bind an onresizeevent like that. You have to add a debounce otherwise you will meet serious performances troubles.

like image 32
user Avatar answered Oct 01 '22 05:10

user