I am building a basic search page in Flask.
When the page loads, I enter the search bar and get the results. Then when I click on the drop-down, I can only select 1st option, "Organization" & not the 2nd option "Domain".
Additionally, after the I select the 1st drop-down option, the backend query also gives me errors. Below are the console logs screenshots:
Errors
Relevant code snippets:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"/>
<link rel="stylesheet" href="templates/static/styles.css">
<script src="templates/static/script.js"></script>
<link rel="icon" type="image/x-icon" href="templates/images/favicon.ico">
<img src="templates/images/company.png" alt="Website Logo" class="logo">
<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>Search</title>
</head>
<body>
<h1 class="heading">TITLE</h1>
<div class="search-bar">
<!-- Dropdown Start -->
<div class ="dropdown">
<div id="drop-text" class ="dropdown-text">
<span id="span">Organization</span>
<i id="icon" class="fas fa-chevron-down"></i>
</div>
<ul id="list" class="dropdown-list">
<li id="search_term_1" class="dropdown-list-item" onclick="selectOption(this)">Organization</li>
<li id="search_term_2" class="dropdown-list-item" onclick="selectOption(this)">Domain</li>
</ul>
</div>
<!-- Dropdown End -->
<!-- Search box input start -->
<div class="search-box">
<input type="search" id="search-input" placeholder="Search..." />
<i class="fas fa-magnifying-glass" onclick="search()"></i>
</div>
</div>
<div id="resultContainer" class="floating-table">
<div id="spinner" class="loader"></div>
<div id="count"></div>
<button id="btn" onclick="exportToCsv()" title="Download data as CSV." class="btn"><i class="fa fa-download"></i> Download</button>
<div id="results" class="resultsTable"></div>
</div>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeModal()">×</span>
</div>
</div>
<div class="footer">
<p>© 2024 company, Inc. All rights reserved.</p>
</div>
</body>
</html>
Javascript
var searchResult = null;
document.addEventListener('DOMContentLoaded', function() {
let dropdownBtn = document.getElementById("drop-text");
let list = document.getElementById("list");
let icon = document.getElementById("icon");
let span = document.getElementById("span");
let input = document.getElementById("search-input");
let listItems = document.querySelectorAll(".dropdown-list-item");
// show dropdown list on click on dropdown btn
dropdownBtn.onclick = function(event){
event.preventDefault();
//rotate arrow icon
if(list.classList.contains('show')){
icon.style.rotate = "0deg";
}else{
icon.style.rotate = "-180deg";
}
list.classList.toggle("show");
}
//hide dropdown list when clicked outside dropdown btn
window.onclick = function(e){
if(
e.target.id !== "drop-text" &&
e.target.id !== "span" &&
e.target.id !== "icon"
){
list.classList.remove("show");
icon.style.rotate = "0deg";
}
};
for(item of listItems){
item.onclick = function(e){
// change dropdown btn txt on click on selected item
span.innerText = e.target.innerText;
// change input placeholder text and input id based on selected list item
if (e.target.innerText == "Organization") {
input.placeholder = "Search Organization...";
input.setAttribute("id", "search_term_1")
}
else if(e.target.innerText == "Domain") {
input.placeholder = "Search Domain...";
input.setAttribute("id", "search_term_2")
}
input.placeholder = "Search " + e.target.innerText + "..."
}
}
});
function formatDateToCustomString(date) {
const year = date.getFullYear();
const month = ('0' + (date.getMonth() + 1)).slice(-2);
const day = ('0' + date.getDate()).slice(-2);
const hours = ('0' + date.getHours()).slice(-2);
const minutes = ('0' + date.getMinutes()).slice(-2);
return `${year}${month}${day}${hours}${minutes}`;
}
const currentDate = new Date();
const formattedDate = formatDateToCustomString(currentDate);
const filename = `search_${formattedDate}`;
function selectOption(option) {
// Deselect all list items
document.querySelectorAll('.dropdown-list-item').forEach(item => {
item.classList.remove('active');
});
// Add the 'active' class to the clicked option
option.classList.add('active');
// Update search input placeholder and ID
const selectedText = option.innerText;
document.getElementById("search-input").placeholder = `Search ${selectedText}...`;
document.getElementById("search-input").setAttribute("id", `search_term_${selectedText === "Organization" ? 1 : 2}`);
// If "Domain" is selected, trigger search
if (selectedText === "Domain") {
search(); // Modify to handle domain search
}
}
function search() {
// Show the loading spinner
document.getElementById('spinner').style.display = 'block';
// Initialize search terms
var search_term_1 = null;
var search_term_2 = null;
var search_term_3 = null;
// Get the search input element
var input = document.getElementById("search-input");
console.log("Input element:", input);
// Dynamically capture search term based on selected dropdown option
var selectedOption = document.querySelector(".dropdown-list-item.active");
console.log("Selected option:", selectedOption);
if (selectedOption && selectedOption.innerText === "Organization") {
search_term_1 = input.value;
input.value = "";
} else if (selectedOption && selectedOption.innerText === "Domain") {
search_term_2 = input.value;
input.value = "";
} else if (selectedOption && selectedOption.innerText === "NA") {
search_term_3 = input.value;
input.value = "";
}
else {
// If no dropdown item is selected, assume Organization
search_term_1 = input.value;
input.value = ""; // Clear the input
}
// Make a POST request to your Flask server
fetch('/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ search_term_1: search_term_1, search_term_2: search_term_2 }),
})
.then(response => response.json())
.then(data => {
// Hide the loading spinner
document.getElementById('spinner').style.display = 'none';
// Display the count at the top
var countDiv = document.getElementById('count');
var totalCount = data.total_count ? data.total_count : 'unknown';
countDiv.innerHTML = `<p>${data.count} of ${totalCount} records found</p>`;
// Update the #results div with the received data
var resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = '';
// Create a table element
var table = document.createElement('table');
table.border = '1';
table.style.backgroundColor = '#ffffffcc';
// Specify the order of columns
var columnOrder = ['Base Domain', 'Serial Number', 'SAN', 'Org Name', 'Org Country', 'Valid From', 'Valid To', 'Product Type', 'CA Owner', ];
// Create table header row
var headerRow = table.insertRow();
columnOrder.forEach(columnName => {
var headerCell = document.createElement('th');
headerCell.innerHTML = columnName;
headerCell.style.color = 'lightblue';
headerRow.appendChild(headerCell);
});
// Create table rows with data
data.results.forEach(result => {
var row = table.insertRow();
Object.values(result).forEach(value => {
var cell = row.insertCell();
cell.innerHTML = value;
cell.style.fontSize = '12px';
});
});
// Append the table to the resultsDiv
resultsDiv.appendChild(table);
// Store the search result
searchResult = data;
// Show the download button
document.getElementById('btn').style.display = 'block';
})
.catch(error => {
// Hide the loading spinner in case of an error
document.getElementById('spinner').style.display = 'none';
console.error('Error:', error);
});
}
CSS
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500&display=swap");
:root {
--blue: #9ab3f5;
--purple: #9a1663;
--white: #ffffff;
--shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px 0px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
list-style-type: none;
font-family: Roboto-Light,sans-serif;
}
body {
font-family: Roboto-Light,sans-serif;
align-items: center;
justify-content: center;
margin: 0;
padding-left: 50px;
padding-right: 50px;
padding-top: 20px;
padding-bottom: 50px;
font-stretch: normal;
background-color: #e6eaec;
}
footer {
display: block;
bottom:0;
justify-content: center;
}
.search-bar {
display: flex;
align-content: center;
border-radius: 50px;
background-color: var(--white);
margin: 2rem auto;
width: 40%;
}
.dropdown{
position: relative;
width: 280px;
border-radius: 50px;
border: 1px solid var(--white);
background-color: #0074c3;
box-shadow: var(--shadow);
cursor: pointer;
}
.dropdown-text{
display: flex;
align-items: center;
justify-content: space-between;
font-size: 1rem;
font-weight: 500;
color: var(--white);
padding: 1rem 1.5rem;
}
.dropdown-list{
position: absolute;
top: 4rem;
left: 0;
width: 100%;
border-radius: 15px;
max-height: 0;
overflow: hidden;
background-color: var(--white);
transition: max-height 0.5s;
}
#list.show{
max-height: 300px;
}
.dropdown-list-item{
font-size: 0.9rem;
font-weight: 500;
padding: 0.9rem 0 1rem 1.5rem;
cursor: pointer;
transition: margin-left 0.2s ease,color 0.2s ease;
}
.dropdown-list-item:hover{
margin-left: 0.5rem;
color: #0074c3;
}
.search-box{
display: flex;
align-items: center;
padding-right: 1rem;
width: 100%;
color: #0074c3;
}
.search-box input{
padding: 1rem;
width: 100%;
font-size: 1rem;
font-weight: 500;
border: 0;
outline: 0;
}
.search-box i{
font-size: 1.3rem;
cursor: pointer;
}
.search-box input::placeholder{
font-size: 1rem;
font-weight: 500;
}
/* Darker background on mouse-over */
#btn:hover {
background-color: #037ed0;
color: white;
}
#btn {
display: none;
border: 2px;
border-radius: 5px;
height: 37px;
width: 9%;
background-color: #0074c3;
border: none;
color: white;
cursor: pointer;
font-size: 15px;
margin-right: -10px;
transition-duration: 50ms;
cursor: pointer;
}
#resultsLink {
position: absolute;
display: inline-block;
top: 45px;
right: 500px;
}
#count {
top: 45px;
position: relative;
}
.footer {
/* position: fixed; */
left: 0;
bottom: 0;
width: 100%;
text-align: center;
padding: 10px;
color: #6a6767; /* Match the text color from Digicert.com */
font-size: 14px; /* Adjust the font size as needed */
}
.heading {
text-align: center;
font-size: 50px;
margin-top: 33.5px;
color: #0074c3;
font-family: FjallaOne-Regular,sans-serif;
}
.floating-table {
/* margin: 20px; */
border-radius: 10px;
overflow: hidden;
position: relative;
}
.exportButton {
position: absolute;
top: 10px;
right: 10px;
}
.floating-table button {
border: 1px;
display: inline-block;
overflow: hidden;
color: white;
cursor: pointer;
position: absolute;
top: 40px;
right: 50px;
}
.resultsTable{
padding-top: 50px;
}
#resultsTable {
border-collapse: collapse;
width: 100%;
height: 100%;
}
#results th, #results td {
border: 1px solid #dddddd;
color: #333333;
text-align: left;
padding: 6px;
font-size: 10px;
}
#results th {
background-color: #f2f2f2 !important;
color: #0074c3 !important;
font-size: 106% !important;
font-weight: bold !important;
}
#results td {
font-size: 12px;
border-bottom: 1px solid #dddddd;
padding: 6px;
width: auto;
}
input[readonly] {
background-color: #f2f2f2; /* Light gray background */
color: #999; /* Gray text color */
cursor: not-allowed; /* Change cursor to not-allowed */
}
.loader {
border: 8px solid #f3f3f3;
border-top: 8px solid #0074c3;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 20px auto;
display: none;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.instruction-box {
margin-top: 14px;
position: absolute;
top: 11.15%;
bottom: 68%;
right: 21px;
transform: translateY(-50%);
background-color: rgba(255, 255, 255, 0.6);
padding: 4px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
max-width: 515px;
max-height: 515px;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100% !important;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
/* Modal content */
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 50% !important;
}
/* Close button */
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.logo {
display: inline-block;
margin-left: -25px;
/* margin-right: auto; */
width: 30%;
max-width: 180px;
height: auto;
padding-top: 20px;
position: absolute;
}
.form-container {
margin-top: 20px;
display: flex;
justify-content: center;
}
.form-container input[type=text] {
padding: 10px;
border: 1px solid #a2a0a0;
border-radius: 5px;
width: 30%;
height: 40px;
font-size: 17px;
margin-right: 10px;
}
.form-container input[type=text]:focus {
outline: none;
border-color: #0074c3;
}
.form-container input[type=text]::placeholder {
opacity: 0.4;
}
.form-container button {
border: 2px;
border-radius: 5px;
background-color: #0074c3;
color: white;
height: 40px;
width: 12%;
font-size: 17px;
margin-right: 6px;
cursor: pointer;
}
.form-container button:hover {
background-color: #0074c3;
}
/* Responsive layout - makes the menu and the logo stack on top of each other */
@media (max-width: 600px) {
.logo {
width: 100%;
}
.search-container {
flex-direction: column;
}
.search-container input[type=text], .search-container button {
width: 100%;
margin-top: 0;
}
.form-container {
flex-direction: column;
}
.form-container input[type=text], .form-container button {
width: 100%;
margin-top: 10px;
margin-right: 0;
}
}