Skip to content

Instantly share code, notes, and snippets.

@bradgessler
Last active February 7, 2024 19:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bradgessler/dbf19c266e503489993582692beb36d3 to your computer and use it in GitHub Desktop.
Save bradgessler/dbf19c266e503489993582692beb36d3 to your computer and use it in GitHub Desktop.
Bi-directional HTML
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "sinatra", "~> 3.1"
gem "nokogiri", "~> 1.15"
gem "puma"
end
require "sinatra"
get "/" do
erb :app
end
post "/" do
dom = Nokogiri::HTML(request.body.read)
todo_input = dom.at("#todo_input")
# Find the ul (or ol) that contains the li elements
list = dom.at_css('ul') # or dom.at_css('ol') if it's an ordered list
# Create a new li element with the content set to the current time
new_item = Nokogiri::XML::Node.new('li', dom)
if todo_input.content.empty?
todo_input.content = "This can't be empty dude!"
else
new_item.content = todo_input.content
todo_input.content = ""
# Append the new item to the list
list.prepend_child(new_item)
end
dom.to_html
end
get "/form" do
erb :form
end
post "/form" do
dom = Nokogiri::HTML(request.body.read)
dom.at_css("h1").content = "Updated at #{Time.now}"
dom.css("[contenteditable]").each do |element|
puts element.content
end
dom.to_html
end
Sinatra::Application.run! inline_templates: true
__END__
@@form
<style>
.label { font-weight: bold; }
html { font-size: 125%; }
</style>
<h1>FORM</h1>
<div>
<div class="label">Name</div>
<div contenteditable>Your Name</div>
</div>
<div>
<div class="label">Email</div>
<div contenteditable>you@email.com</div>
</div>
<span class="button" href>Submit</span>
<span href="/">Home</span>
@@ app
<h1 id="todo_input" contenteditable autofocus></h1>
<div href>Add Item</div>
<ul reversed>
<li>First Item</li>
</ul>
@@ layout
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>The App</title>
<style>
[href] {
user-select: none;
cursor: pointer;
text-decoration: underline;
color: blue;
}
[contenteditable] {
border: 1px dotted blue;
}
.button {
display: inline-block;
border-radius: 0.5rem;
padding: 0.25rem;
border: 1px solid #ccc;
background: #eee;
}
</style>
</head>
<body>
<%= yield %>
<script>
function setupEventListeners() {
document.body.addEventListener('click', function(event) {
if (event.target.matches('[href]')) {
event.preventDefault();
const htmlContent = document.documentElement.outerHTML;
const postUrl = event.target.getAttribute('href') || document.location.href;
fetch(postUrl, {
method: 'POST',
headers: {
'Content-Type': 'text/html'
},
body: htmlContent
})
.then(response => response.text())
.then(newHtml => {
document.documentElement.innerHTML = newHtml;
setupEventListeners(); // Re-setup the event listeners after the new content is loaded
})
.catch(error => console.error('Error:', error));
}
});
}
// Initialize event listeners on page load
setupEventListeners();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment