Diff between d3150166f8f288d96f4b7ed32bc7edbc5a7771c4 and 9069db30e6a07c7c3b534f1d9517aa07a5ab637d

Changed Files

File Additions Deletions Status
index.php +21 -2 modified
search ja sort table/.gitignore +2 -0 added
search ja sort table/LICENSE +21 -0 added
search ja sort table/README.md +87 -0 added
search ja sort table/scripts/searchTable.js +41 -0 added
search ja sort table/scripts/sortTable.js +109 -0 added
style2.css +53 -0 added

Full Patch

diff --git a/index.php b/index.php
index 465eea2..f4088d0 100755
--- a/index.php
+++ b/index.php
@@ -4,7 +4,7 @@
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Harjoittelupaikat</title>
-    <link rel="stylesheet" type="text/css" href="style.css">
+    <link rel="stylesheet" type="text/css" href="style2.css">
 </head>
 <body>
 	<?php include "header.php"; ?>
@@ -17,6 +17,7 @@
             <h2>Tässä on ensimmäinen (ja ainoa) taulukko</h2>
             <p>Tässä on taulukko</p>
             <div class="table-container">
+			<input class="searchInput" data-table-id="tr" type="search" placeholder="Search" aria-label="Search" aria-target="tr">  
 		<table>
 			<tr>
 				<th>Nimi</th>
@@ -28,6 +29,7 @@
 				<th>Status</th>
 				<th>Ruokaraha</th>
 				<th>Muuta</th>
+				<th>Muokkaus</th>
 			</tr>
 		<!-- Ota tiedot tiedot kannasta -->
  		<?php
@@ -35,6 +37,8 @@
 		?>
 	   </table> 
 	  </div>
+	  <script src="./search ja sort table/scripts/searchTable.js"></script>
+    <script src="./search ja sort table/scripts/sortTable.js"></script>
 	</section>
         <!--
         <aside>
@@ -48,6 +52,21 @@
         <hr>
         <p>&copy; Harjoittelupaikat 2025</p>
         <a href="index.html">Takaisin alkuun</a>
-    </footer>
+	</footer>
+	<script>
+		var inputs = document.querySelectorAll('input'); // get the input element
+		inputs.forEach((input) => {
+			if (input.type == "text")
+			{
+				console.log(input.value);
+				input.addEventListener('input', resizeInput); // bind the "resizeInput" callback on "input" event
+				resizeInput.call(input); // immediately call the function
+			}
+		});
+
+		function resizeInput() {
+			this.style.width = this.value.length + "ch";
+		}
+	</script>
 </body>
 </html>
diff --git a/search ja sort table/.gitignore b/search ja sort table/.gitignore
new file mode 100644
index 0000000..46b8baa
--- /dev/null
+++ b/search ja sort table/.gitignore
@@ -0,0 +1,2 @@
+.vscode/
+index.html
diff --git a/search ja sort table/LICENSE b/search ja sort table/LICENSE
new file mode 100644
index 0000000..d04e22e
--- /dev/null
+++ b/search ja sort table/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Rory Sullivan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/search ja sort table/README.md b/search ja sort table/README.md
new file mode 100644
index 0000000..3a7366b
--- /dev/null
+++ b/search ja sort table/README.md
@@ -0,0 +1,87 @@
+# Search and Sort HTML Tables
+
+A simple and easy way to add sorting and search/filter functionality to your
+HTML table. See
+[here](https://rory-sullivan.github.io/Search-and-Sort-HTML-Tables/) for a
+working example.
+
+## Features
+
+* Support for multiple tables on a single page
+* Icons indicating sort direction
+* Support for hidden sort values
+* Preserves previous sort
+
+Note that this package will not work with paginated tables, the whole table has
+to be visible on the page.
+
+## Installation and Usage
+
+Download the [latest version](https://github.com/Rory-Sullivan/Search-and-Sort-HTML-Tables/releases)
+into your project (unpack it wherever you keep your static files or packages).
+Add the following scripts to the page containing the table you wish to sort or
+search (make sure to add the local path to the scripts). Put the script
+somewhere after the table, usually just before the closing body tag.
+
+```html
+<script src="./LOCAL_PATH/scripts/searchTable.js"></script>
+<script src="./LOCAL_PATH/scripts/sortTable.js"></script>
+```
+
+Use the example table provided in the example folder to build up your table or,
+if you have and existing table follow the instructions bellow.
+
+### Sort Feature
+
+To make your table sortable add a class of `.sortTable` to the `<table>` element
+and give it an `id` if it does not have one already. Make sure you have a
+`<thead>` section, all the headers in this section will become clickable and
+will sort the table based on the relevant column. By default, the sort function
+sorts alphanumerically, if you would rather sort a column numerically add
+`data-type="number"` to the appropriate header tag.
+
+#### Custom sort values
+
+Sometimes you will want to sort based on a value other than that displayed in
+the table. For example if you have a status column with categories 'low',
+'medium' and 'high', sorting this alphabetically would not make sense. In
+this case you can add a `data-sort-value` attribute to the relevant `<td>`
+tags. Data with a sort value will be sorted based on that value instead of
+the inner text. In the example above we might assign a value of 1 to 'low', 2 to
+'medium' and 3 to 'high' so that they are sorted in a more sensible order.
+
+```html
+<td data-sort-value="1">Low</td>
+<td data-sort-value="2">Medium</td>
+<td data-sort-value="3">High</td>
+```
+
+### Search/Filter Feature
+
+Add a search input with a class of `.searchInput` as below. Set the
+`data-table-id` attribute to the id of the table you wish to search.
+
+```html
+<input class="searchInput" data-table-id="TABLE_ID" type="search" placeholder="Search" aria-label="Search" aria-target="TABLE_ID">      
+```
+
+### Multiple tables
+
+For multiple tables simply repeat the above steps for each table, making sure
+each table has a unique id.
+
+## Notes on the Implementation
+
+The sort function uses a simple bubble sort algorithm. While this is definitely
+not the fastest or most efficient algorithm for the job it has some advantages.
+Firstly the algorithm is small and easy to understand making the code base small
+and easy for an end user to jump in and see what is going on. Secondly it is
+very memory efficient, consuming virtually no extra memory above what it takes
+to store the table. Finally bubble sort is stable meaning it preserves the
+previous sort order of the elements.
+
+If a header is clicked multiple times in order to swap between ascending and
+descending order, the sort function is only implemented the first time.
+Subsequent sorts make a call to a reverse function which simply reverses the
+order of the table rather than sorting it again. This increases the efficiency
+of subsequent sorts.
diff --git a/search ja sort table/scripts/searchTable.js b/search ja sort table/scripts/searchTable.js
new file mode 100644
index 0000000..a7441e4
--- /dev/null
+++ b/search ja sort table/scripts/searchTable.js
@@ -0,0 +1,41 @@
+function searchTable (tableId, value) {
+  const filter = value.toLowerCase()
+  const table = document.getElementById(tableId)
+  const rows = table.getElementsByTagName('tr')
+
+  for (let i = 1; i < rows.length; i++) { // skip first row as this is the header row
+    const row = rows[i]
+    let cols = Array.from(row.getElementsByTagName('td'))
+    cols = cols.concat(Array.from(row.getElementsByTagName('th')))
+
+    let shouldHide = true
+
+    cols.forEach(col => {
+      const text = col.innerText
+      if (text.toLowerCase().indexOf(filter) > -1) {
+        shouldHide = false
+      }
+    })
+
+    if (shouldHide) {
+      row.style.display = 'none'
+    } else {
+      row.style.display = ''
+    }
+  }
+}
+
+function addSearchListeners (inputs) {
+  inputs.forEach(input => {
+    input.addEventListener('input', (event) => {
+      const target = event.target
+      const targetTableId = target.dataset.tableId
+      const value = target.value
+      searchTable(targetTableId, value)
+    })
+  })
+}
+
+const searchInputs = Array.from(document.getElementsByClassName('searchInput'))
+
+addSearchListeners(searchInputs)
diff --git a/search ja sort table/scripts/sortTable.js b/search ja sort table/scripts/sortTable.js
new file mode 100644
index 0000000..ab97823
--- /dev/null
+++ b/search ja sort table/scripts/sortTable.js
@@ -0,0 +1,109 @@
+function sortTable (tableId, col, type) {
+  const table = document.getElementById(tableId)
+  const rows = table.rows
+
+  let sorting = true
+
+  while (sorting) {
+    sorting = false
+
+    for (let i = 1; i < rows.length - 1; i++) {
+      let swap = false
+      let x = rows[i].querySelectorAll('th, td')[col]
+      let y = rows[i + 1].querySelectorAll('th, td')[col]
+
+      if (x.dataset.sortValue) {
+        x = x.dataset.sortValue
+        y = y.dataset.sortValue
+      } else {
+        x = x.innerHTML
+        y = y.innerHTML
+      }
+
+      if (type === 'number') {
+        x = Number(x)
+        y = Number(y)
+      }
+
+      if (x > y) {
+        swap = true
+      }
+
+      if (swap) {
+        rows[i].parentNode.insertBefore(rows[i + 1], rows[i])
+        sorting = true
+      }
+    }
+  }
+}
+
+function reverseTable (tableId) {
+  const table = document.getElementById(tableId)
+  const rows = table.rows
+  const lastRow = rows.length - 1
+
+  for (let i = 1; i < rows.length; i++) {
+    rows[i].parentNode.insertBefore(rows[lastRow], rows[i])
+  }
+}
+
+function getHeaders (table) {
+  const head = table.getElementsByTagName('thead')[0]
+  return Array.from(head.getElementsByTagName('th'))
+}
+
+function addSortListeners (tables) {
+  tables.forEach(table => {
+    const headers = getHeaders(table)
+    const tableId = table.id
+
+    for (let i = 0; i < headers.length; i++) {
+      const header = headers[i]
+      const columnNo = i
+      let sortType
+
+      if (header.dataset.type) {
+        sortType = header.dataset.type
+      } else {
+        sortType = ''
+      }
+
+      header.addEventListener('click', (event) => {
+        const target = event.target
+        const sorted = (target.dataset.sorted === 'true')
+
+        if (sorted) {
+          reverseTable(tableId)
+          const arrow = target.getElementsByTagName('span')[0]
+          if (arrow.innerHTML === '↑') {
+            arrow.innerHTML = '↓'
+          } else {
+            arrow.innerHTML = '↑'
+          }
+        } else {
+          headers.forEach(header2 => {
+            const arrow = header2.getElementsByTagName('span')[0]
+            if (arrow) {
+              if (header2.dataset.sorted === 'true') {
+                arrow.innerHTML = '•'
+              } else {
+                arrow.innerHTML = ''
+              }
+            } else {
+              header2.innerHTML += '<span></span>'
+            }
+            header2.dataset.sorted = false
+          })
+
+          sortTable(tableId, columnNo, sortType)
+          target.dataset.sorted = true
+          target.getElementsByTagName('span')[0].innerHTML = '↑'
+        }
+      })
+    }
+  })
+}
+
+const TABLES = Array.from(document.getElementsByClassName('sortTable'))
+
+addSortListeners(TABLES)
diff --git a/style2.css b/style2.css
new file mode 100644
index 0000000..7093bba
--- /dev/null
+++ b/style2.css
@@ -0,0 +1,53 @@
+/* Global background + text color */
+body {
+    background-color: black;
+    color: white; /* or use the animated color below */
+    margin: 0;
+    font-family: sans-serif;
+}
+
+/* Optional: animated RGB color loop for text & borders */
+@keyframes rgbLoop {
+    0% { color: rgb(255, 0, 0); border-color: rgb(255, 0, 0); }
+    33% { color: rgb(0, 255, 0); border-color: rgb(0, 255, 0); }
+    66% { color: rgb(0, 0, 255); border-color: rgb(0, 0, 255); }
+    100% { color: rgb(255, 0, 0); border-color: rgb(255, 0, 0); }
+}
+
+/* Apply to text and borders */
+body, table, th, td, nav, main, section, aside, footer {
+    animation: rgbLoop 6s linear infinite;
+}
+
+/* Table styles */
+table, th, td {
+    border: 1px solid;
+    border-collapse: collapse;
+}
+
+/* Scrollable table container */
+.table-container {
+    width: 100%;
+    overflow-x: auto;
+}
+
+/* Layout sections */
+nav {
+    text-align: right;
+}
+main {
+    width: 100vw; /* fixed typo: wv → vw */
+    display: flex;
+    flex-direction: row;
+    flex: 1;
+}
+section {
+    width: 100%;
+}
+aside {
+    text-align: right;
+    flex: 1;
+}
+footer {
+    text-align: center;
+}