5

I am trying to write a test to work out whether the text rendered inside an <input> has the same baseline as a label:

label adjacent to input with baselines highlighted

In order to do this, I would like to compute the baseline of the text that has been rendered in each element and compare the two values. Is this possible and if so, how is it done? If not, is there a better way to establish that the baseline of the two elements is the same?

I have found a way to determine the baseline of the label which seems to work reliably:

function getBaseline(element) {
    var span = document.createElement("span");
    span.setAttribute("style", "font-size:0");
    span.innerText = "A";
    jQuery(element).prepend(span);
    return span.getBoundingClientRect().bottom;
}

However, this method doesn't work for the <input>, as I cannot insert the span in that case.

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • How big a problem is this? Since these things are `vertical-align:baseline` by default, they _do_ have the same baseline if you don't write any CSS that messes up. – Mr Lister Aug 07 '15 at 11:53
  • 2
    @MrLister the problem is that people _do_ write CSS that messes these things up, which is why I'm trying to write unit tests that will fail when they do! – Tom Fenech Aug 07 '15 at 11:55
  • 1
    [This is messy, but explains my thinking](http://jsfiddle.net/getuxnkc/). It replaces the input element with a span that has every CSS property copied from the input. Could you do something similar to run a check? The CSS copy script is [over here](http://stackoverflow.com/a/18108773/2930477) – misterManSam Aug 07 '15 at 12:28

1 Answers1

2

I found a way to get the computed baseline based on your function. The trick is to create a wrapper and apply all styles from the element to it. I can confirm that it works in Firefox (v38) and Chrome (v44) on OSX. Unfortunately it doesn't work proper in Safari.

DEMO

function getBaseline(element) {
    element = jQuery(element);
    var span = document.createElement("span");
    span.setAttribute("style", "font-size:0");
    span.innerText = "A";

    var wrapper = document.createElement("span");
    applyCSSProperties.call(wrapper, element);
    element.wrap(wrapper);
    element.before(span);

    var computed = span.getBoundingClientRect().bottom;

    span.remove();
    element.unwrap(wrapper);

    return computed;
}

function applyCSSProperties(element) {
    var styles = {};
    var comp = getComputedStyle(element[0]);

    for(var i = 0; i < comp.length; i++){
        styles[comp[i]] = comp.getPropertyValue(comp[i]);
    }

    jQuery(this).css(styles);
}
damian
  • 5,314
  • 5
  • 34
  • 63
  • 1
    In the end I went with a similar technique, which was to create a separate span and apply the computed styles to it, rather than wrapping the input in a span. However, the general approach is the same, so thanks. – Tom Fenech Aug 24 '15 at 11:10