Formatting issue with chords and lyrics

I am in the process of creating a website that features chords and lyrics. However, I want to ensure that the chords cannot be copied due to some formatting issues. The main objective is for users to be able to copy the chord and lyrics together and paste them into MS Word or any text editor while still maintaining the content (text, not html) without affecting its layout, which includes having the chord positioned above the corresponding lyrics.

https://i.sstatic.net/ZNz1b.jpg

Check out the jsfiddle here

var markUpChordLines = function() {
  jQuery('.post-content').html(function(i, html) {
    return html.replace(/\[(.*?)\]/g, '<span class="chord" data-chord="$1"></span>');
  });

  jQuery('.chord').each(function () {
    jQuery(this.nextSibling).wrapAll('<span class="lyric_content"></span>');
    jQuery(this.nextSibling).appendTo(this);
  });
};

markUpChordLines();
span.chord {
  position       : relative;
  display        : inline-flex;
  flex-direction : column;
  vertical-align : bottom;
  }
span.chord:before {
  content     : attr(data-chord);
  position    : relative;
  font-style  : italic;
  font-weight : bold;
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="post-content">
  <p style="display: none;">G  D </p>
  <p>Al [G]contrario di [D]te  </p>
  <p>Io[F] non lo s[A]o</p>
  <p>[D] Se è g[G]iusto co[F]sì    </p>
  <p>C[A]omunqu[G]e [D]sia</p>
  <p>Io [G#sus]non mi mu[Fm]ovo</p>
  <p>Io r[Bm]esto qu[E]i</p>
</div>

Answer №1

After struggling with the rtf format and wasting time, I eventually found success by working with html and tables.

const txtP        = document.querySelectorAll('.post-content p')
  ,  rgxCut       = /(?=\[)|(?<=\])/
  ,  zCopyChanged = document.querySelector('#copyChangedZone')
  ;
txtP.forEach(pLine=>pLine.innerHTML = setParagraph_Chord( pLine.textContent ))
  
zCopyChanged.addEventListener('copy', evt => 
  {
  let lines =  (''+document.getSelection()).split('\n').filter(Boolean)

  let tPlain = lines.join('\n')
    , t_html = lines.reduce((a,c)=>a+setTable_chord(c),'')

  evt.clipboardData.setData('text/plain', tPlain )
  evt.clipboardData.setData('text/html', t_html )

  evt.preventDefault()
  })

function setParagraph_Chord (line)
  {
  let newP = line
            .replaceAll('] [',']&emsp;[')
            .split(rgxCut)
            .map(el=>
          {
          if (el[0]!=='[') return el
          let chord = el.substring(1,el.length-1)
          return `<span class="chord" data-chord="${chord}">${el}</span>`
          })
  return  newP.join('')
  }
function setTable_chord( line )
  {
  let tc = []
    , tl = []
    , tx = true
    ;
  line.split(rgxCut).forEach(el=>
    {
    if (el[0]!=='[') 
      {
      if (tx) tc.push('');
      tl.push(el)   
      tx = true     
      }
    else
      {
      tc.push(el.substring(1,el.length-1))
      tx = false
      }
    })
  let rep = '<table style="border-collapse:collapse;white-space:pre;"><tboby>'
          + '<tr><td style="padding:0;font:12px;border:none"><b><i>'
          + tc.join('</td><td style="padding:0;font:12px;border:none"><b><i>')
          + '</td></tr><tr><td style="padding:0;font:15px;border:none">'
          + tl.join('</td><td style="padding:0;font:15px;border:none">')
          + '</td></tr></tboby></table><br>'
  return rep
  }
.post-content {
  line-height : 20px;
  font-size   : 16px;
  font-family : Arial, Helvetica, sans-serif;
  margin      : 2em .7em;
  }
span.chord {
  font-size : 0; 
  position  : relative;
  background: #ca7a6c;
  }
span.chord:after {
  position    : absolute;
  font-size   : 12px;
  top         :  -32px;
  content     : attr(data-chord);
  font-style  : italic;
  font-weight : bold;
  color       : blue;
  transform   : rotate( -30deg );  
  }
p {
  margin : .7em;
  }
h3 { margin-bottom:0; } 
h5 { margin:0 .8em; }  
 
<h3> test a copy here <small>(on lyrics) </small>and past it to :</h3>
<h5> 1 - a simple text editor ( kEdit, notePad...)</h5>
<h5> 2 - a writter editor ( LibreOffice Writer, Word...)</h5>
<hr>
<div id="copyChangedZone">
  <div class="post-content">
    <p>Al [G]contrario di [D]te</p>
    <p>Io[F] non lo s[A]o</p>
    <p>[D] Se è g[G]iusto co[F]sì</p>
    <p>C[A]omunqu[G]e [D]sia</p>
    <p>Io [G#sus]non mi mu[Fm]ovo</p>
    <p>Io r[Bm]esto qu[E]i</p>
  </div>

Answer №2

Finally, I have completed the task:

const txtP  = document.querySelectorAll('.post-content p')
  ,  rgxCut = /(?=\[)|(?<=\])/
  ;
txtP.forEach(pLine=>
  {
  let newP =
    pLine.textContent.split(rgxCut)
        .map(el=>
          {
          if (el[0]!=='[') return el
          let chord = el.substring(1,el.length-1)
          return `<span class="chord" data-chord="${chord}">${el}</span>`
          })
  pLine.innerHTML = newP.join('')
  })
.post-content {
  line-height : 20px;
  font-size   : 16px;
  font-family : Arial, Helvetica, sans-serif;
  margin      : 2em .7em;
  }
span.chord {
  font-size : 0; 
  position  : relative;
  }
span.chord:after {
  position    : absolute;
  font-size   : 12px;
  top         :  -30px;
  content     : attr(data-chord);
  font-style  : italic;
  font-weight : bold;
  color       : blue;
}
<div class="post-content">
  <p style="display: none;">G  D </p>
  <p>Al [G]contrario di [D]te</p>
  <p>Io[F] non lo s[A]o</p>
  <p>[D] Se è g[G]iusto co[F]sì</p>
  <p>C[A]omunqu[G]e [D]sia</p>
  <p>Io [G#sus]non mi mu[Fm]ovo</p>
  <p>Io r[Bm]esto qu[E]i</p>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm constantly running into a logic issue that is causing my array output to be filled with zeros

I seem to have encountered a tricky logic error in my code where the output is not as expected due to values not being returned correctly. I'm seeking guidance on how to troubleshoot this issue. Before delving into that, let me provide some context ab ...

Adding individual flag icons for each country in your Datatables can be a significant enhancement to

Below is the code snippet in JavaScript with flags: <script type="text/javascript"> $(document).ready(function() { var dataTable = $("#example").DataTable( { processing: true, bSort: false, ...

What could be causing the data to be displaying as undefined when using AJAX

My issue involves making an ajax call and passing an array with data. However, when I attempt to debug the code in the console, it shows that the data is undefined. What could be causing this? ...

How can we develop a NodeJS function that returns a query result using a callback method?

I'm currently working on verifying the proper operation of a database using mocha/chai in NodeJS. I am looking for a solution to run a SQL query and then validate its execution. The issue I'm facing is that when I reach the assertion, the result ...

Can a file be transferred from an Electron application to an Express server by supplying the file path?

This is my current objective: A user drags and drops a file into Electron Electron uses a python script via child_process.exec to convert the file The conversion process generates a new file in the same directory as the original With knowledge of the path ...

Incorporate the list seamlessly into the remaining content of the page

https://i.sstatic.net/lIsnd.png https://i.sstatic.net/Nftto.png I am currently developing a Vue component that utilizes an API call to search for a list of cities based on user input. My challenge is ensuring that the displayed list does not overlap with ...

Tips for prolonging the visibility of the Bootstrap 5 Toast alert

Within my HTML code, I have defined a toast element: <div class="position-fixed top-0 start-50 translate-middle-x" style="z-index: 9999"> <div id="errNeutralAlreadyExists" class="toast fade bg-solid-white&quo ...

Showing a div element with the power of JavaScript

I want to enhance the accessibility of my website for users who do not have JavaScript enabled. Content that will be visible if the user has JavaScript enabled. Content visible when JavaScript is disabled. By default, DisableJS is set to Display:none; ...

Mongoose: The aggregate method does not filter properly when using Model.field_1.field_2

Explaining a custom function: const getNotificationsForLounge = async (lounge_id) => { try { const notifications = await Notification.aggregate([ { $match: { "lounge_join_request.lounge": lounge_id, loun ...

Issue: mongoose.model is not a valid function

I've been diving into several MEAN tutorials, and I've hit a snag that none of them seem to address. I keep encountering this error message: Uncaught TypeError: mongoose.model is not a function Even after removing node_modules and reinstalling ...

Working with Thymeleaf and DatePicker to establish a minimum date

Looking to establish a minimum selectable date in my datepicker using Thymeleaf. Attempted code: <label th:utext="#{${name}}" th:for="${name}" class="control-label"></label> <div class="input-group input-group-prepend"> <span cla ...

Can you outline the distinctions between React Native and React?

Recently delving into the world of React sparked my curiosity, leading me to wonder about the distinctions between React and React Native. Despite scouring Google for answers, I came up short on finding a comprehensive explanation. Both React and React N ...

Is there a way to configure Cordova to utilize Yarn JS instead of NPM when installing plugins?

Updated Question: When adding plugins to my Cordova project, I currently use the command cordova plugin add x, which I believe utilizes npm in the background. Is there a way to switch out npm for Yarn within Cordova? This change would greatly impact cach ...

Do API applications require a session to function properly?

I am in the process of developing an API and I am unsure whether I should implement express-session or a similar tool to handle sessions. app.use(expressSession({ secret: 'Something' }); Moreover, I have been blocking CORS. Is this measure suf ...

How can you create HTML elements with a unique perspective?

To achieve a unique perspective when rendering HTML elements, we explored various methods. However, we found that CSS skew transformations did not produce the desired effect, often distorting the element's aspect ratio or creating unwelcome warping. ...

Struggling with transferring information up the React component hierarchy to manipulate the Document Object Model

** I'm still learning React, so any assistance is greatly appreciated! ** A Little Background I have a modal (bootstrap4) hidden within the main app component, containing a form. The form is automatically filled with information from the selected re ...

Tips on adjusting the label size of a radar chart in chart.js

My radar chart labels are appearing skewed and messed up on mobile devices, so I decided to scale them using the following code within ComponentDidMount(): const plugins = [{ beforeDraw: function(c) { var chartHeight = c.chart.height; c ...

The constructor window.CCapture is not valid in Node.js environment

Can anyone help me figure out how to load CCapture in Node.js without using a headless browser for recording canvas stream? Every time I try, I keep running into the error message saying "window.CCapture is not a constructor." If you are familiar with the ...

The makeSVG function from GoJS is failing to generate the correct HTML SVG object

I have been utilizing the Diagram.makeSVG() method to create an SVG from my diagram. Subsequently, I appended the SVG to a new empty tab using this script (diagram represents my Diagram Object): function print() { var newTab = window.open(), svg ...

Converting the AppDelegate.m file to AppDelegate.mm and updating the AppDelegate.h file during the transition from React Native 0.66.4 to 0.71.3

Original code in AppDelegate.h: #import <React/RCTBridgeDelegate.h> #import <UIKit/UIKit.h> #import <UserNotifications/UserNotifications.h> @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificat ...