4

I am trying to style my <code>/<pre> tags without preventing the user from being able to highlight and copy the code properly.

The way I was hoping to do this (see below) only works for the first line, and won't ever repeat.

I'm sure this could be made to work using JavaScript, but I'm not sure what the best way to do that without a large amount of processing would be.

body {
  font-family: sans-serif;
}

code, pre {
  background: #e5f3ff;
}

code.styled,
pre.styled {
  display: block;
  padding: 8px;
  margin: 8px 0;
  overflow-x: auto;
}

code.styled::before,
pre.styled::before {
  content: "–";
  padding-right: 8px;
}
<p>This is an example of using <code>::before</code> to add content,<br>
and still being able to highlight/copy text without copying prefix.</p>
<pre class="styled">
adb wait-for-device
adb reboot-bootloader
fastboot devices
</pre>
<p>Note: You can copy the code without having to worry about the prefix.</p>

What is the best way that I could achieve a similar effect, but spanning every line? I am looking for a vanilla JavaScript method if possible.

Jsilvermist
  • 491
  • 7
  • 16
  • While `user-select: none;` is very likely a part of a larger answer, it still doesn't solve programmatically prepending content to the beginning of every line. – Jsilvermist Apr 22 '16 at 21:25
  • Either wrap each line in its own `pre` or you need script – Asons Apr 22 '16 at 21:27
  • You can put each line into its own tag and then add a `::before` to each. Of course, that means you decide how to wrap lines, not browser, but that might be good or bad depending on what you are trying to do. – zvone Apr 22 '16 at 21:27
  • Which is exactly why I tagged this question with JavaScript, and made a comment to such in the question itself. What I'm looking for is a way to simply add a class or something simple, and be able to simply add the code without worry about styling, but have it styled automatically. – Jsilvermist Apr 22 '16 at 21:27

3 Answers3

3

If you don't want to wrap each line in it's own tag, you could try a background image technique similar to this answer:

styling each line inside pre with css

http://www.dte.web.id/2012/03/css-only-zebra-striped-pre-tag.html#.UUoV6lugkoM

UPDATE: added code sample

body {
  font-family: sans-serif;
}

code, pre {
  background: #e5f3ff;
}

code.styled,
pre.styled {
  display:block;
  font:normal 12px/22px Monaco,Monospace !important;
  color:#000;
  background-color:#e5f3ff;
  background-image: radial-gradient(circle at 50%, #333 0%, #333 10%, #e5f3ff 20%);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='22'><circle cx='11' cy='11' r='3' fill='green' stroke='black' stroke-weight='1' /></svg>");
  background-size: 22px 22px;
  background-repeat: repeat-y;
  padding:0em 20px;
  overflow:auto;
}
<pre class="styled">
adb wait-for-device
adb reboot-bootloader
fastboot devices
</pre>
<p>Note: You can copy the code without having to worry about the prefix.</p>
andyvanee
  • 958
  • 9
  • 21
  • Would it be possible to use e.g. `$` as symbol not the background-image cycle? – KargWare Oct 03 '21 at 20:34
  • @KargWare with this technique, it needs to be an image, since it's depending on the `background-repeat` CSS which only works for images. It certainly would be possible to use an SVG image instead if you want to use text or more complicated shapes. – andyvanee Oct 06 '21 at 16:01
3

Here is a pure javascript solution

var _pre = document.querySelector("pre.styled");
_pre.innerHTML="<span class='line'>"+(_pre.textContent.split("\n").filter(Boolean).join("</span>\n<span class='line'>"))+"</span>";
body {
  font-family: sans-serif;
}

code, pre {
  background: #e5f3ff;
}

code.styled,
pre.styled {
  display: block;
  padding: 8px;
  margin: 8px 0;
  overflow-x: auto;
}

code.styled .line::before,
pre.styled .line::before {
  content: "–";
  padding-right: 8px;
}
<p>This is an example of using <code>::before</code> to add content,<br>
and still being able to highlight/copy text without copying prefix.</p>
<pre class="styled">
adb wait-for-device
adb reboot-bootloader
fastboot devices
</pre>
<p>Note: You can copy the code without having to worry about the prefix.</p>

How this works: We retrieve the textContent from pre as string, split the string into an array of lines, filter out empty lines and join the lines back together by wrapping each in a span.

jayms
  • 3,828
  • 2
  • 11
  • 18
  • I love it! It's perfect! Would it be possible to explain how it works, as I am unfamiliar with the `filter()` method? – Jsilvermist Apr 22 '16 at 21:58
  • I added a human readable version at the bottom of my post. `filter(Boolean)` is a nice little trick to filter out false-like elements - in this case empty strings (`""`) It is optional, I just put it in there to tidy things up a bit. – jayms Apr 22 '16 at 22:12
1

How about just use a bit of javascript to wrap each line with spans and style them:

$('pre.newline').each( function() {
  var text = $(this).text().split('\n');
  $(this).html('')

  for(var i = 0; i <  text.length; i++) {
    $(this).append( $('<span>').html( text[i] ) );
  }

  $(this).html(html)
})

And style the spans display:block

pre.newline span::before {
  content: "–";
  padding-right: 8px;
}

pre.newline span {
  display: block;
}
j_ernst
  • 969
  • 7
  • 4
  • This seems great, but how would I go about doing this without JQuery, but only vanilla JavaScript? – Jsilvermist Apr 22 '16 at 21:36
  • Okay, look into native DOM methods then like [document.querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll). Use a for loop instead of the jQuery.each and use a native DOM appending method. Look for appendChild and innerHtml, for instance. – j_ernst Apr 22 '16 at 21:38