GoRails Markdown and Preview
Hey Chris,
Just wondering what are you using to preview Markdown in forum posts? I watched the whole Forum series and I saw the other video about html-pipeline gem but you don't mention syntax highlight nor Markdown preview.
Thanks in advance.
I don't know which gem is GoRails using but I can recommend you a very good gem for that, it's called RedCarpet, it's what powers Github's markdown rendering, maybe that's the one... Check it out here.
I'm using RedCarpet + Pygments for the final rendering with syntax highlighting and I'm using the marked
Javascript library to render the previews when the text box content changes.
# Markdown previews to comments
class Comment
constructor: (element) ->
@element = $(element)
@commentField = @element.find("[data-behavior='comment-body']")
@previewArea = @element.find("[data-behavior='comment-preview']")
@setEvents()
setEvents: ->
@commentField.on "change", @handlePreview
handlePreview: =>
html = marked @commentField.val()
@previewArea.html html
jQuery ->
$.each $("[data-behavior='comment-form']"), (i, element)->
new Comment(element)
I don't mean to bring up a sort of old post, but wanted to post this here in case someone else was running into similar issues.
I was working on a form where I wanted a live preview of the parsed markdown on the same page and I like to see things in real time so I wanted to update the preview any time a key is pressed or I unfocus on the text field.. here's my coffeescript that I added to from what Chris posted.
Oh and I for whatever reason could not get @previewArea.html html to actually update the preview area so I changed it to select the ID of the div which I had set to preview.
class PostPreview
constructor: (element) ->
@element = $(element)
@commentField = @element.find("[data-behavior~='post-content']")
@previewArea = @element.find("[data-behavior='post-preview']")
@setEvents()
setEvents: ->
@commentField.on "focusout", @handlePreview
@commentField.on "change", @handlePreview
@commentField.on "keyup", @handlePreview
handlePreview: =>
html = marked @commentField.val()
document.getElementById('preview').innerHTML = html;
jQuery ->
$.each $("[data-behavior='post-form']"), (i, element)->
new PostPreview(element)
All in all basically the same as Chris. If there's a better way to do this or reasons I shouldn't I'd love to hear em.
In Turbolinks 5, the equivalent method of jQuery ->
is
document.addEventListener("turbolinks:load", function() { ... }
However, from the docs:
When possible, avoid using the turbolinks:load event to add event listeners directly to elements on the page body. Instead, consider using event delegation to register event listeners once on document or window.
What this means is that events on a page bubble up the document tree. You can catch an event for a <li>
on it's parent <ul>
, and further than that -- all the way up to the document
. So you could actually rewrite this Comment
coffeescript class as:
$(document).on "change", "[data-behavior='post-form'] [data-behavior='post-body']", (event, object) ->
html = marked $("[data-behavior='post-form'] [data-behavior='post-body']").val()
$("[data-behavior='post-form'] [data-behavior='post-preview']").html html
However, I think this has reduced levels of developer clarity compared to the Comment
implementation.
I think you could abstract out the selectors and make defining those communicate the intent clearly, as well as potentially let you drop in a bunch of different setups of selectors if you want the preview functionality in more places than one.
class Preview
constructor: (element, inputField, previewField) ->
@element = element
@inputField = inputField
@previewField = previewField
inputSelector: =>
@element + " " + @inputField
previewSelector: =>
@element + " " + @previewField
previews = [
new Preview("[data-behavior='post-form']", "[data-behavior='post-body']", "[data-behavior='post-preview']")
]
for preview in previews
do ->
$(document).on "change", $(preview.inputSelector()), (event, object) ->
html = marked $(preview.inputSelector()).val()
$(preview.previewSelector()).html html
This would let you reuse "Previews" all over the place in a way thats turbolinks 5-friendly, and all you have to do is add in another Preview object with new selectors into the previews
array. Total overkill right here, but interesting to think through.