Bookmark Program using JavaScript - javascript

<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" />
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<h1 class="heading">Book Search</h1>
<input placeholder="Type Book name" class="searchBar" id="searchInput" type="search">
<br>
<select id="selectDisplayCount" class="options">
<option selected value="10">10 Books</option>
<option value="20">20 Books</option>
<option value="30">30 Books</option>
</select>
</div>
<div id="searchResults">
<div class="col-12 d-none mt-5" id="spinner">
<div class="d-flex flex-row justify-content-center">
<div class="spinner-border" role="status"></div>
</div>
</div>
</div>
</body>
</html>
let searchInputEl = document.getElementById("searchInput");
let spinnerEl = document.getElementById("spinner");
let searchresultsEl = document.getElementById("searchResults")
let selectDisplayCountEl = document.getElementById("selectDisplayCount");
let bookcount = selectDisplayCountEl.value;
let formData = {
count: "10 Books",
}
function createAndAppendSearchResult(result) {
let {
title,
imageLink,
author
} = result;
let resultItemEl = document.createElement("div");
resultItemEl.classList.add("country-card", "col-6", "mr-auto", "ml-auto", "d-flex", "flex-column")
searchresultsEl.appendChild(resultItemEl);
let imageEl = document.createElement("img");
imageEl.classList.add("image", "mt-auto", "mb-auto")
imageEl.src = imageLink;
resultItemEl.appendChild(imageEl);
let authorEl = document.createElement("p");
authorEl.classList.add("author-name")
authorEl.textContent = author;
resultItemEl.appendChild(authorEl);
}
function displaySearchResults(searchresult) {
for (let result of searchresult) {
let titleName = result.title
if (titleName.includes(searchInputEl.value)) {
createAndAppendSearchResult(result);
} else {
searchInputEl.textontent = "No reuslts found";
}
}
}
function searchBook(event) {
if (event.key === "Enter") {
let url = "https://apis.ccbp.in/book-store?title=kalam&maxResults=30" + searchInput;
let options = {
method: "GET"
}
spinnerEl.classList.remove("d-none");
searchresultsEl.classList.add("d-none");
fetch(url, options)
.then(function(response) {
return response.json();
})
.then(function(jsonData) {
searchresultsEl.classList.remove("d-none");
spinnerEl.classList.add("d-none");
console.log(jsonData)
let {
search_results
} = jsonData;
console.log(JSON.stringify(jsonData))
displaySearchResults(search_results);
})
}
}
selectDisplayCountEl.addEventListener("change", function(event) {
formData.count = event.target.value;
console.log(formData.count)
});
searchInputEl.addEventListener("keydown", searchBook);
errors I am getting
When a value is entered in the HTML input element with id searchInput, x books option is selected in the HTML select element with id selectDisplayCount, an HTTP GET request with a valid url parameters title and maxResults should be made
When a value is entered in the HTML input element with id searchInput, x books option is selected in the HTML select element with id selectDisplayCount, an HTTP GET request should be made to fetch and display x book items
how exactly it should work is:
https://assets.ccbp.in/frontend/content/dynamic-webapps/book_search_output.gif
let searchInputEl = document.getElementById("searchInput");
let spinnerEl = document.getElementById("spinner");
let searchresultsEl = document.getElementById("searchResults")
let selectDisplayCountEl = document.getElementById("selectDisplayCount");
let bookcount = selectDisplayCountEl.value;
let formData = {
count: "10 Books",
}
function createAndAppendSearchResult(result) {
let {
title,
imageLink,
author
} = result;
let resultItemEl = document.createElement("div");
resultItemEl.classList.add("country-card", "col-6", "mr-auto", "ml-auto", "d-flex", "flex-column")
searchresultsEl.appendChild(resultItemEl);
let imageEl = document.createElement("img");
imageEl.classList.add("image", "mt-auto", "mb-auto")
imageEl.src = imageLink;
resultItemEl.appendChild(imageEl);
let authorEl = document.createElement("p");
authorEl.classList.add("author-name")
authorEl.textContent = author;
resultItemEl.appendChild(authorEl);
}
function displaySearchResults(searchresult) {
for (let result of searchresult) {
let titleName = result.title
if (titleName.includes(searchInputEl.value)) {
createAndAppendSearchResult(result);
} else {
searchInputEl.textontent = "No reuslts found";
}
}
}
function searchBook(event) {
if (event.key === "Enter") {
let url = "https://apis.ccbp.in/book-store?title=kalam&maxResults=30" + searchInput;
let options = {
method: "GET"
}
spinnerEl.classList.remove("d-none");
searchresultsEl.classList.add("d-none");
fetch(url, options)
.then(function(response) {
return response.json();
})
.then(function(jsonData) {
searchresultsEl.classList.remove("d-none");
spinnerEl.classList.add("d-none");
console.log(jsonData)
let {
search_results
} = jsonData;
console.log(JSON.stringify(jsonData))
displaySearchResults(search_results);
})
}
}
selectDisplayCountEl.addEventListener("change", function(event) {
formData.count = event.target.value;
console.log(formData.count)
});
searchInputEl.addEventListener("keydown", searchBook);
<link rel='stylesheet' href='//stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' integrity='sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z' crossorigin='anonymous' />
<script src='//code.jquery.com/jquery-3.5.1.slim.min.js' integrity='sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj' crossorigin='anonymous'></script>
<script src='//cdn.jsdelivr.net/npm/popper.js#1.16.1/dist/umd/popper.min.js' integrity='sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN' crossorigin='anonymous'></script>
<script src='//stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js' integrity='sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV' crossorigin='anonymous'></script>
<div class='container'>
<h1 class='heading'>Book Search</h1>
<input placeholder='Type Book name' class='searchBar' id='searchInput' type='search'>
<br>
<select id='selectDisplayCount' class='options'>
<option selected value='10'>10 Books</option>
<option value='20'>20 Books</option>
<option value='30'>30 Books</option>
</select>
</div>
<div id='searchResults'>
<div class='col-12 d-none mt-5' id='spinner'>
<div class='d-flex flex-row justify-content-center'>
<div class='spinner-border' role='status'></div>
</div>
</div>
</div>

According to the animated GIF the select menu ought to also trigger the AJAX request to the 3rd party api however according to your code it simply updates the global variableformData. As both the select and the keydown/enter are to trigger the same actions a single event handler can be used. With a little re-writing of the above and some new functions one can simplify the code to accomplish what you are trying (I think) to do.
The following code snippet omits the various external scripts and stylesheets used in the original but will serve to illustrate the result.
document.addEventListener('DOMContentLoaded',()=>{
let oRes=document.getElementById('searchResults');
let oText=document.getElementById('searchInput');
let oSel=document.getElementById('selectDisplayCount');
let oSpin=document.getElementById('spinner');
const baseurl='https://apis.ccbp.in/book-store';
// utility to ease construction of full urls in modern browsers
const buildurl=function( endpoint, params ){
let url=new URL( endpoint );
url.search = new URLSearchParams( params ).toString();
return url;
};
// utility method to create new DOM elements
// where t=node type, a=attributes & p=parent to append to.
const create=function(t,a,p){
let el = ( typeof( t )=='undefined' || t==null ) ? document.createElement( 'div' ) : document.createElement( t );
let _arr=['innerHTML','innerText','html','text'];
for( let x in a ) if( a.hasOwnProperty( x ) && !~_arr.indexOf( x ) ) el.setAttribute( x, a[ x ] );
if( a.hasOwnProperty('innerHTML') || a.hasOwnProperty('html') ) el.innerHTML=a.innerHTML || a.html;
if( a.hasOwnProperty('innerText') || a.hasOwnProperty('text') ) el.innerText=a.innerText || a.text;
if( p!=null ) typeof( p )=='object' ? p.appendChild( el ) : document.getElementById( p ).appendChild( el );
return el;
};
// The callback method to generate output HTML
const callback=(json)=>{
toggleclasses(false);
let data=json.search_results;
oRes.innerHTML='';
Object.keys( data ).forEach( k => {
let obj=data[ k ];
let div=create(null,{'class':'country-card col-6 mr-auto ml-auto d-flex flex-column'},oRes);
create('h1',{text:obj.title},div);
create('img',{src:obj.imageLink,'class':'image mt-auto mb-auto'},div);
create('p',{text:obj.author,'class':'author-name'},div);
});
};
// helper to switch classes as per original
const toggleclasses=(b)=>{
switch(b){
case true:
oSpin.classList.remove('d-none');
oRes.classList.add('d-none');
break;
case false:
oSpin.classList.add('d-none');
oRes.classList.remove('d-none');
break;
}
};
// single event handler to send AJAX request
const requesthandler=(e)=>{
if( e.key === 'Enter' || e.type=='change' ) {
toggleclasses(true);
if( oText.value=='')return;
let args={
title:oText.value,
maxResults:oSel.value
};
fetch( buildurl( baseurl, args ) )
.then( r=>r.json() )
.then( json=>callback( json ) )
}
};
oSel.addEventListener('change',requesthandler);
oText.addEventListener('keydown',requesthandler);
});
<div class='container'>
<h1 class='heading'>Book Search</h1>
<input placeholder='Type Book name' class='searchBar' id='searchInput' type='search' />
<br />
<select id='selectDisplayCount' class='options'>
<option value='1'>1 Book
<option value='5'>5 Books
<option selected value='10'>10 Books
<option value='20'>20 Books
<option value='30'>30 Books
<option value='50'>50 Books
<option value='100'>100 Books
</select>
</div>
<div id='searchResults'>
<div class='col-12 d-none mt-5' id='spinner'>
<div class='d-flex flex-row justify-content-center'>
<div class='spinner-border' role='status'></div>
</div>
</div>
</div>

Related

Liveserach passing values ​from other object fields iterates

I put a snippet to make the idea better.
Type any character in the Find field, in the console you will see the log of the dataAdd object, if now we change the value of the select Department and type any character again, the log becomes double, with the value of Department previous and current one, and if you repeat this, each time you add the value in the log.
I need the passed value to send it on the server side to filter the search by department but this way it can't work.
Can you help me, I'm going crazy
const $ = id => document.getElementById(id);
let ajaxInProgress = false;
const ajaxGet = async (elab, q, dataAdd) => {
if (ajaxInProgress) return;
ajaxInProgress = true;
let url;
if (dataAdd != null) {
url = "/utility/cerca/getLiveSearch/" + elab + "/" + q + "/" + btoa(JSON.stringify(dataAdd));
} else {
url = "/utility/cerca/getLiveSearch/" + elab + "/" + q;
}
let response = await fetch(url),
data = await response.text();
if (response.ok) {
ajaxInProgress = false;
return data;
} else if (response.status === 404) {
ajaxInProgress = false;
return false;
} else {
ajaxInProgress = false;
return "Error: " + response.status;
}
};
const liveSearch = (id, elCont, elab, dataAdd) => {
const el = document.querySelector("input" + id);
// If I can't find the execution block element
if (!el) return !1;
el.classList.add("liveSearch");
el.parentElement.classList.add("position-relative");
el.setAttribute("autocomplete", "off");
// If the item doesn't already exist, I create it
if (!$(elCont)) {
// Appendo elemento al campo input
let lsContainer = document.createElement("div");
lsContainer.id = elCont;
lsContainer.classList.add("liveSearchContent", "shadow", "bg-white", "border", "border-2", "rounded-2", "position-absolute", "overflow-auto", "mt-1", "d-none");
el.after(lsContainer);
}
// To KeyUp
el.addEventListener("keyup", ls => {
// I get the typed value
let q = el.value,
innerResult = document.querySelector("#" + elCont);
if (q != "") {
// Log data send from input field
console.log(dataAdd);
ajaxGet(elab, q, dataAdd).then(html => {
// Resize liveSearch
resizeLiveSearch();
// I hang the results
innerResult.innerHTML = html + ' - ' + q;
// Hide/Show liveSearch
ls.target.value.length < 1 ? innerResult.classList.add("d-none") : innerResult.classList.remove("d-none");
});
} else {
// Hide/Show liveSearch
ls.target.value.length < 1 ? innerResult.classList.add("d-none") : innerResult.classList.remove("d-none");
}
});
el.addEventListener("focus", el => {
el.target.closest("div").querySelector(".liveSearchContent").classList.remove("d-none");
});
// When I resize the window, I resize the livesearch
window.onresize = resizeLiveSearch;
document.addEventListener("click", e => {
let box = document.querySelector(".liveSearchContent");
if (box) {
if (box.contains(e.target) || !e.target.classList.contains("liveSearch")) {
handleBlur();
} else {
return false;
}
}
});
};
let resizeLiveSearch = () => {
document.querySelectorAll(".liveSearch").forEach(e => {
e.closest("div").querySelector(".liveSearchContent").style.width = e.offsetWidth + "px";
});
};
let handleBlur = () => {
document.querySelectorAll(".liveSearchContent").forEach(el => {
el.classList.add("d-none");
});
};
// -- Piece of code that interacts with liveSearch
document.addEventListener("DOMContentLoaded", () => {
let idReparto = $("id_reparto"),
idEvento = $("id_evento"),
evento = $("evento"),
reparto = idReparto.value;
idReparto.addEventListener("change", el => {
console.clear();
idEvento.value = evento.value = "";
reparto = el.target.value;
liveSearch("#evento", "searchEventoRicerca", "evento_reparto", { reparto: reparto });
});
liveSearch("#evento", "searchEventoRicerca", "evento_reparto", { reparto: $("id_reparto").value });
$("searchEventoRicerca").addEventListener("click", el => {
let elt = el.target,
evento2 = "";
if (elt.matches(".srel")) {
evento2 = elt.getAttribute("id");
el.preventDefault();
elt.blur();
evento.value = elt.text;
idEvento.value = evento2;
pdvEvento.value = idPdv.value = "";
// Mostro pdv_evento e occulto pdv
pdvEvento.parentElement.classList.remove("d-none");
console.log(evento2);
}
});
});
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.2/dist/css/bootstrap.min.css" rel="stylesheet"/>
<form class="mb-5 mt-2 mx-2">
<div class="row g-3">
<input type="hidden" id="id_evento" name="id_evento" value="">
<div class="col-md-3">
<label for="id_reparto" class="form-label">Department</label>
<select class="form-select" id="id_reparto" name="id_reparto" title="id_reparto">
<option value="1">Dep 1</option>
<option value="3">Dep 3</option>
<option value="2" selected="selected">Dep 2</option>
</select>
</div>
<div class="col-md-6">
<label for="evento" class="form-label">Find</label>
<input type="text" class="form-control liveSearch" placeholder="Type any character" id="evento" name="evento" value="" autocomplete="off">
<div id="searchEventoRicerca" class="liveSearchContent shadow bg-white border border-2 rounded-2 position-absolute overflow-auto mt-1 d-none"></div>
</div>
</div>
</form>

JS dynamically created area with buttons is missing when to-do task is edited (and not always)

I wanted to create my own To-do list app and I have a problem when I edit a task the area with three buttons (accept, edit and cancel btns) sometimes goes missing.
I'm trying to figure out what's happening with it in the dev tools and it looks like when there are e.g. two tasks (two li elements) the div area with buttons move from one to another and the second li has two divs and the first li (edited one) it left with no buttons at all.
Here's my code, please excuse any weird things you may find there (I'm not really experienced).
const inputField = document.querySelector('.todo__input')
const addBtn = document.querySelector('.todo__btn')
const ulList = document.querySelector('.todo__list')
const infoParagraph = document.querySelector('.todo__info-paragraph')
const popupDiv = document.querySelector('.pop-up')
const popupAcceptBtn = document.querySelector('.edit-accept')
const popupCancelBtn = document.querySelector('.edit-cancel')
const popupInput = document.querySelector('.edit__input')
const popupInfo = document.querySelector('.edit__info')
let liItem
let acceptButton
let editButton
let cancelButton
let editedTodoTask
let editBtnToBeDisabled
const createLiSpace = () => {
const div = document.createElement('div')
div.classList.add('todo__list-buttons')
liItem.append(div)
acceptButton = document.createElement('button')
acceptButton.classList.add('todo__list-btn')
acceptButton.classList.add('todo__list-btn--accept')
acceptButton.innerHTML = '<i class="fa-solid fa-check"></i>'
editButton = document.createElement('button')
editButton.classList.add('todo__list-btn')
editButton.classList.add('todo__list-btn--edit')
editButton.innerHTML = '<i class="fa-solid fa-pencil"></i>'
cancelButton = document.createElement('button')
cancelButton.classList.add('todo__list-btn')
cancelButton.classList.add('todo__list-btn--cancel')
cancelButton.innerHTML = '<i class="fa-solid fa-xmark"></i>'
div.append(acceptButton, editButton, cancelButton)
acceptButton.addEventListener('click', completeTask)
editButton.addEventListener('click', showPopup)
cancelButton.addEventListener('click', deleteTask)
}
const addTask = () => {
if (inputField.value !== '') {
liItem = document.createElement('li')
liItem.classList.add('todo__list-item')
liItem.textContent = inputField.value
createLiSpace()
ulList.appendChild(liItem)
inputField.value = ''
infoParagraph.textContent = ''
} else if (inputField.value == '') {
infoParagraph.style.color = 'rgb(206, 99, 28)'
infoParagraph.textContent = 'Please type in your task first!'
}
}
const showPopup = e => {
editedTodoTask = e.target.closest('li')
popupDiv.style.visibility = 'visible'
popupInfo.textContent = ''
popupInput.value = editedTodoTask.firstChild.textContent
}
const changeTodoTask = () => {
if (popupInput.value !== '') {
editedTodoTask.textContent = popupInput.value
createLiSpace()
closePopup()
popupInput.value = ''
} else {
popupInfo.textContent = 'Please type in some text here...'
}
}
const enterAddTask = (e) => {
if (e.key == "Enter") {
addTask()
}
}
const completeTask = e => {
editedTodoTask = e.target.closest('li')
editedTodoTask.classList.toggle('crossline')
}
const closePopup = () => {
popupDiv.style.visibility = 'hidden'
}
const deleteTask = e => {
ulList.removeChild(e.target.closest('li'))
}
popupAcceptBtn.addEventListener('click', changeTodoTask)
popupCancelBtn.addEventListener('click', closePopup)
addBtn.addEventListener('click', addTask)
inputField.addEventListener('keyup', enterAddTask)
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div class="container">
<div class="todo-top-section">
<h1 class="todo__header">My to-do list</h1>
<input type="text" placeholder="Type your task here..." class="todo__input">
<button class="todo__btn"><img class="todo__blob" src="/src/img/blobanimation.svg" alt="">add</button>
</div>
<div class="todo-bottom-section">
<h2 class="todo__heading">Task list:</h2>
<p class="todo__info-paragraph">No tasks here yet</p>
<ul class="todo__list">
</ul>
</div>
<div class="pop-up">
<h3 class="edit__header">Want to change anything in here?</h3>
<p class="edit__info"></p>
<div class="edit-container">
<input type="text" class="edit__input" placeholder="Type in your change...">
<div class="todo__list-buttons">
<button class="todo__list-btn todo__list-btn--accept edit-accept">Accept <i class="fa-solid fa-check"></i></button>
<button class="todo__list-btn todo__list-btn--cancel edit-cancel">Cancel <i class="fa-solid fa-xmark"></i></button>
</div>
</div>
</div>
Could you please check and specify what is causing that trouble?
You can find my code here too: https://codepen.io/re_aleksandra/pen/PoebEPv
Thank you!

Instantiate ES6 class

I am trying to implement the following tags with my design.
I am using the class Tags to simply create tags within my input field, however when I initialize the library I get an error.
const ACTIVE_CLASS = "bg-light";
const VALUE_ATTRIBUTE = "data-value";
class Tags {
/**
* #param {HTMLSelectElement} selectElement
*/
constructor(selectElement) {
this.selectElement = selectElement;
this.selectElement.style.display = "none";
this.placeholder = this.getPlaceholder();
this.allowNew = selectElement.dataset.allowNew ? true : false;
// Create elements
this.holderElement = document.createElement("div");
this.containerElement = document.createElement("div");
this.dropElement = document.createElement("ul");
this.searchInput = document.createElement("input");
this.holderElement.appendChild(this.containerElement);
this.containerElement.appendChild(this.searchInput);
this.holderElement.appendChild(this.dropElement);
// insert after
this.selectElement.parentNode.insertBefore(this.holderElement, this.selectElement.nextSibling);
// Configure them
this.configureSearchInput();
this.configureHolderElement();
this.configureDropElement();
this.configureContainerElement();
this.buildSuggestions();
}
/**
* Attach to all elements matched by the selector
* #param {string} selector
*/
static init(selector = "select[multiple]") {
let list = document.querySelectorAll(selector);
for (let i = 0; i < list.length; i++) {
let el = list[i];
let inst = new Tags(el);
}
}
/**
* #returns {string}
*/
getPlaceholder() {
let firstOption = this.selectElement.querySelector("option");
if (!firstOption) {
return;
}
if (!firstOption.value) {
let placeholder = firstOption.innerText;
firstOption.remove();
return placeholder;
}
if (this.selectElement.getAttribute("placeholder")) {
return this.selectElement.getAttribute("placeholder");
}
if (this.selectElement.getAttribute("data-placeholder")) {
return this.selectElement.getAttribute("data-placeholder");
}
return "";
}
configureDropElement() {
this.dropElement.classList.add("dropdown-menu");
}
configureHolderElement() {
this.holderElement.classList.add("form-control");
this.holderElement.classList.add("dropdown");
}
configureContainerElement() {
this.containerElement.addEventListener("click", (event) => {
this.searchInput.focus();
});
// add initial values
let initialValues = this.selectElement.querySelectorAll("option[selected]");
for (let j = 0; j < initialValues.length; j++) {
let initialValue = initialValues[j];
if (!initialValue.value) {
continue;
}
this.addItem(initialValue.innerText, initialValue.value);
}
}
configureSearchInput() {
this.searchInput.type = "text";
this.searchInput.autocomplete = false;
this.searchInput.style.border = 0;
this.searchInput.style.outline = 0;
this.searchInput.style.maxWidth = "100%";
this.adjustWidth();
this.searchInput.addEventListener("input", (event) => {
this.adjustWidth();
if (this.searchInput.value.length >= 1) {
this.showSuggestions();
} else {
this.hideSuggestions();
}
});
// keypress doesn't send arrow keys
this.searchInput.addEventListener("keydown", (event) => {
if (event.code == "Enter") {
let selection = this.getActiveSelection();
if (selection) {
this.addItem(selection.innerText, selection.getAttribute(VALUE_ATTRIBUTE));
this.resetSearchInput();
this.hideSuggestions();
} else {
// We use what is typed
if (this.allowNew) {
this.addItem(this.searchInput.value);
this.resetSearchInput();
this.hideSuggestions();
}
}
event.preventDefault();
return;
}
if (event.code == "ArrowUp") {
this.moveSelectionUp();
}
if (event.code == "ArrowDown") {
this.moveSelectionDown();
}
if (event.code == "Backspace") {
if (this.searchInput.value.length == 0) {
this.removeLastItem();
this.adjustWidth();
}
}
});
}
moveSelectionUp() {
let active = this.getActiveSelection();
if (active) {
let prev = active.parentNode;
do {
prev = prev.previousSibling;
} while (prev && prev.style.display == "none");
if (!prev) {
return;
}
active.classList.remove(ACTIVE_CLASS);
prev.querySelector("a").classList.add(ACTIVE_CLASS);
}
}
moveSelectionDown() {
let active = this.getActiveSelection();
if (active) {
let next = active.parentNode;
do {
next = next.nextSibling;
} while (next && next.style.display == "none");
if (!next) {
return;
}
active.classList.remove(ACTIVE_CLASS);
next.querySelector("a").classList.add(ACTIVE_CLASS);
}
}
/**
* Adjust the field to fit its content
*/
adjustWidth() {
if (this.searchInput.value) {
this.searchInput.size = this.searchInput.value.length + 1;
} else {
// Show the placeholder only if empty
if (this.getSelectedValues().length) {
this.searchInput.placeholder = "";
this.searchInput.size = 1;
} else {
this.searchInput.size = this.placeholder.length;
this.searchInput.placeholder = this.placeholder;
}
}
}
/**
* Add suggestions from element
*/
buildSuggestions() {
let options = this.selectElement.querySelectorAll("option");
for (let i = 0; i < options.length; i++) {
let opt = options[i];
if (!opt.getAttribute("value")) {
continue;
}
let newChild = document.createElement("li");
let newChildLink = document.createElement("a");
newChild.append(newChildLink);
newChildLink.classList.add("dropdown-item");
newChildLink.setAttribute(VALUE_ATTRIBUTE, opt.getAttribute("value"));
newChildLink.setAttribute("href", "#");
newChildLink.innerText = opt.innerText;
this.dropElement.appendChild(newChild);
// Hover sets active item
newChildLink.addEventListener("mouseenter", (event) => {
this.removeActiveSelection();
newChild.querySelector("a").classList.add(ACTIVE_CLASS);
});
newChildLink.addEventListener("click", (event) => {
event.preventDefault();
this.addItem(newChildLink.innerText, newChildLink.getAttribute(VALUE_ATTRIBUTE));
this.resetSearchInput();
this.hideSuggestions();
});
}
}
resetSearchInput() {
this.searchInput.value = "";
this.adjustWidth();
}
/**
* #returns {array}
*/
getSelectedValues() {
let selected = this.selectElement.querySelectorAll("option:checked");
return Array.from(selected).map((el) => el.value);
}
/**
* The element create with buildSuggestions
*/
showSuggestions() {
if (!this.dropElement.classList.contains("show")) {
this.dropElement.classList.add("show");
}
// Position next to search input
this.dropElement.style.left = this.searchInput.offsetLeft + "px";
// Get search value
let search = this.searchInput.value.toLocaleLowerCase();
// Get current values
let values = this.getSelectedValues();
// Filter the list according to search string
let list = this.dropElement.querySelectorAll("li");
let found = false;
let firstItem = null;
for (let i = 0; i < list.length; i++) {
let item = list[i];
let text = item.innerText.toLocaleLowerCase();
let link = item.querySelector("a");
// Remove previous selection
link.classList.remove(ACTIVE_CLASS);
// Hide selected values
if (values.indexOf(link.getAttribute(VALUE_ATTRIBUTE)) != -1) {
item.style.display = "none";
continue;
}
if (text.indexOf(search) !== -1) {
item.style.display = "list-item";
found = true;
if (!firstItem) {
firstItem = item;
}
} else {
item.style.display = "none";
}
}
// Special case if nothing matches
if (!found) {
this.dropElement.classList.remove("show");
}
// Always select first item
if (firstItem) {
if (this.holderElement.classList.contains("is-invalid")) {
this.holderElement.classList.remove("is-invalid");
}
firstItem.querySelector("a").classList.add(ACTIVE_CLASS);
} else {
// No item and we don't allow new items => error
if (!this.allowNew) {
this.holderElement.classList.add("is-invalid");
}
}
}
/**
* The element create with buildSuggestions
*/
hideSuggestions(dropEl) {
if (this.dropElement.classList.contains("show")) {
this.dropElement.classList.remove("show");
}
if (this.holderElement.classList.contains("is-invalid")) {
this.holderElement.classList.remove("is-invalid");
}
}
/**
* #returns {HTMLElement}
*/
getActiveSelection() {
return this.dropElement.querySelector("a." + ACTIVE_CLASS);
}
removeActiveSelection() {
let selection = this.getActiveSelection();
if (selection) {
selection.classList.remove(ACTIVE_CLASS);
}
}
removeLastItem() {
let items = this.containerElement.querySelectorAll("span");
if (!items.length) {
return;
}
let lastItem = items[items.length - 1];
this.removeItem(lastItem.getAttribute(VALUE_ATTRIBUTE));
}
/**
* #param {string} text
* #param {string} value
*/
addItem(text, value) {
if (!value) {
value = text;
}
let span = document.createElement("span");
span.classList.add("badge");
span.classList.add("bg-primary");
span.classList.add("me-2");
span.setAttribute(VALUE_ATTRIBUTE, value);
span.innerText = text;
this.containerElement.insertBefore(span, this.searchInput);
// update select
let opt = this.selectElement.querySelector('option[value="' + value + '"]');
if (opt) {
opt.setAttribute("selected", "selected");
} else {
// we need to create a new option
opt = document.createElement("option");
opt.value = value;
opt.innerText = text;
opt.setAttribute("selected", "selected");
this.selectElement.appendChild(opt);
}
}
/**
* #param {string} value
*/
removeItem(value) {
let item = this.containerElement.querySelector("span[" + VALUE_ATTRIBUTE + '="' + value + '"]');
if (!item) {
return;
}
item.remove();
// update select
let opt = this.selectElement.querySelector('option[value="' + value + '"]');
if (opt) {
opt.removeAttribute("selected");
}
}
}
export default Tags;
import Tags
Tags.init();
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.80.0">
<title>Insider</title>
<link rel="canonical" href="">
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.4.0/font/bootstrap-icons.css">
<!-- Favicons -->
<link rel="apple-touch-icon" href="https://getbootstrap.com/docs/5.0/assets/img/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" href="https://getbootstrap.com/docs/5.0/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="https://getbootstrap.com/docs/5.0/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<link rel="manifest" href="https://getbootstrap.com/docs/5.0/assets/img/favicons/manifest.json">
<link rel="mask-icon" href="https://getbootstrap.com/docs/5.0/assets/img/favicons/safari-pinned-tab.svg" color="#7952b3">
<link rel="icon" href="https://getbootstrap.com/docs/5.0/assets/img/favicons/favicon.ico">
<meta name="theme-color" content="#7952b3">
<!-- Custom styles for this template -->
<link href="https://unpkg.com/bootstrap-table#1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
</head>
<body>
<header class="p-3 bg-dark text-white">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li>
<button class="btn btn-outline-light" type="button" data-bs-toggle="modal" data-bs-target="#exampleModal">Check
</button>
</li>
</ul>
</div>
</div>
</header>
<main class="container pt-3">
<div class="row mt-3">
<h2>Content</h2>
</div>
</main>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Your Check</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<br>
<p class="h2">Check</p>
<p>Input your symbols and we will send you all relevant information.</p>
<br>
<form>
<div class="col">
<select class="form-select" id="validationTags" multiple="" data-allow-new="true" style="display: none;">
<option value="1" selected="selected">JavaScript</option>
<option value="2">HTML5</option>
<option value="3">CSS3</option>
<option value="4">jQuery</option>
<option value="5">React</option>
<option value="6">Angular</option>
<option value="7">Vue</option>
<option value="8">Python</option>
</select>
<div class="form-control dropdown">
<div><span class="badge bg-primary me-2" data-value="1">JavaScript</span><input type="text" autocomplete="false" placeholder="" size="1" style="border: 0px; outline: 0px; max-width: 100%;">
</div>
<ul class="dropdown-menu">
<li><a class="dropdown-item" data-value="1" href="#">JavaScript</a></li>
<li><a class="dropdown-item" data-value="2" href="#">HTML5</a></li>
<li><a class="dropdown-item" data-value="3" href="#">CSS3</a></li>
<li><a class="dropdown-item" data-value="4" href="#">jQuery</a></li>
<li><a class="dropdown-item" data-value="5" href="#">React</a></li>
<li><a class="dropdown-item" data-value="6" href="#">Angular</a></li>
<li><a class="dropdown-item" data-value="7" href="#">Vue</a></li>
<li><a class="dropdown-item" data-value="8" href="#">Python</a></li>
</ul>
</div>
<div class="invalid-feedback">Please select a valid tag.</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
<!-- Modal END -->
<footer class="text-muted py-5">
<div class="container">
<p class="mb-1">Footer</p>
</div>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>
</body>
</html>
As you can see the script does not work.
To be honest I am not quite sure why. I am guessing there is a problem when initializing the Tags-class. I currently do it the following:
import Tags
Tags.init();
Any suggestions what is wrong or how to correctly call the init()-function from the Tags-class?
I appreciate your replies!
In my answer I'm assuming that your question is about how to import a class from a <script> to another <script> using ECMAScript modules, which is not very clear when reading your question.
You just have to specify type="module" on the exporting script and also the importing one, then import one file from the other with its URL.
For example if you have this web site structure:
app/
├─ lib/
│ ├─ tags.js
├─ index.js
├─ index.html
The tags.js file would contain:
class Tags {
// ...
}
export default Tags;
And index.js would contain:
import Tags from './lib/tags.js';
// ...
Here is a demo, I have used data URLs as a workaround because I can't publish JavaScript files in a StackOverflow snippet, but it works exactly the same with regular <script> tags and regular URLs:
const lib = `class Tags {
static init() {
console.log('it works!');
}
}
export default Tags;`;
const libURL = createScript(lib);
const code = `import Tags from '${libURL}';
Tags.init();`;
createScript(code);
function createScript(code) {
const url = `data:application/javascript;charset=utf-8;base64,${btoa(code)}`;
const script = document.createElement('script');
script.src = url;
script.type = 'module';
document.body.appendChild(script);
return url;
}
to initiate a es6 class you would use:
const new_object = new Tags(selectElement)
you pass selectElement into your constructor function and get a new object in return.
i don't know where you are calling this code from, if you want to call it from another file you need to define it in your export default.
this way you can use the es6 import syntax.
The error export declarations may only appear at top level of a module is already explained in #Guerric P's answer.
You are calling Tags.init(); from the same file as the definition, so if you really want that you can remove:
export default Tags;
import Tags
because it's a class definition and you are calling a static function:
Tags.init(); /* default 'select[multiple]' */
it will result in no error.

why i can't show data i want when user clicks on button

i am making a simple search autocomplete app which search cities and it's information.
the problem is when clicked on suggestion i want that cities information in different html element.
so my javascript code is:
let search=document.getElementById("search");
let searchlist=document.getElementById("searchlist");
search.addEventListener("input",()=>searchcities(search.value));
const searchcities=async searchtext=>{
var res= await fetch("in.json");
const cities=await res.json();
let matches=cities.filter(city=>{
const regex=new RegExp(`^${searchtext}`,'gi');
return city.city.match(regex);
});
if(searchtext.length===0){
matches=[];
searchlist.innerHTML="";
}
outputHTML(matches);
};
function outputHTML(matches){
if(matches.length>0){
const html=matches.map(match=>`
<button class="card" >
<h4>${match.city}<span class="state ">(${match.admin})</span></h4>
<small>Latitude: ${match.lat}<br>longitude:${match.lng}</small>
</button>
`).join("");
searchlist.innerHTML=html;
document.querySelector(".card").addEventListener("click",showresult)
function showresult(match){
let result=document.querySelector("#finalresult");
const show=`
<div class="result">
<h4>${match.city}<span class="state ">(match.admin})</span></h4>
<small>Latitude: ${match.lat}<br>longitude:${match.lng}</small>
</div>
`;
result.innerHTML=show;
console.log(result);
}
}
}
i want this function to work :
function showresult(match){
let result=document.querySelector("#finalresult");
const show=`
<div class="result">
<h4>${match.city}<span class="state ">(match.admin})</span></h4>
<small>Latitude: ${match.lat}<br>longitude:${match.lng}</small>
</div>
`;
result.innerHTML=show;
console.log(result);
}
}
but it is giving me undefined data. I don't know what to do .
the structure of json file is like this:
{
"city": "Mumbai",
"admin": "Maharashtra",
"country": "India",
"population_proper": "12691836",
"iso2": "IN",
"capital": "admin",
"lat": "18.987807",
"lng": "72.836447",
"population": "18978000"
}
thanks for helping.
You're making it yourself to complex for what it actually is. Have you thought about data attributes? Here's an example:
//Fetch function
const searchCities = async searchText => {
if(searchText == null) return;
let matches;
if(searchText.length < 1) matches = []; //current behaviour will set it to no matches when your removed everything from the input
else {
const res = await fetch('js/in.json');
const cities = await res.json();
matches = await cities.filter(city=>{
const regex=new RegExp(`^${searchText}`,'gi');
return city.city.match(regex);
});
}
outputHTML(matches);
};
//Inner function
const outputHTML = (matches) => {
if(matches == null) return;
let html = '<p>no matches</p>';
if(matches.length !== 0) {
html = matches.map(match => `
<button class="card" data-city="${match.city}" data-admin="${match.admin}" data-lat="${match.lat}" data-lng="${match.lng}">
<h4>${match.city}<span class="state ">(${match.admin})</span></h4>
<small>Latitude: ${match.lat}<br>longitude:${match.lng}</small>
</button>
`).join('');
}
const searchlist = document.getElementById("searchlist");
searchlist.innerHTML = html;
const cards = document.querySelectorAll(".card");
cards.forEach(card => card.addEventListener("click", showResult));
};
//Click card function
const showResult = (event) => {
const card = event.currentTarget;
const result = document.querySelector('#finalresult');
const cardData = card.dataset;
const show = `
<div class="result">
<h4>${cardData.city}<span class=""state">${cardData.admin}</span></h4>
<small>Latidude: ${cardData.lat}<br>longitude:${cardData.lng}</small>
</div>
`;
result.innerHTML = show;
};
//Setup
const setup = () => {
const search = document.getElementById("search");
search.addEventListener("input",()=>searchCities(search.value));
};
//Load
window.addEventListener('load', setup);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Indian cities</title>
<script src="js/main.js"></script>
</head>
<body>
<div class="indian-flag">
<div class="1"></div>
<div class="2"></div>
<div class="3"></div>
</div>
<div class="head">CITIES lookup</div>
<input type="text" name="" id="search" class="input" placeholder="Enter city name...">
<div id="searchlist"></div>
<div id="finalresult"></div>
</body>
</html>

Removing trailing letter from textarea JS Function

I have created a SPA Translator App using the Yandex API. I have just finished the logic and the functionality to my app, I am stuck though on how to remove the trailing letter in my output text-area when a user removes all the text from the input text-area.
How would one write a function to clear the output text-area back to ('') blank string after the user has removed all the input text. A link to my live site is here (https://translex-app-shanemuir.c9users.io/dist/index.html).
Here I have a HTML form:
<div class="container myForm text-center">
<div class="row row-center">
<div class="col-sm-12">
<form class="form-inline well justify-content-center">
<textarea id="inputText" class="form-control" placeholder="Enter a Word..."></textarea>
<select class="form-control" id="selectLang">
<option selected>Choose Language...</option>
</select>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h2 class="translation-header">Translation</h2>
<form class="form-inline well justify-content-center">
<textarea readonly class="form-control" placeholder="Translation" id="translation"></textarea>
</form>
</div>
</div>
</div>
Here I have some JS:
let searchInput = document.querySelector('#inputText');
let select = document.querySelector('#selectLang');
let textArea = document.querySelector('#translation');
let selectedLang = 'es';
function getLanguages(){
axios.get('https://translate.yandex.net/api/v1.5/tr.json/getLangs?key=trnsl.1.1.20180914T205319Z.b0ffef87e97badd3.e78787ec8a392b9772f8b8f56933b1d86463d330&ui=en')
.then((response) => {
let languages = response.data.langs;
let languageCode = {
name:'',
code:''
};
for(language in languages) {
languageCode.name = languages[language];
languageCode.code = language;
let option = document.createElement('option');
option.innerHTML = languageCode.name;
option.value = languageCode.code;
select.appendChild(option);
}
})
.catch((err) => {
console.log(err);
});
}
function translateText(textArg){
let searchText;
if(textArg == ''){
searchText = document.querySelector('#inputText').value;
}
else{
searchText = textArg;
}
if(searchText != ''){
axios.get('https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20180914T205319Z.b0ffef87e97badd3.e78787ec8a392b9772f8b8f56933b1d86463d330&lang=' + selectedLang + '&text=' + searchText)
.then((response) => {
let translateInput = document.querySelector('#translation');
translateInput.value = response.data.text[0];
})
.catch((err) => {
console.log(err);
});
}
}
searchInput.addEventListener('keyup',() => {
translateText('');
});
select.addEventListener('change',() => {
selectedLang = select.options[select.selectedIndex].value;
translateText('');
});
getLanguages();
Add an else clause to the if, to empty the textarea.
if(searchText != ''){
axios.get('https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20180914T205319Z.b0ffef87e97badd3.e78787ec8a392b9772f8b8f56933b1d86463d330&lang=' + selectedLang + '&text=' + searchText)
.then((response) => {
let translateInput = document.querySelector('#translation');
translateInput.value = response.data.text[0];
})
.catch((err) => {
console.log(err);
});
} else {
document.querySelector('#translation').value = '';
}

Categories