I have a form that has multiple image components inside it that are outputted onto the page with a while
loop. When the form is submitted after the image details have been added they are uploaded to a MySQL database via PHP (along with some usage of the JavaScript fetch API to prevent a hard page refresh).
Because of the design of the form I've used multiple image components inside one form instead of having multiple forms: this is so the user can upload multiple images and can do the final submission all in one go (a far better user experience).
The Problem
Because the components also each have an individual delete button so that the individual images can be deleted prior to the final set of images being uploaded, this currently has an unwanted side effect: when a user hits the 'enter/return' key on a keyboard, the browser goes to the nearest submit button in the form, which is the delete button for that particular component.
What I want to do is have it so if the 'Image Title' input field is in focus, and the user presses the 'return' key, it defaults to the main image submission button at the very bottom of the form with the class .upload-details-submit
. It's not an issue with the <textarea>
element because hitting return when that is in focus just takes you down a line inside the <textarea>
field and obviously if the 'delete' button has focus then hitting return will carry out the desired action of deleting the image component.
My Question
The question I have is essentially two-fold:
a) Is there away to fix this with the HTML tabindex
attribute?
b) If not, and I have to use javascript, what is the best way to approach this? I understand you can use the .hasFocus()
property on an element to check if it has focus, but how would I go about triggering certain events when the user has given the input element (the Image Title input) focus? Or more specifically when they have filled in the 'Image Title' field and hit the return key, it ignores the nearest 'Delete' button and fires the main .upload-details-submit
button ?
I have included my JavaScript as well, but I can't seem to get this to work.
HTML
<form method="post" enctype="multipart/form-data">
<!-- IMAGE DETAILS COMPONENT. THESE COMPONENTS ARE OUTPUTTED WITH A WHILE LOOP-->
<div >
<div >
<img src="image.jpg">
</div>
<div >
<div >
<label for="image-title">Image Title</label>
<input id="title-title>" type="text" name="image-title[]">
</div>
<div >
<label for="tags">Comma Separated Image Tags</label>
<textarea id="tags" type="text" name="image-tags[]"></textarea>
</div>
<div >
<!-- DELETE BUTTON -->
<button name="upload-details-delete" value="12" >DELETE</button>
<input type="hidden" name="image-id[]" value="12">
</div>
</div>
</div>
<!-- IMAGE DETAILS COMPONENT - END -->
<div >
<button id="upload-submit" type="submit" name="upload-submit">COMPLETE UPLOADS</button>
</div>
</form>
JAVASCRIPT
Note: I'm using a forEach
loop because although there is only one component in the HTML example above, there will invariably be more than one outputted via the while
loop on the live site.
let inputTitle = document.querySelectorAll('.input-title')
inputTitle.forEach((title) => {
if (title.hasFocus()) {
title.addEventListener('keyup', (e) => {
if (e.keycode === 13) {
e.target.closest('form').querySelector('.upload-details-submit').click()
}
})
}
})
CodePudding user response:
You can solve this by using the focus event on document with capture and combining the focus state with the keydown state.
const title = document.querySelector('#title-title')
let isInTitle = false;
document.addEventListener('focus', e => {
if (document.activeElement === title) {
isInTitle = true
return;
}
isInTitle = false
}, true);
document.addEventListener('keydown', e => {
var { key } = e
if (isInTitle && (key === "Enter" || key === "Tab")) {
e.stopImmediatePropagation();
e.preventDefault()
}
})
<form method="post" enctype="multipart/form-data">
<!-- IMAGE DETAILS COMPONENT. THESE COMPONENTS ARE OUTPUTTED WITH A WHILE LOOP-->
<div >
<div >
<img src="image.jpg">
</div>
<div >
<div >
<label for="image-title">Image Title</label>
<input id="title-title" type="text" name="image-title[]">
</div>
<div >
<label for="tags">Comma Separated Image Tags</label>
<textarea id="tags" type="text" name="image-tags[]"></textarea>
</div>
<div >
<!-- DELETE BUTTON -->
<button name="upload-details-delete" value="12" >DELETE</button>
<input type="hidden" name="image-id[]" value="12">
</div>
</div>
</div>
<!-- IMAGE DETAILS COMPONENT - END -->
<div >
<button id="upload-submit" type="submit" name="upload-submit">COMPLETE UPLOADS</button>
</div>
</form>
CodePudding user response:
I've simplified the HTML for readability (and added a second "component")
Add a keypress
handler to document
, then check which element is the target
on the keypress
- then it's really simple
document.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && e.target.classList.contains('input-title')) {
e.preventDefault();
e.target.closest('form').querySelector('.upload-details-submit').click()
}
})
/// just to prevent actual form submission for the answer - you wouldn't do this
document.querySelectorAll('form').forEach(form => form.addEventListener('submit', e => {
console.log('form would submit', e.submitter.id)
e.preventDefault();
}))
<form>
<div >
<label for="image-title">Image Title</label>
<input id="title-title" type="text" name="image-title[]">
<label for="tags">Comma Separated Image Tags</label>
<textarea id="tags" type="text" name="image-tags[]"></textarea>
<!-- DELETE BUTTON -->
<button id='r1' name="upload-details-delete" value="12" >DELETE</button>
<input type="hidden" name="image-id[]" value="12">
</div>
<div >
<label for="image-title2">Image Title 2</label>
<input id="title-title2" type="text" name="image-title[]">
<label for="tags">Comma Separated Image Tags</label>
<textarea id="tags2" type="text" name="image-tags[]"></textarea>
<!-- DELETE BUTTON -->
<button id='r2' name="upload-details-delete" value="13" >DELETE</button>
<input type="hidden" name="image-id[]" value="13">
</div>
<button id="upload-submit" type="submit" name="upload-submit">COMPLETE UPLOADS</button>
</form>