I'm making a note taker app that gives you the option to view said note in a modal whenever the button is clicked. The HTML for the note and modal is dynamically generated by event listeners. There are two ways the close the modal, by clicking the "X" button or by clicking outside of the modal. The program has full functionality whenever only one note is generated, but once I generate a second note the code breaks down. Once this happens only I'm able to open the modal of the first note generated, but not close it. And the second one won't open whatsoever. How could I fix this issue?
class Input {
constructor(note) {
this.note = note;
}
}
class UI {
addNote(input) {
// Get table body below form
const content = document.querySelector(".content");
// Create tr element
const row = document.createElement("tr");
// Insert new HTML into div
row.innerHTML = `
<td>
${input.note}
<br><br>
<button class="modalBtn">View Note</button>
</td>
`;
content.appendChild(row);
// Event listener to make modal
document.querySelector(".modalBtn").addEventListener("click", function(e) {
// Get container div
const container = document.querySelector(".container");
// Create div
const div = document.createElement("div");
// Assign class to it
div.className = "modal";
// Insert HTML into div
div.innerHTML = `
<div class="modal-content">
<span class="closeBtn">×</span>
<div>
<p>${input.note}</p>
</div>
</div>
`;
// Append the new div to the container div
container.appendChild(div);
// Get modal
const modal = document.querySelector(".modal");
// Event listener to close modal when "x" is clicked
document.querySelector(".closeBtn").addEventListener("click", function() {
container.removeChild(modal);
});
// Event listener to close when the window outside the modal is clicked
window.addEventListener("click", function(e) {
if (e.target === modal) {
container.removeChild(modal);
}
});
});
}
// Clear input field
clearInput() {
note.value = "";
}
}
// Event listener for addNote
document.getElementById("note-form").addEventListener("submit", function(e) {
// Get form value
const note = document.getElementById("note").value;
// Instantiate note
const input = new Input(note);
// Instantiate UI
const ui = new UI();
// Validate form (make sure input is filled)
if (note === "") {
// Error alert
alert("Please fill in text field!");
}
else {
// Add note
ui.addNote(input);
// Clear input field
ui.clearInput();
}
e.preventDefault();
});
h5 {
color: green;
}
.modal {
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fff;
margin: 20% auto;
padding: 30px;
width: 70%;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
animation-name: modalopen;
animation-direction: 1s;
}
.closeBtn {
color: #aaa;
/* float: right; */
font-size: 30px;
margin-bottom: 1rem;
padding-bottom: 1rem;
}
.closeBtn:hover,
.closeBtnBtn:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
.closeBtn + div {
margin-top: 2rem;
}
#keyframes modalopen {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css" integrity="sha512-5fsy+3xG8N/1PV5MIJz9ZsWpkltijBI48gBzQ/Z2eVATePGHOkMIn+xTDHIfTZFVb9GMpflF2wOWItqxAP2oLQ==" crossorigin="anonymous" />
<link rel="stylesheet" href="style.css">
<title>Note Taker</title>
</head>
<body>
<div class="container">
<h1>Note Taker</h1>
<h5>Add A New Note:</h5>
<form id="note-form">
<div>
<label>Note:</label>
<textarea name="Note" id="note" class="u-full-width"> </textarea>
</div>
<div>
<button type="submit" class="button-primary">Add Note</button>
</div>
</form>
<table>
<tbody class="content"></tbody>
</table>
</div>
<script src="app.js"></script>
</body>
</html>
Fist of all.. I love your syntaxt!! Best I've seen so far! second.. you do a general query Selector and don't handle them separately. Can that be an issue?
EDIT:
Because of reasons I'll reformulated my answer..
document.querySelector('class') returns an Html-Collection with DOM element references containing the specified html class and should be handled separately.
Related
Below is a web component written in vanilla js. It is a general component that is a modal drawer, it has a simple job, to open and close.
How would this generic component be able to utilize different event listeners based on its content? For example, if a login form was put into the modal, then how could we add a submit event to the slot content in the shadow dom? If a reset password form was in the modal, then we would need a different submit event.
If the content was a form, and the slot had a submit event listener on it, could the event listener for the slot be removed in the disconnected callback?
const button = document.querySelector('.modal-btn')
button.addEventListener('click', () => {
const modal = document.createElement('drawer-modal')
document.querySelector('#app').appendChild(modal)
})
const template = document.createElement('template')
template.innerHTML = `
<style>
.modal {
z-index: 9;
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
.modal-mask {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 9;
transition: opacity 0.4s ease-in-out;
background-color: rgba(0 0 0 / 50%);
opacity: 0;
}
.modal-wrapper {
display: flex;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 10;
flex-direction: column;
justify-content: flex-end;
transition: transform 0.4s ease-in-out;
transform: translateY(100%);
}
.modal-inner {
position: static;
flex: 0 0 auto;
display: flex;
flex-direction: column;
background: #fff;
padding: 24px;
}
</style>
<div class="modal">
<div class="modal-mask"></div>
<div class="modal-wrapper">
<div class="modal-inner">
<!--
if this was a slot with form content, how
would an event listener for submit be added and
how would that same event listener be removed
in disconnected callback?
-->
<slot name="content"></slot>
<p>click on the mask to close the modal</p>
</div>
</div>
</div>
`
class DrawerModal extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.appendChild(template.content.cloneNode(true))
this.wrapper = this.shadowRoot.querySelector('.modal-wrapper')
this.mask = this.shadowRoot.querySelector('.modal-mask')
this.content = this.shadowRoot.querySelector('.modal-inner')
this.wrapper.addEventListener('click', this.closeModal)
this.content.addEventListener('click', this.stopPropagation)
}
connectedCallback() {
setTimeout(() => {
this.wrapper.style.transform = 'translateY(0%)'
this.mask.style.opacity = '1'
})
}
disconnectedCallback() {
this.wrapper.removeEventListener('click', this.closeModal)
this.content.removeEventListener('click', this.stopPropagation)
}
closeModal = e => {
this.wrapper.style.transform = 'translateY(100%)'
this.mask.style.opacity = '0'
setTimeout(() => {
this.remove()
}, 400)
}
stopPropagation = e => {
e.stopPropagation()
}
}
window.customElements.define('drawer-modal', DrawerModal)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>modal test</h1>
<button class="modal-btn">open modal</button>
<div id="app"></div>
</body>
</html>
Example 1
Here a form is being inserted into the modal drawer, this needs a submit event, can we add the submit event inside the drawer components connectedCallback and remove the event listener in the disconnectedCallback?
button.addEventListener('click', () => {
const modal = document.createElement('drawer-modal')
modal.innerHTML = `
<form slot="content">
<label for="email>email</label>
<input type="text" id="email">
</form>
`
document.querySelector('#app').appendChild(modal)
})
Example 2
Here a button is being inserted into the modal drawer and needs a click event listener.
button.addEventListener('click', () => {
const modal = document.createElement('drawer-modal')
modal.innerHTML = `
<button slot="content" class="btn">click me</button>
`
document.querySelector('#app').appendChild(modal)
})
Good day folks,
I am creating a to-do app, and so far when I enter the task in via the input the console shows my object firing but does not display it on the screen. Please look at my code and help me point out the issue, I have been debugging this for some time today.
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/to-doStylesheet.css">
<title>To-Do App</title>
</head>
<body>
<div class=container>
<div class= base>
<div class = screen>
<img src="images/WarGreymon_Render.png" alt="Wargreymon">
<div id ="speach-bubble"> What is on your
agenda for today?
</div>
<div class = "dateTime">
</div>
</div>
<div class = "nameInput" id = "inputContainer">
<form class ="form">
<input type="text" class ="userInput" placeholder="Add Agenda and press enter">
<input type="submit" value ="Add">
</form>
</div>
</div>
<ul class="list"></ul>
<script src="js/add-task.js"></script>
</body>
</html>
CSS
.form
{
margin-left: 400px;
}
.userInput
{
width: 394px;
height: 30px;
background-color: #B62626;
font-family: Digimon Basic;
color: #33D61A;
margin-left: -359px;
}
.userInput ::placeholder
{
color: #33D61A;
font-family: Digimon Basic;
}
.list:empty
{
display: none;
}
.list
{
list-style: none;
margin-bottom: 20px;
}
.input[type="checkbox"] {
display: none;
}
.tick {
width: 30px;
height: 30px;
border: 3px solid #333;
border-radius: 50%;
display: inline-flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.todo-item {
margin-bottom: 10px;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.todo-item span {
flex-grow: 1;
margin-left: 10px;
margin-right: 10px;
font-size: 22px;
}
JS
let tasks = [];
const currentdt = new Date()
function todo(text) {
const todo = {
text,
checked: false,
id: Date.now(),
timestamp: currentdt
};
tasks.push(todo);
console.log(tasks);
}
// Select the form element
const form = document.querySelector('.form');
// Add a submit event listener
form.addEventListener('submit', event => {
// prevent page refresh on form submission
event.preventDefault();
// select the text input
const input = document.querySelector('.userInput');
// Get the value of the input and remove whitespace
const text = input.value.trim();
if (text !== '') {
todo(text);
input.value = '';
input.focus();
}
});
//This function is to display new to do on the screen
function displaytasks(todo)
{
const list = document.querySelector('list');
const isChecked = todo.checked ? 'done': '';
const addedList = document.createElement("li");
addedList.setAttribute('class', `todo-item ${isChecked}`);
addedList.setAttribute('data-key', todo.timestamp);
addedList.innerHTML = `<input id="${todo.timestamp}" type="checkbox"/>
<label for="${todo.timestamp}" class="tick js-tick"></label>
<span>${todo.text}</span>
<button class="delete-todo js-delete-todo">
<img class = "delete" src="images/delete.png" alt="delete icon">
</button>`;
list.append(addedList);
}
So I am busy with the js file at the moment, I think it has to do something with the innerHTML, but I am not sure what exactly is wrong there, because when I look in the console on the HTML side I do not see the <ul class="list"></ul> firing at all to bring the new HTML elements.
Your help will be much appreciated
It looks like the code to display the todos is not being called, so I would recommend you add in a function call after reading in a new todo.
...
const text = input.value.trim();
if (text !== '') {
todo(text);
input.value = '';
input.focus();
displaytasks(tasks); // Show tasks after submission
}
});
//This function is to display new to do on the screen
function displaytasks(todo)
{
const list = document.querySelector('.list');
...
The program I am creating is a meme generator. I have a div container set up with display:grid with a few tags inside of that which act as the top and bottom text for the meme. I'm trying to dynamically set the background image for the grid cell using plain vanilla JS. When I attach the link inside the CSS file it works perfectly, but using JS the background-image is never set when i check inside the browser. I put a big arrow so you can see where I am attempting to set the image
const imageLink = document.querySelector('#imageLink');
const topText = document.querySelector('#topText');
const bottomText = document.querySelector('#bottomText');
const memeDiv = document.querySelector('#meme');
//listener for submit
document.addEventListener("submit", function(e) {
e.preventDefault();
//if the user doesn't enter an image, or if they don't enter any text, don't generate the meme when they submit.
if (imageLink.value === "") {
return;
} else if (topText === "" && bottomText === "") {
return;
}
console.log(imageLink.value);
//create elements
var div = document.createElement("div");
//set attribute for div containing our memes
div.setAttribute("id", "meme");
//When the page loads apply the users photo to the background of the grid
window.addEventListener('DOMContentLoaded', function() {
memeDiv.style.backgroundImage = `url(${imageLink.value})`; // < -- -- -
});
//create text and remove button for the memes
const top = document.createElement("p"); //for top text
const bottom = document.createElement("p"); //for bottom text
const removeBtn = document.createElement("input");
//remove button attributes
removeBtn.setAttribute("id", "remove");
removeBtn.setAttribute("type", "image");
removeBtn.setAttribute("height", "200px");
removeBtn.setAttribute("width", "200px");
removeBtn.setAttribute(
"src",
"https://www.freeiconspng.com/uploads/x-png-33.png"
);
//set attributes for text
top.setAttribute("id", "top");
top.innerText = topText.value;
bottom.setAttribute("id", "bottom");
bottom.innerText = bottomText.value;
//put the top and bottom text with the remove button together with the same div
div.appendChild(top);
div.appendChild(bottom);
div.appendChild(removeBtn);
//append to the div
document.querySelector("#memeContainer").appendChild(div);
//reset
imageLink.value = "";
topText.value = "";
bottomText.value = "";
})
document.addEventListener("click", function(e) {
if (e.target.id === "remove") {
e.target.parentElement.remove();
} else {
return;
}
})
* {
margin: 0px;
}
#formContainer {
background-color: blue;
margin-bottom: 5px;
text-align: center;
}
h1 {
text-align: center;
background-color: blue;
margin: 0px;
}
#memeContainer {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 300px);
grid-gap: 5px;
}
#top,
#bottom,
#remove {
position: relative;
display: inline;
}
#top {
left: 225px;
z-index: 1;
font-family: Impact;
font-size: 40px;
/* color:white; */
}
#bottom {
top: 300px;
left: 225px;
z-index: 2;
font-family: Impact;
font-size: 40px;
/* color:white; */
}
#remove {
top: -150px;
left: 180px;
z-index: 3;
/* filter: opacity(1%); */
}
#remove:hover {
z-index: 3;
filter: opacity(25%);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meme Generator</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<h1>MEME GENERATOR</h1>
<div id="formContainer">
<form>
<input id="imageLink" type="text" placeholder="please link to an image">
<input id="topText" type="text" placeholder="TOP TEXT">
<input id="bottomText" type="text" placeholder="BOTTOM TEXT">
<button type="submit">submit</button>
</form>
</div>
<div id="memeContainer"></div>
<script src="app.js"></script>
</body>
</html>
The DOMContentLoaded event is only called once whenever all HTML have been loaded. So you are only adding an event listener which never fires and thus nothing happens.
window.addEventListener('DOMContentLoaded', function() {
memeDiv.style.backgroundImage = `url(${imageLink.value})`;
});
Remove the event listener and correct the memeDiv variable name to div and your code will run.
div.style.backgroundImage = `url(${imageLink.value})`;
const imageLink = document.querySelector('#imageLink');
const topText = document.querySelector('#topText');
const bottomText = document.querySelector('#bottomText');
const memeDiv = document.querySelector('#meme');
//listener for submit
document.addEventListener("submit", function(e) {
e.preventDefault();
//if the user doesn't enter an image, or if they don't enter any text, don't generate the meme when they submit.
if (imageLink.value === "") {
return;
} else if (topText.value === "" && bottomText.value === "") {
return;
}
console.log(imageLink.value);
//create elements
var div = document.createElement("div");
//set attribute for div containing our memes
div.setAttribute("id", "meme");
//When the page loads apply the users photo to the background of the grid
div.style.backgroundImage = `url(${imageLink.value})`; // < -- -- -
//create text and remove button for the memes
const top = document.createElement("p"); //for top text
const bottom = document.createElement("p"); //for bottom text
const removeBtn = document.createElement("input");
//remove button attributes
removeBtn.setAttribute("id", "remove");
removeBtn.setAttribute("type", "image");
removeBtn.setAttribute("height", "200px");
removeBtn.setAttribute("width", "200px");
removeBtn.setAttribute(
"src",
"https://www.freeiconspng.com/uploads/x-png-33.png"
);
//set attributes for text
top.setAttribute("id", "top");
top.innerText = topText.value;
bottom.setAttribute("id", "bottom");
bottom.innerText = bottomText.value;
//put the top and bottom text with the remove button together with the same div
div.appendChild(top);
div.appendChild(bottom);
div.appendChild(removeBtn);
//append to the div
document.querySelector("#memeContainer").appendChild(div);
//reset
imageLink.value = "";
topText.value = "";
bottomText.value = "";
})
document.addEventListener("click", function(e) {
if (e.target.id === "remove") {
e.target.parentElement.remove();
} else {
return;
}
})
* {
margin: 0px;
}
#formContainer {
background-color: blue;
margin-bottom: 5px;
text-align: center;
}
h1 {
text-align: center;
background-color: blue;
margin: 0px;
}
#memeContainer {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 300px);
grid-gap: 5px;
}
#top,
#bottom,
#remove {
position: relative;
display: inline;
}
#top {
left: 225px;
z-index: 1;
font-family: Impact;
font-size: 40px;
/* color:white; */
}
#bottom {
top: 300px;
left: 225px;
z-index: 2;
font-family: Impact;
font-size: 40px;
/* color:white; */
}
#remove {
top: -150px;
left: 180px;
z-index: 3;
/* filter: opacity(1%); */
}
#remove:hover {
z-index: 3;
filter: opacity(25%);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meme Generator</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<h1>MEME GENERATOR</h1>
<div id="formContainer">
<form>
<input id="imageLink" type="text" placeholder="please link to an image">
<input id="topText" type="text" placeholder="TOP TEXT">
<input id="bottomText" type="text" placeholder="BOTTOM TEXT">
<button type="submit">submit</button>
</form>
</div>
<div id="memeContainer"></div>
<script src="app.js"></script>
</body>
</html>
I am trying to open a window and process the file in the calling JavaScript. I can pass the file name using localStorage but if I return the file I can't get it right.
I can't use this solution due to restrictions of the system I am calling the JavaScript from:
var fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');
fileSelector.click();
Can a file object be passed using localStorage or should I use another method?
My code is:
<!DOCTYPE html>
<html>
<script language="JavaScript">
function testInjectScript2(){
try {
var myhtmltext =
'<input type="file" id="uploadInput3" name=\"files[]" onchange=\'localStorage.setItem("myfile",document.getElementById("uploadInput3").files[0]);\' multiple />';
console.log("myhtmltext="+myhtmltext);
var newWin2 = window.open('',"_blank", "location=200,status=1,scrollbars=1, width=500,height=200");
newWin2.document.body.innerHTML = myhtmltext;
newWin2.addEventListener("unload", function (e) {
if(localStorage.getItem("myfile")) {
var f = localStorage.getItem("myfile");
alert ('in function.f='+f);
alert ('in function.f.name='+(f).name);
localStorage.removeItem("myfile");
}
});
} catch (err) {
alert(err);
}
}
</script>
<body>
<input type="button" text="testInjectScript2" onclick="testInjectScript2()" value="testInjectScript2" />
</body>
</html>
First of all, welcome to SO. If I get you right, you want to upload a file using a new window and get that file using localStorage onto your main page. This is a possible solution. However, please do also note that the maximum size of the localStorage can vary depending on the user-agent (more information here). Therefore it is not recommend to use this method. If you really want to do this, please have a look at the first snippet.
var read = document.getElementById("read-value"), open_win = document.getElementById("open-win"), win, p = document.getElementById("file-set");
open_win.addEventListener("click", function(){
win = window.open("", "", "width=200,height=100");
win.document.write(
'<input id="file-input" type="file"/>' +
'<script>' +
'var input = document.getElementById("file-input");' +
'input.addEventListener("change", function(){window.localStorage.setItem("file", input.files[0]);})'+
'<\/script>'
);
})
read.addEventListener("click", function(){
var file = window.localStorage.getItem("file");
if(file){
p.innerText = "file is set";
}else{
p.innerText = "file is not set";
}
})
<button id="open-win">Open window</button>
<br><br>
<!-- Check if file is set in localStorage -->
<button id="read-value">Check</button>
<p id="file-set" style="margin: 10px 0; font-family: monospace"></p>
<i style="display: block; margin-top: 20px">Note: This only works locally as SO snippets lack the 'allow same origin' flag. i.e. just copy the html and js into a local file to use it.</i>
However, why not use a more elegant solution:
Simply using a modal. When the input value changes you can simply close the modal and get the file value without all the hassle of a localStorage.
// Get the modal, open button and close button
var modal = document.getElementById('modal'),
btn = document.getElementById("open-modal"),
span = document.getElementById("close"),
input = document.getElementById("file-input"),
label = document.getElementById("input-label"), file;
// When the user clicks the button, open the modal
btn.addEventListener("click", function() {
modal.style.display = "block";
})
// When the user clicks on <span> (x), close the modal
span.addEventListener("click", function() {
modal.style.display = "none";
})
input.addEventListener("change", function(){
file = input.files[0];
modal.style.display = "none";
//Change value of the label for nice styling ;)
label.innerHTML = input.files[0].name;
//do something with your value
})
// When the user clicks anywhere outside of the modal, close it
window.addEventListener("click", function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
})
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 10px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
.modal h2 {
font-family: sans-serif;
font-weight: normal;
}
/* Modal Content */
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
/* The Close Button */
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
/* Input styles, added bonus */
.file-input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.file-input + label {
font-size: 1.25em;
font-weight: 700;
padding: 10px 20px;
border: 1px solid #888;
display: inline-block;
cursor: pointer;
font-family: sans-serif;
}
.file-input:focus + label,
.file-input + label:hover {
background-color: #f7f7f7;
}
<!-- Trigger/Open The Modal -->
<button id="open-modal">Open Modal</button>
<!-- The Modal -->
<div id="modal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<span id="close" class="close">×</span>
<h2><i>Upload a file?</i></h3>
<input id="file-input" name="file-input" class="file-input" type="file"/>
<label id="input-label" for="file-input">Upload a file</label>
</div>
</div>
Hope it helps! Let me know!
Cheers!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Ajax Auto Suggest</title>
<script type="text/javascript" src="jquery-1.2.1.pack.js"></script>
<script type="text/javascript">
var stringcount = 0;
var st = "";
var vv = "f";
function lookup2(e,inpstring)
{
lookup1(e.keyCode,inpstring);
}
function lookup1(j,inputstring)
{
var x= inputstring.length;
st = inputstring ;
if (inputstring.charAt(parseInt(x,10)-1) == " ")
{
stringcount = stringcount + 1;
}
else
{
var mySplitResult = inputstring.split(" ");
var stringtemp = "" ;
var w = 0;
for (w =0 ; w < stringcount ;w++)
{
stringtemp = stringtemp+ " "+ mySplitResult[w];
}
st = stringtemp;
lookup(mySplitResult[stringcount],inputstring);
}
}
function lookup(inputString,i) {
if(inputString.length == 0) {
// Hide the suggestion box.
$('#suggestions').hide();
} else {
$.post("rpc.php", {queryString: ""+inputString+"" }, function(data){
if(data.length >0) {
$('#suggestions').show();
$('#autoSuggestionsList').html(data);
}
});
}
} // lookup
function fill(thisValue) {
$('#inputString').val(st.substring(1,st.length)+" "+thisValue);
setTimeout("$('#suggestions').hide();", 200);
}
</script>
<style type="text/css">
body {
font-family: Helvetica;
font-size: 11px;
color: #000;
}
h3 {
margin: 0px;
padding: 0px;
}
.suggestionsBox {
position: relative;
left: 30px;
margin: 10px 0px 0px 0px;
width: 200px;
background-color: #212427;
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
border: 2px solid #000;
color: #fff;
}
.suggestionList {
margin: 0px;
padding: 0px;
}
.suggestionList li {
margin: 0px 0px 3px 0px;
padding: 3px;
cursor: pointer;
}
.suggestionList li:hover {
background-color: #659CD8;
}
</style>
</head>
<body>
<div>
<form>
<div>Type your county here:<br />
<input type="text" size="30" value="" id="inputString" onkeyup="lookup2(event,this.value);" onblur="" />
</div>
<div class="suggestionsBox" id="suggestions" style="display: none;">
<img src="upArrow.png" style="position: relative; top: -12px; left: 30px;" alt="upArrow" />
<div class="suggestionList" id="autoSuggestionsList"> </div>
</div>
</form>
</div>
</body>
</html>
This is the code i am using. The auto-suggestion box is accessed by clicking on the desired option. How can i scroll through the option by using the up/down keys of the keyboard and select an option by using enter?
It looks like (because you have not quoted the really important code) that your server side ajax endpoint returns an HTML unordered list and this is pasted into the suggestionList div. That's going to be my assumption. Your CSS allows for the hover pseudo-selector so mouse support looks good.
For keyboard support, you are going to have add an event handler for the keypress event, probably on the document. Add the handler when the suggestion box is displayed, remove it when it is dismissed.
The event handler will have to track the up and down arrow keys as well as enter. You will have to add and remove a special class (or maybe an id) on the li element that is currently selected, which means you will have to track how many elements there are to scroll through, and which one is the currently highlighted one. So, if you see the down arrow key, add one to the current index (if you're at the last one, ignore the key). Remove the special class from the li element you just left and add it to the new one (obviously style the class accordingly in your CSS). When the enter key is pressed you know which element is selected, so return it, or do what you want with it.