How do you handle text in HTML templates?

Me, again. CSS is killing me. It’s like 2009 all over again. I think I’m going to smash the computer very soon.

How do you handle text in HTML templates? I’m doing OK with updates and transitions but can’t seem to accomplish the simplest thing: Center a block of text vertically. I spent most of the day just trying countless tricks and hacks to make it work but always finding some problems with some scenarios.
I used some JS libraries to make it fit the container and then tried to center it manually. If I set a fixed height it works OK until I start shrinking the text. Some tricks make the text stay at the top of the container, some have very weird behaviors with multiple lines. Can’t seem to find the right way to do that.
In AS3 I just set the text at the maximum size for the template, then scale it and center when it exceeds the bounds of the container. If it’s two or three lines it still will fit centered.
For example:
autosize

Do I have to avoid CSS for the positioning and handle that in JS?

@rrebuffo, it depends on the version of Caspar you are building for.

2.7.0 and 2.1.0 Beta use Chromium 33.0.1750.170 which means you need to do everything with JS. We can talk more if this is the case.

2.2.0 uses Chromium 63.0.3239.132 (Found here), which gives you CSS Flexbox.

Here is a CodePen with the example.

You will need to use JavaScript’s window.getComputedStyle method to get the current width of the element then set it’s style to be that width.

Or you take this neat little helper function below and just watch the magic happen!

Simply call getElemComputedStyles() with an CSS selector or DOM element and then pass an array or string of attributes you want back and any operations you want done with them.

//  Checks for spaces in a value and if they are found, splits and adds each value together
    //  @param {string} value - The string to check for spaces or return as a number
    //  @return {number} - The sum of the value passed in.
    function addStyleValues(value, width, height) {
        if(value.indexOf(' ') !== -1) {
            const values = value.split(' ');
            value = values.reduce((acc, val, index) => {
                if(index % 2 !== 0 && !width) return acc;
                if(index % 2 === 0 && !height) return acc;
                acc = values.length === 2 ? acc + (Number(val)* 2) : acc + Number(val);
                return acc;
            }, 0);
            return value;
        } else {
            return Number(value);
        }
    }
    // Takes an element and returns the computed styles as a total or object of all the values
    // @param {string || DOM node} elem - The elemnt to get the style from
    // @param {string || array} attrs - The attribute/s that need to have their values computed
    // @param {object} direction - The direction to compute. width or height
    // @param {string} operation - The operation to perform on the attribute values
    function getElemComputedStyles({elem, attrs, direction, ops}) {
        if(!elem || !attrs) throw 'Missing element or attributes for getElemComputedStyles';
        if(typeof elem === 'string') elem = document.querySelector(elem);
        if(!direction) direction = {width: true, height: false};
        const compStyles = window.getComputedStyle(elem);
        try {
            if(Array.isArray(attrs)) {
                return attrs.reduce((acc, prop, i) => {
                    const rawValue = compStyles.getPropertyValue(prop).replace(/px/g, '');
                    const value = addStyleValues(rawValue, direction.width, direction.height);
                    if(isNaN(value)) throw new Error(`${prop} could not be used`);
                    const operation = Array.isArray(ops) && ops[i] 
                        ? ops[i] : ops; 
                    switch(operation) {
                        case 'add': 
                        default:
                            acc.total += value;
                            break;
                        case 'subtract':
                            acc.total -= value;
                            break;
                        case 'multiple':
                            acc.total *= value;
                            break;
                        case 'divide': 
                            acc.total /= value;
                            break;
                        case 'all':
                            acc[prop] = value
                            break;
                    }
                    return acc;
                }, {total: 0});
            } else {
                return addStyleValues(compStyles.getPropertyValue(attrs).replace(/px/g, ''), direction.width, direction.height);
            }
        } catch (error) {
            return console.error(error);
        }
    }

Here is an example:

Say we have this DOM element.

<div class="someClass"></div>

And we wanted to know it’s width and padding total; which on the CSS file would be 200px.

.someClass {
  width: 100px;
  padding: 100px;
}

Trying document.querySelector('.someClass').style.width will return undefined so instead use.

const widthAndPaddingTotal = getElemComputedStyles({
   elem: '.someClass', 
   attrs: ['width', 'padding'],
   // This tells the helper, for any properties that return two values,
   // like padding (100px 100px), use either the height, width or both.
   direction: {width: true, height: false},
   ops: 'add'
});

widthAndPaddingTotal will now equal the width of the object plus the padding on the left and the right.

Please let me know if that helps!

Testing in server version 2.3.0 d442a8f8 and in Chromium 71.0.3578.98 so Flexbox is available.
I will look into it. Thanks!

or you can use line height CodePen Here

1 Like

Here’s a simple one that I ripped out of one of my projects: https://codepen.io/hreinnbeck/pen/ExxOLqB

1 Like

Well I did it! That you all so much, specially @ouellettec for the patience and taking the time to explain it very well!

That did the trick. I simply shrink the text until it fits (not very efficient but it works).
Can’t recall which codepen I used :joy: at this point is a proper frankenstein.

Here you have a test I recorded:

2 Likes

Triggert by you guys i found this on the WWW

and changed it a bit to this

<html>
<head>
<style>

.parent {
  justify-content: center;
  display: flex;
  flex-direction: column;
  left:96px;
  bottom:96px;
  position: fixed;
  min-height: 80px;
  text-align: center;
  background-color: blue;
}

#f0 {
  display: inline-block;
  color: white;
  width: 400px;
  height: min-height;
  line-height: min-height;
  font-family: 'impact';
  font-size: 80px;
  white-space: nowrap;
}

h1{
    color:white;
}

</style>
</head>

<body>

<div class='parent'>
    <div id='f0' class='resizable'>f0</div>   
</div>
<h1 id="size"></h1>   
<script>

function resizeContents() {
    var resizableDivs = document.getElementsByClassName('resizable');
    for (index = 0; index < resizableDivs.length; ++index) {
        var div = resizableDivs[index];
        if (div.innerText != '') {
            var divWidth = div.clientWidth;
            var divHeight = div.clientHeight;
            var contentWidth = div.scrollWidth;
            var contentHeight = div.scrollHeight;
            const style = getComputedStyle(div)
            var fontSize = style.fontSize;
            fontSize = Number(fontSize.substring(0, fontSize.length - 2));

            while (contentWidth <= divWidth && contentHeight <= divHeight) {
                fontSize += 1;
                div.style.fontSize = fontSize + 'px';
                contentWidth = div.scrollWidth;
                contentHeight = div.scrollHeight;
                document.getElementById('size').innerHTML = 'Fontsize=' + fontSize
            }

             while (contentWidth > divWidth || contentHeight > divHeight) {
                 fontSize -= 1                 
                 if (fontSize < 1) {
                    break;
                 }
                 div.style.fontSize = fontSize + 'px';
                 contentWidth = div.scrollWidth;
                 contentHeight = div.scrollHeight;
                 document.getElementById('size').innerHTML = 'Fontsize=' + fontSize
            }
        }
    }
}


function play() {
    // keep caspar happy or animate stuff
}

function update(dataCaspar)   {
       let templateData = JSON.parse(dataCaspar)
       let keys = Object.keys(templateData)
       for (let i in keys) {
        if (document.getElementById(keys[i])) {
            document.getElementById(keys[i]).innerHTML = templateData[keys[i]]
            resizeContents()
        } 
    }
}

</script>
</body>
</html

What is the editor you’re using ? Own dev ? Open source ?

That’s an app I developed for a couple of clients as part of a Caspar based solution. Is mainly based on this prototype.
Only some libraries are open source, the rest is not.