Skip to content

Instantly share code, notes, and snippets.

@juanbrujo
Last active December 29, 2024 19:23
Show Gist options
  • Save juanbrujo/25f7fd343182012466acc9f32383ff89 to your computer and use it in GitHub Desktop.
Save juanbrujo/25f7fd343182012466acc9f32383ff89 to your computer and use it in GitHub Desktop.
<form action='index.php' method='post'>
<input name='title' type='text' placeholder='Title...' value=''>
<div class='articles-list'></div>
<br />
<textarea name='body' placeholder='Content...' ></textarea>
<br />
<a class='submit-button' href='#' />Submit</a>
<br />
<div class='error-message'></div>
</form>
'use strict';
// TODO A: Improve readability and documentation.
// TODO B: Implement an autocomplete functionality for the text input.
// TODO C: Preview file content when clicking on an autocomplete suggestion.
// TODO D: Display autocomplete suggestions only when the input is in focus.
// TODO E: Add error handling for the autocomplete suggestions in case of a network failure.
(() => {
const submitButton = document.querySelector('.submit-button');
const form = document.querySelector('form');
const articleTitle = document.querySelector('input');
const articleBody = document.querySelector('textarea');
const autoCompleteList = document.querySelector('.articles-list');
const errorMessage = document.querySelector('.error-message');
const delay = 200;
/**
* Create child element for the autocomplete list.
*
* @function createChildOption
* @param {string} articleName
* @returns {HTMLElement} The created child option element wrapped
*/
const createChildOption = (articleName) => {
const childOption = document.createElement('div');
childOption.textContent = articleName;
childOption.addEventListener('click', () => {
articleTitle.value = articleName;
fetchArticleContent(articleName);
clearAutoCompleteList();
});
return childOption;
};
/**
* Clear autocomplete list removing all child elements and hides the list
* from view.
*
* @function clearAutoCompleteList
*/
const clearAutoCompleteList = () => {
autoCompleteList.innerHTML = '';
autoCompleteList.style.display = 'none';
}
/**
* Handles errors that occur during data fetching.
*
* @function handleFetchError
* @param {Error} error - The error object thrown during the fetch operation
*/
const handleFetchError = (error) => {
console.error('Error fetching data:', error);
errorMessage.textContent = 'Unable load content. Please try again.';
errorMessage.style.display = 'block';
};
/**
* Fetch an existing title of an article based on a search query
* and fill the autocomplete list.
*
* @function fetchArticleTitle
* @param {string} query
*/
const fetchArticleTitle = (query) => {
fetch(`/api.php?search=${encodeURIComponent(query)}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
})
.then(data => {
autoCompleteList.innerHTML = '';
if (data.content && data.content.length > 0) {
data.content.forEach(articleName => {
const childOption = createChildOption(articleName);
autoCompleteList.appendChild(childOption);
});
autoCompleteList.style.display = 'block';
} else {
autoCompleteList.style.display = 'none';
}
})
.catch(error => {
clearAutoCompleteList();
handleFetchError(error);
});
}
/**
* Fetch the content of an article based on its title and fill textarea.
*
* @function fetchArticleContent
* @param {string} title
*/
const fetchArticleContent = (title) => {
fetch(`/api.php?title=${encodeURIComponent(title)}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
})
.then(data => {
articleBody.value = data.content;
errorMessage.style.display = 'none';
})
.catch(error => {
handleFetchError(error);
});
};
/**
* Initializes the autocomplete functionality for the article title input.
*
* This function set up event listeners to provide autocomplete suggestions
* as the user types in the input field. Handle hiding the autocomplete list
* when the input loses focus.
*
* The autocomplete data are fetched after a delay to avoid too many
* requests in short time.
*
* @function autoComplete
*/
const autoComplete = () => {
let autoCompleteTimeout = null;
articleTitle.addEventListener('input', function () {
const query = this.value.trim();
clearTimeout(autoCompleteTimeout);
clearAutoCompleteList();
if (query.length === 0) return;
autoCompleteTimeout = setTimeout(() => {
fetchArticleTitle(query);
}, delay);
});
/**
* Hide the autocomplete list with a delay when the input loses focus.
*/
articleTitle.addEventListener('blur', () => {
setTimeout(() => {
clearAutoCompleteList();
}, delay);
});
}
/**
* Validate the form fields before submitting the form.
*
* @function submitArticle
*/
const submitArticle = () => {
submitButton.addEventListener('click', (e) => {
if (articleTitle.value.trim() === '' || articleBody.value.trim() === '') {
e.preventDefault();
errorMessage.innerHTML = 'Fields shouldn\'t be empty.';
errorMessage.style.display = 'block';
} else {
form.submit();
}
});
};
/**
* Calls submitArticle function on DOM loaded to set up the form submission.
*
* @event DOMContentLoaded
*/
document.addEventListener('DOMContentLoaded', function() {
autoComplete();
submitArticle();
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment