I'm building a simple blog following this --> to switch into this form -> and for the content above to be hidden when the editing form is open.
Please find below my code for the html.erb page I'm trying to get this to work on:
<style>
#element_content:hover {
background-color: #E7E7E7;
}
</style>
<div >
<div >
<%= link_to "Back to card", cards_path, class: 'btn btn-secondary mb-3' %>
<div >
<div ></div>
<h4 >Editing Card</h4>
<%= render "form", card: @card %>
</div>
</div>
<div >
<div >
<%= form_with(model: [@card, @paragraph],) do |form| %>
<%= form.hidden_field :element_type, value: 'paragraph' %>
<%= form.submit "New Element", class: 'btn btn-primary' %>
<% end %>
</div>
<div >
<script>
document.addEventListener('turbolinks:load', () => {
document.addEventListener('click', () => {
let element = event.target.closest('.paragraph-content')
if (!element) return;
element.classList.add('d-none')
element.nextElementSibling.classList.remove('d-none')
})
document.addEventListener('click', () => {
if (!event.target.matches('.cancel')) return;
let element = event.target.closest('.paragraph-form')
element.classList.add('d-none')
element.previousElementSibling.classList.remove('d-none')
})
})
</script>
<div >
<% @card.elements.each do |element| %>
<% if element.persisted? %>
<div >
<div >
<% if element.content.present? %>
<%= element.content %>
<% else %>
New Element will display here.
<% end %>
</div>
<div >
<%= form_with(model: [@card, element]) do |form| %>
<%= form.rich_text_area :content %>
<div >
<%= form.submit "Save", class: 'btn btn-primary' %>
<a href="#" >Cancel</a>
</div>
<% end %>
</div>
</div>
<% end %>
<% end %>
</div>
</div>
</div>
</div>
</div>
Below is the actual HTML generated (copied from Chrome's inspector mode)
<html><head><style type="text/css" data-tag-name="trix-editor">trix-editor {
display: block;
}
trix-editor:empty:not(:focus)::before {
content: attr(placeholder);
color: graytext;
cursor: text;
pointer-events: none;
}
trix-editor a[contenteditable=false] {
cursor: text;
}
trix-editor img {
max-width: 100%;
height: auto;
}
trix-editor [data-trix-attachment] figcaption textarea {
resize: none;
}
trix-editor [data-trix-attachment] figcaption textarea.trix-autoresize-clone {
position: absolute;
left: -9999px;
max-height: 0px;
}
trix-editor [data-trix-attachment] figcaption[data-trix-placeholder]:empty::before {
content: attr(data-trix-placeholder);
color: graytext;
}
trix-editor [data-trix-cursor-target] {
display: inline-block !important;
width: 1px !important;
padding: 0 !important;
margin: 0 !important;
border: none !important;
}
trix-editor [data-trix-cursor-target=left] {
vertical-align: top !important;
margin-left: -1px !important;
}
trix-editor [data-trix-cursor-target=right] {
vertical-align: bottom !important;
margin-right: -1px !important;
}</style><style type="text/css" data-tag-name="trix-toolbar">trix-toolbar {
display: block;
}
trix-toolbar {
white-space: nowrap;
}
trix-toolbar [data-trix-dialog] {
display: none;
}
trix-toolbar [data-trix-dialog][data-trix-active] {
display: block;
}
trix-toolbar [data-trix-dialog] [data-trix-validate]:invalid {
background-color: #ffdddd;
}</style>
<title>Knowledgebase</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="oRMykwAKSg6xWWo3LJHmsB1pkPlfYvP4PZN72gBJK2qH4gb2MkylWPcxc6KOni95V8bIxqhAR0dqVgiB2idfEw">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
<link rel="stylesheet" href="/assets/application-904c933b80333e5306f46a2a4a560084b9579e45ffa2564bd024d45caa6b79c5.css" data-turbo-track="reload">
<script type="importmap" data-turbo-track="reload">{
"imports": {
"application": "/assets/application-53c449d104ca7e92fd7c92a99d5a225fddc3a41a930a27c9621c56294fa3f06f.js",
"@hotwired/turbo-rails": "/assets/turbo.min-e5023178542f05fc063cd1dc5865457259cc01f3fba76a28454060d33de6f429.js",
"@hotwired/stimulus": "/assets/stimulus.min-b8a9738499c7a8362910cd545375417370d72a9776fb4e766df7671484e2beb7.js",
"@hotwired/stimulus-loading": "/assets/stimulus-loading-1fc59770fb1654500044afd3f5f6d7d00800e5be36746d55b94a2963a7a228aa.js",
"trix": "/assets/trix-1563ff9c10f74e143b3ded40a8458497eaf2f87a648a5cbbfebdb7dec3447a5e.js",
"@rails/actiontext": "/assets/actiontext-28c61f5197c204db043317a8f8826a87ab31495b741f854d307ca36122deefce.js",
"controllers/application": "/assets/controllers/application-368d98631bccbf2349e0d4f8269afb3fe9625118341966de054759d96ea86c7e.js",
"controllers/hello_controller": "/assets/controllers/hello_controller-549135e8e7c683a538c3d6d517339ba470fcfb79d62f738a0a089ba41851a554.js",
"controllers": "/assets/controllers/index-1569d47a5473ce34c056f906b4dbc6541274c7f5a61e00a1c1978b90009761e0.js"
}
}</script>
<link rel="modulepreload" href="/assets/application-53c449d104ca7e92fd7c92a99d5a225fddc3a41a930a27c9621c56294fa3f06f.js">
<link rel="modulepreload" href="/assets/turbo.min-e5023178542f05fc063cd1dc5865457259cc01f3fba76a28454060d33de6f429.js">
<link rel="modulepreload" href="/assets/stimulus.min-b8a9738499c7a8362910cd545375417370d72a9776fb4e766df7671484e2beb7.js">
<link rel="modulepreload" href="/assets/stimulus-loading-1fc59770fb1654500044afd3f5f6d7d00800e5be36746d55b94a2963a7a228aa.js">
<script src="/assets/es-module-shims.min-d89e73202ec09dede55fb74115af9c5f9f2bb965433de1c2446e1faa6dac2470.js" async="async" data-turbo-track="reload"></script>
<script type="module">import "application"</script>
</head>
<body >
<div >
<p ></p>
<p ></p>
<style>
#element_content:hover {
background-color: #E7E7E7;
}
</style>
<div >
<div >
<a href="/cards">Back to card</a>
<div >
<div ></div>
<h4 >Editing Card</h4>
<form action="/cards/1" accept-charset="UTF-8" method="post"><input type="hidden" name="_method" value="patch" autocomplete="off"><input type="hidden" name="authenticity_token" value="1WnwBmNUcDWLZHN-9SWYtAJlnShGLjDOjLDCy4nHl43bbUvicGTNDQDSCpT0UTgahWR_p8RGY8fQtHro3i3NPw" autocomplete="off">
<div >
<label for="card_title">Title</label>
<input type="text" value="Card Test" name="card[title]" id="card_title">
</div>
<div >
<label for="card_description">Description</label>
<textarea name="card[description]" id="card_description">Testing this card</textarea>
</div>
<div >
<input type="submit" name="commit" value="Update Card" data-disable-with="Update Card">
</div>
</form>
</div>
</div>
<div >
<div >
<form action="/cards/1/elements" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="TmrgVfIbb23NOtQWVNw_SvsV7DuPwJCMBpnRMfAo4vRUHSjIeHRSHPHpZJIluTAvbwImLcihRGIi3ewb7xMGeQ" autocomplete="off">
<input value="paragraph" autocomplete="off" type="hidden" name="element[element_type]" id="element_element_type">
<input type="submit" name="commit" value="New Element" data-disable-with="New Element">
</form> </div>
<div >
<script>
document.addEventListener('turbolinks:load', () => {
document.addEventListener('click', () => {
let element = event.target.closest('.paragraph-content')
if (!element) return;
element.classList.add('d-none')
element.nextElementSibling.classList.remove('d-none')
})
document.addEventListener('click', () => {
if (!event.target.matches('.cancel')) return;
let element = event.target.closest('.paragraph-form')
element.classList.add('d-none')
element.previousElementSibling.classList.remove('d-none')
})
})
</script>
<div >
<div >
<div >
New Element will display here.
</div>
<div >
<form action="/cards/1/elements/1" accept-charset="UTF-8" method="post"><input type="hidden" name="_method" value="patch" autocomplete="off"><input type="hidden" name="authenticity_token" value="bGTr4lAuCbfiblGvUmfGioQC9wwckPXUFYuEHr4PgvZh_oTaoKMqIrZOT6vqZ1CFKtD16pyJ7ANvBGnOvA7LDg" autocomplete="off">
<input type="hidden" name="element[content]" id="element_content_trix_input_element_1" autocomplete="off"><trix-toolbar id="trix-toolbar-1"><div >
<span data-trix-button-group="text-tools">
<button type="button" data-trix-attribute="bold" data-trix-key="b" title="Bold" tabindex="-1">Bold</button>
<button type="button" data-trix-attribute="italic" data-trix-key="i" title="Italic" tabindex="-1">Italic</button>
<button type="button" data-trix-attribute="strike" title="Strikethrough" tabindex="-1">Strikethrough</button>
<button type="button" data-trix-attribute="href" data-trix-action="link" data-trix-key="k" title="Link" tabindex="-1">Link</button>
</span>
<span data-trix-button-group="block-tools">
<button type="button" data-trix-attribute="heading1" title="Heading" tabindex="-1">Heading</button>
<button type="button" data-trix-attribute="quote" title="Quote" tabindex="-1">Quote</button>
<button type="button" data-trix-attribute="code" title="Code" tabindex="-1">Code</button>
<button type="button" data-trix-attribute="bullet" title="Bullets" tabindex="-1">Bullets</button>
<button type="button" data-trix-attribute="number" title="Numbers" tabindex="-1">Numbers</button>
<button type="button" data-trix-action="decreaseNestingLevel" title="Decrease Level" tabindex="-1">Decrease Level</button>
<button type="button" data-trix-action="increaseNestingLevel" title="Increase Level" tabindex="-1">Increase Level</button>
</span>
<span data-trix-button-group="file-tools">
<button type="button" data-trix-action="attachFiles" title="Attach Files" tabindex="-1">Attach Files</button>
</span>
<span ></span>
<span data-trix-button-group="history-tools">
<button type="button" data-trix-action="undo" data-trix-key="z" title="Undo" tabindex="-1">Undo</button>
<button type="button" data-trix-action="redo" data-trix-key="shift z" title="Redo" tabindex="-1">Redo</button>
</span>
</div>
<div data-trix-dialogs="">
<div data-trix-dialog="href" data-trix-dialog-attribute="href">
<div >
<input type="url" name="href" placeholder="Enter a URL…" aria-label="URL" required="" data-trix-input="" disabled="disabled">
<div >
<input type="button" value="Link" data-trix-method="setAttribute">
<input type="button" value="Unlink" data-trix-method="removeAttribute">
</div>
</div>
</div>
</div></trix-toolbar><trix-editor id="element_content" input="element_content_trix_input_element_1" data-direct-upload-url="http://localhost:3000/rails/active_storage/direct_uploads" data-blob-url-template="http://localhost:3000/rails/active_storage/blobs/redirect/:signed_id/:filename" contenteditable="" role="textbox" trix-id="1" toolbar="trix-toolbar-1"></trix-editor>
<div >
<input type="submit" name="commit" value="Save" data-disable-with="Save">
<a href="#" >Cancel</a>
</div>
</form> </div>
</div>
</div>
</div>
</div>
</div>
</div>
</body></html>
I would appreciate any help.
CodePudding user response:
1. Don't nest your script tag inside a body element
I would move your <script>
tag into the <head>
tag or to the bottom of the page, nesting it inside a div is a bit strange.
2. Is the JS even loading?
Try adding an alert or two:
<script>
alert("I loaded!");
document.addEventListener('turbolinks:load', () => {
...
})
alert("I finished!");
</script>
3. In Javascript, semicolons matter.
Every line should end with a semicolon:
document.addEventListener('turbolinks:load', () => {
document.addEventListener('click', () => {
let element = event.target.closest('.paragraph-content')
if (!element) return;
element.classList.add('d-none');
element.nextElementSibling.classList.remove('d-none');
})
document.addEventListener('click', () => {
if (!event.target.matches('.cancel')) return;
let element = event.target.closest('.paragraph-form');
element.classList.add('d-none');
element.previousElementSibling.classList.remove('d-none');
})
})
4. Make your function triggers more specific.
You are adding an event listener for every click anywhere in the document, which is overkill. I would try being more specific:
document.addEventListener('turbolinks:load', () => {
document.getElementsByClassName('paragraph').addEventListener('click', () => {
let element = event.target.closest('.paragraph-content')
if (!element) return;
element.classList.add('d-none');
element.nextElementSibling.classList.remove('d-none');
})
document.getElementsByClassName('cancel').addEventListener('click', () => {
if (!event.target.matches('.cancel')) return;
let element = event.target.closest('.paragraph-form');
element.classList.add('d-none');
element.previousElementSibling.classList.remove('d-none');
})
})
5. Focus on what should be happening:
In your first click listener, you are saying:
- Find the closest element with the class 'paragraph-content'
- If nothing is found, return
- Add the class 'd-none' to the element
- Remove the class 'd-none' from the next sibling element
So, what should adding 'd-none' to the class do? I'm not seeing any any CSS or JS that would change anything in your HTML based upon an element having the 'd-none' class.
I'm guessing you instead want to hide or show the 'content' or the 'form'. You have 2 options to do this:
- Use CSS to hide everything with the 'd-none' class
.d-none {
display: none;
}
- Make your Javascript more explicit on what you want to happen:
document.addEventListener('turbolinks:load', () => {
document.addEventListener('click', () => {
let element = event.target.closest('.paragraph-content');
if (!element) return;
// element.classList.add('d-none');
element.style.display = 'none';
// element.nextElementSibling.classList.remove('d-none');
element.nextElementSibling.display = 'inline';
})
document.addEventListener('click', () => {
if (!event.target.matches('.cancel')) return;
let element = event.target.closest('.paragraph-form');
// element.classList.add('d-none')
element.style.display = 'none';
// element.previousElementSibling.classList.remove('d-none');
element.previousElementSibling.display = 'inline';
})
})
Hopefully this gives you some ideas to play with.