# Convert Single Column Text Input to CSS Grid Box

**URL:** <https://forums.ankiweb.net/t/convert-single-column-text-input-to-css-grid-box/5078>\
**Category:** Card Design\
**Created:** [November 13, 2020, 2:05pm UTC](https://forums.ankiweb.net/t/convert-single-column-text-input-to-css-grid-box/5078 "2020-11-13T14:05:40Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![werner](https://avatars.discourse-cdn.com/v4/letter/w/f4b2a3/32.png) [@werner](https://forums.ankiweb.net/u/werner)\
**Post date:** [November 13, 2020, 2:05pm UTC](https://forums.ankiweb.net/t/convert-single-column-text-input-to-css-grid-box/5078/1 "2020-11-13T14:05:40Z")

</div>

Hi Anki community!

The issue I faced was that I had multiple fields for a single purpose. Creating the individual notes was kind of cumbersome and it was easy to lose overview on each individual note.  
My project was to consolidate these multiple fields and append their values to the card using javascript. In addition to that, I wanted a code that could handle more pieces of information (entities) in a dynamic manner. The output should be presented in a way that adjusts dynamically and has a nice structured panel look to it.

Narrow view:

 ![narrow view](https://us1.discourse-cdn.com/flex002/uploads/anki2/original/2X/0/0fd80301952ba4ed093cebf73977b6a0e7225940.png)

Broad view:

 ![broad view](https://us1.discourse-cdn.com/flex002/uploads/anki2/original/2X/0/08a3aa8892b0249dcf0ede43da6d1534b0023391.png)

In my case I have 3 different fields for the example sentence + 1 field for audio:  
1x sentence in target language  
1x phonetic transcription of the sentence  
1x translation of the sentence.  
1x audio

The javascript allows me to have multiple example sentences. Before that I was restricted to only one example sentence.  
I chose a single column structure for the input value that uses double colons “::” as a delimiter for the headline. If the input should have nested objects then slash “/” is used for this purpose.  
You can define your key values in the first line, separating the keys using double colons “::” as delimiter:

**ids::target::phonetic::native::audio**  
(As an object with nested value, it could look like this:  
ids::sentence/target::sentence/phonetic::sentence/native::audio )

The header defines the structure of your input. In this case I have 5 rows per entity.:  
(1) ids, (2) target, (3) phonetic, (4) native, (5) audio  
Newline (’\n’, break) is used as delimiter within the entity.  
Numbers followed by period (1., 2., 3., aso.) are used as delimiter between the separate entities.  
The complete input value looks like this in my case:

'ids::target::phonetic::native::audio

1. example  
FIRST Target SNTNC  
FIRST Phonetic SNTNC  
FIRST Native SNTNC  
first audio file

2. example  
SECOND Target SNTNC  
SECOND Phonetic SNTNC  
SECOND Native SNTNC

3. example  
THIRD Target SNTNC  
THIRD Phonetic SNTNC  
THIRD Native SNTNC  
third audio file’

The issues that haven’t been solved yet:  
I have not manged yet to include the audio files dynamically as Anki responds with error messages as soon as I try to create a code like: ‘[sound:’ + list[i][Object.keys(list[0])[4]] + ‘]’  
I also haven’t figured out how to hide empty ‘undefined’ audio values.  
Initially, I planned to have the code detect dynamically if there are nested objects and act accordingly but I can’t figure out how to make it work.  
Instead of showing the audio symbol on each panel, I would prefer to have a different background color of the panel indicating that this example sentence provides audio on click or on hover.  
I’m still researching on the topic of dynamic identifiers in HTML and CSS to make it easier to adjust the individual panels.

The solution so far:  
The field name in my case is: {{example}}

**HTML code** (on front or back as needed):  
`<div class="wrapper" id="EXAMPLE"></div>`

**CSS code**  
You have to provide the CSS code for the snippets of information on each panel.  
The key names defined in the header are the class names in CSS.

```
<style>
.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  grid-auto-rows: minmax(120px, auto);
  justify-content: center;
  grid-column-gap: 6px;
  grid-row-gap: 6px;
  padding: 2px 0;
  background-color: floralwhite;
}

.panel {
  background-color: snow;
	border: 2px solid teal;
  padding: 1rem;

}

.target {
color: green;
}

.phonetic {
color: Crimson;
}

.native {
color: SlateBlue;
}
</style>

```

**Javascript code**  
I have very basic knowledge of javascript, so this is the best I could come up with:

```
<script src='_jquery.js'></script>

<script>

// convert an HTML fragment to plain text
    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = '{{example}}';
    document.documentElement.appendChild(tempDiv);
    var singleColumnCSV = tempDiv.innerText;
    document.documentElement.removeChild(tempDiv);

var pipeSeparatedValues = singleColumnCSV.replace(/\s\n|\n/g, "|");
var removePipesTwoOrMore = pipeSeparatedValues.replace(/(\|)\1+/g, "|");
var removeSuperSpace = removePipesTwoOrMore.replace(/(?<=\|)\s(?=\d\.|$)/g,"");
var createNewArray = removeSuperSpace.match(/(^[^|]+)|(\b\d+\.+(.+?)(?=\s*\|\b\d+\.|\s*$))/g );

//source: 
//https://stackoverflow.com/questions/44178371/converting-csv-to-nested-json-in-javascript/44179597

// PSV = "Pipe Separated Values"
var PSV = createNewArray

var attrs = PSV.splice(0,1);

var result = PSV.map(function(row) {
  var obj = {};
  // use PIPE '|' as delimiter
  var rowData = row.split('|');
  // use 2 colons '::' as delimiter
  attrs[0].split('::').forEach(function(val, idx) {
    obj = constructObj(val, obj, rowData[idx]);
  });
  return obj;
})

function constructObj(str, parentObj, data) {
  if(str.split('/').length === 1) {
    parentObj[str] = data;
    return parentObj;
  }

  var curKey = str.split('/')[0];
  if(!parentObj[curKey])
    parentObj[curKey] = {};
  parentObj[curKey] = constructObj(str.split('/').slice(1).join('/'), 
                                   parentObj[curKey], data);
  return parentObj;
}

var list = result
var html = '';

// Get id and print
for (var i = 0, l = list.length; i < l; i++) {
	$('#EXAMPLE')
			.append('<div class="panel">'
+'<div>' 
			+	list[i][Object.keys(list[0])[0]] + '</div>'
+'<div class="'+ [Object.keys(list[0])[1]] + '">'
			+ list[i][Object.keys(list[0])[1]] + '</div>'
+'<div class="'+ [Object.keys(list[0])[2]] + '">'
			+ list[i][Object.keys(list[0])[2]] + '</div>'
+'<div class="'+ [Object.keys(list[0])[3]] + '">'
			+ list[i][Object.keys(list[0])[3]] + '</div>'

// audio file:
+'<div class="'+ [Object.keys(list[0])[4]] + '">'
			+ list[i][Object.keys(list[0])[4]] 
			+ '</div>'

			+'</div>');   

}

</script>
```

---

<div class="post-metadata">

**Author:** ![werner](https://avatars.discourse-cdn.com/v4/letter/w/f4b2a3/32.png) [@werner](https://forums.ankiweb.net/u/werner)\
**Post date:** [November 13, 2020, 5:43pm UTC](https://forums.ankiweb.net/t/convert-single-column-text-input-to-css-grid-box/5078/2 "2020-11-13T17:43:38Z")

</div>

I’ve also posted this code on the reddit Anki community. It might be worth checking if someone came up with amendments.

> **[r/Anki - Convert Single Column Text Input to CSS Grid Box](https://www.reddit.com/r/Anki/comments/jtjkvv/convert_single_column_text_input_to_css_grid_box/)**
>
> 0 votes and 0 comments so far on Reddit

---

<div class="post-metadata">

**Author:** ![werner](https://avatars.discourse-cdn.com/v4/letter/w/f4b2a3/32.png) [@werner](https://forums.ankiweb.net/u/werner)\
**Post date:** [November 15, 2020, 7:48pm UTC](https://forums.ankiweb.net/t/convert-single-column-text-input-to-css-grid-box/5078/3 "2020-11-15T19:48:55Z")

</div>

**Update:**

Solved issues:

- Including the audio files dynamically
- Hiding empty ‘undefined’ audio values
- Border and background color indicate if this example sentence provides audio.

Input value could look like this:

'ids::target::phonetic::native::audio

1. example  
FIRST Target SNTNC  
FIRST Phonetic SNTNC  
FIRST Native SNTNC  
audio001.mp3

2. example  
SECOND Target SNTNC  
SECOND Phonetic SNTNC  
audio002.mp3

3. example  
THIRD Target SNTNC  
THIRD Phonetic SNTNC  
THIRD Native SNTNC  
audio003.mp3’

In case you want to transfer your deck to AnkiMobile make sure to include a field in the note where you add your sound files separately. Otherwise your media will not be transferred to your mobile device.

**Narrow view:**

 ![narrow view](https://us1.discourse-cdn.com/flex002/uploads/anki2/original/2X/f/fbdeab871a0aba1f3b21ea9ba0789d3d1c5ae830.png)

**Broad view:**

 ![broad view](https://us1.discourse-cdn.com/flex002/uploads/anki2/original/2X/5/5b14a3b142306e76b0e5d6b6a36be341509dabac.png)

**CSS Code**

```
    .wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  grid-auto-rows: minmax(140px, auto);
  justify-content: center;
  grid-column-gap: 6px;
  grid-row-gap: 6px;
  padding: 2px 0;
  background-color: floralwhite;
}

.panel {
  background-color: SeaShell;
	border: 2px solid LightSteelBlue;
  padding: 1rem;

}

.panelwithaudio {
  background-color: Snow;
	border: 2px solid SteelBlue;
  padding: 1rem;
}

.target {
color: green;
}

.phonetic {
color: Crimson;
}

.native {
color: SlateBlue;
}

.audio {
		margin: 0;
		display: inline-block;
		margin: 1.25rem auto auto ;
}

```

**HTML Code**

```
<div class="wrapper" id="EXAMPLE">
</div>

```

**Javascript**

```
<script src='_jquery.js'></script>

<script>

// convert an HTML fragment to plain text
    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = '{{example}}';
    document.documentElement.appendChild(tempDiv);
    var singleColumnCSV = tempDiv.innerText;
    document.documentElement.removeChild(tempDiv);

var pipeSeparatedValues = singleColumnCSV.replace(/\s\n|\n/g, "|");
var removePipesTwoOrMore = pipeSeparatedValues.replace(/(\|)\1+/g, "|");
var removeSuperSpace = removePipesTwoOrMore.replace(/(?<=\|)\s(?=\d\.|$)/g,"");
var createNewArray = removeSuperSpace.match(/(^[^|]+)|(\b\d+\.+(.+?)(?=\s*\|\b\d+\.|\s*$))/g );

//source: 
//https://stackoverflow.com/questions/44178371/converting-csv-to-nested-json-in-javascript/44179597

// PSV = "Pipe Separated Values"
var PSV = createNewArray

var attrs = PSV.splice(0,1);

var result = PSV.map(function(row) {
  var obj = {};
  // use PIPE '|' as delimiter
  var rowData = row.split('|');
  // use 2 colons '::' as delimiter
  attrs[0].split('::').forEach(function(val, idx) {
    obj = constructObj(val, obj, rowData[idx]);
  });
  return obj;
})

function constructObj(str, parentObj, data) {
  if(str.split('/').length === 1) {
    parentObj[str] = data;
    return parentObj;
  }

  var curKey = str.split('/')[0];
  if(!parentObj[curKey])
    parentObj[curKey] = {};
  parentObj[curKey] = constructObj(str.split('/').slice(1).join('/'), 
                                   parentObj[curKey], data);
  return parentObj;
}

var list = result

// Get id and print
for (var i = 0, l = list.length; i < l; i++) {
	$('#EXAMPLE')
			.append(
(list[i][Object.keys(list[0])[4]] != undefined ? '<div class="panelwithaudio">' : '<div class="panel">')
+'<div>' 
			+	list[i][Object.keys(list[0])[0]] + '</div>'
+'<div class="'+ [Object.keys(list[0])[1]] + '">'
			+ list[i][Object.keys(list[0])[1]] + '</div>'
+'<div class="'+ [Object.keys(list[0])[2]] + '">'
			+ list[i][Object.keys(list[0])[2]] + '</div>'
+'<div class="'+ [Object.keys(list[0])[3]] + '">'
			+ list[i][Object.keys(list[0])[3]] + '</div>'
// audio file:
		+ (list[i][Object.keys(list[0])[4]] != undefined ? 
			'<div class="'+ [Object.keys(list[0])[4]] + '">' 
			+ '<audio controls><source src="' + list[i][Object.keys(list[0])[4]] + '" type="audio/mp3"/></audio></div>' : '')

// end of panel:
			+'</div>');   
}

</script>
```

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex002/uploads/anki2/original/1X/8f1279ababc5879d54e4838989f606cfe55af8c7.jpeg) [@system](https://forums.ankiweb.net/u/system)\
**Post date:** [May 1, 2023, 7:43am UTC](https://forums.ankiweb.net/t/convert-single-column-text-input-to-css-grid-box/5078/4 "2023-05-01T07:43:04Z")

</div>


