skip to Main Content

I am trying to figure out a good way to display guitar chords on the web in a variable-width font, preferably using just HTML and CSS if possible. I’m trying to do this by lining up the chord above a specific letter.

I’ve come up with the following solution (
https://jsfiddle.net/u33v87ob/ ):

HTML:

<div class="chunk">
  <span class="chord"></span>
  <br>
  <span class="lyric">As&nbsp;</span>
</div>
<div class="chunk">
  <span class="chord">C</span>
  <br>
  <span class="lyric">I was going over the&nbsp;</span>
</div>
<div class="chunk">
  <span class="chord">Am</span>
  <br>
  <span class="lyric">far fam'd Kerry Mountains,</span>
</div>

CSS:

.chunk {
  float: left;
}

From a display perspective this works perfectly. However, search engines read it like this, which means that I lose out on search results for the lyrics:

As CI was going over theAmfar fam'd Kerry Mountains

Trying to copy+paste results in garbled output as well. I would rather copied text look like:

CAm
As I was going over the far fam'd Kerry Mountains,

Is there some way I can accomplish this?

Edit: For posterity, here is an extension on the original question which you should definitely check out if this answer is helpful!

2

Answers


  1. Why not simply relying on pseudo element and data attribute:

    p {
     margin-top:50px;
    }
    span.chunk {
     position:relative;
    }
    span.chunk:before {
      content:attr(data-chord);
      position:absolute;
      top:-15px;
    }
    <p>
    As
    <span class="chunk" data-chord="C">I was going over the</span> 
    <span class="chunk" data-chord="Am">far fam'd Kerry Mountains,</span></p>
    Login or Signup to reply.
  2. Answer

    I found a way of doing the same thing as @Temani Afif but also making sure that the chords do not overlap. When they are too close together, we move them to the right the required distance for them not to go over each other.

    See jsfiddle.

    HTML

    <p>
      As <span class="chunk" data-chord="C"></span>I was going over 
      <span class="chunk" data-chord="Am"></span>th<span class="chunk" data-chord="E"></span>e
    </p>
    <p>
      <span class="chunk" data-chord="Am"></span>
      <span class="chunk" data-chord="C"></span>far fam'd Kerry 
      <span class="chunk" data-chord="C7"></span>Mountains
    </p>
    

    JavaScript

    // Function to get the width of the chord text
    function getTextWidth(text, font) {
      // if given, use cached canvas for better performance
      // else, create new canvas
      var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
      var context = canvas.getContext("2d");
      context.font = font;
      var metrics = context.measureText(text);
      return metrics.width;
    };
    
    // Function to obtain a CSS property of an element
    function getCSS(element, property) {
      return window.getComputedStyle(element, null).getPropertyValue(property);
    }
    
    // Function to check if two chord spans overlap
    function chordsOverlap(el1, el2) {
      const domElement1 = el1.getBoundingClientRect();
      const domElement1Text = getTextWidth(el1.getAttribute('data-chord'), getCSS(el1, 'font'));
      const domElement2 = el2.getBoundingClientRect();
      const domElement2Text = getTextWidth(el2.getAttribute('data-chord'), getCSS(el2, 'font'));
    
      return !(
        domElement1.top > domElement2.bottom ||
        domElement1.left + domElement1Text < domElement2.left ||
        domElement1.bottom < domElement2.top ||
        domElement1.left > domElement2.left + domElement2Text
      );
    }
    
    // Function to get the x-offset between two chords that do overlap
    function xOffset(el1, el2) {
      const domElement1 = el1.getBoundingClientRect();
      const domElement1Text = getTextWidth(el1.getAttribute('data-chord'), getCSS(el1, 'font'));
      const domElement2 = el2.getBoundingClientRect();
      const domElement2Text = getTextWidth(el2.getAttribute('data-chord'), getCSS(el2, 'font'));
    
      return domElement1.left + domElement1Text - domElement2.left;
    }
    
    
    // Function to callibrate the chords so that they do not overlap
    function callibrateChords() {
      const chords = document.querySelectorAll('span.chunk');
      const spaceWidth = getTextWidth('u00A0', getCSS(chords[0], 'font'));
      for (let i = 0; i < chords.length - 1; i++) {
        let ch1 = chords[i];
        let ch2 = chords[i + 1];
        if (chordsOverlap(ch1, ch2)) {
          //console.log(i, i+1, true, xOffset(ch1, ch2))
          let chordContent = ch2.getAttribute('data-chord').replace(/u00A0/g, '');
          ch2.setAttribute('data-chord', 'u00A0'.repeat(Math.round(xOffset(ch1, ch2) / spaceWidth) + 1) + chordContent)
        } else {
          //console.log(i, i+1, false)
        }
      }
    }
    
    callibrateChords();
    

    CSS

    p {
      margin-top: 20px;
    }
    
    span.chunk {
      position: relative;
    }
    
    span.chunk:before {
      content: attr(data-chord);
      position: absolute;
      top: -15px;
      color: red;
    }
    

    References

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search