I've been searching for this solution for quite some time, although I've been trying to find solutions in anything but JS (since I'm very unfamiliar with it).
I've stripped down some code so I can implement it.
Currently it has a search function which can filter based on the input in text you put in, but I'd like to create a link with preformatted text to use the same function.
So for instance, if I'd like to filter on "IS" without typing it, that there is a link/button one can use that uses the same function and give the same result. I can only find other JS examples that don't filter based on the input but only on specific criteria which, if it doesn't match exactly, returns without the other results.
This is the stripped code:
function myFunction() {
var input, filter, table, tr, td, i, txtValue;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[0];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
</style>
</head>
<body>
<table id="myTable">
<tr>
<th>Name</th>
<th>Country</th>
</tr>
<tr>
<td>Alfreds Futterkiste</td>
<td>Germany</td>
</tr>
<tr>
<td>Berglunds snabbkop</td>
<td>Sweden</td>
</tr>
<tr>
<td>Island Trading</td>
<td>UK</td>
</tr>
<tr>
<td>Koniglich Essen</td>
<td>Germany</td>
</tr>
<tr>
<td>Laughing Bacchus Winecellars</td>
<td>Canada</td>
</tr>
<tr>
<td>Magazzini Alimentari Riuniti</td>
<td>Italy</td>
</tr>
<tr>
<td>North/South</td>
<td>UK</td>
</tr>
<tr>
<td>Paris specialites</td>
<td>France</td>
</tr>
</table>
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search for names.." title="Type in a name">
</body>
</html>
You may have buttons next to the input for filtering the list. Each of them will call a common function filterBy(value) that will automatically populate the input text with the passed argument and will trigger its keyup event.
Here's a demo:
function myFunction() {
var input, filter, table, tr, td, i, txtValue;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[0];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
function filterBy(value){
const target = document.querySelector('#myInput');
target.value = value;
target.dispatchEvent(new Event('keyup'));
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
</style>
</head>
<body>
<table id="myTable">
<tr>
<th>Name</th>
<th>Country</th>
</tr>
<tr>
<td>Alfreds Futterkiste</td>
<td>Germany</td>
</tr>
<tr>
<td>Berglunds snabbkop</td>
<td>Sweden</td>
</tr>
<tr>
<td>Island Trading</td>
<td>UK</td>
</tr>
<tr>
<td>Koniglich Essen</td>
<td>Germany</td>
</tr>
<tr>
<td>Laughing Bacchus Winecellars</td>
<td>Canada</td>
</tr>
<tr>
<td>Magazzini Alimentari Riuniti</td>
<td>Italy</td>
</tr>
<tr>
<td>North/South</td>
<td>UK</td>
</tr>
<tr>
<td>Paris specialites</td>
<td>France</td>
</tr>
</table>
<div class="filter-container">
<input
type="text"
id="myInput"
onkeyup="myFunction()"
placeholder="Search for names.."
title="Type in a name">
<button type="button" onclick="filterBy('IS');">IS</button>
</div>
</body>
</html>
One approach – with explanatory comments in the code – is below:
// to save typing, I cached a reference to the document element:
const D = document,
// and created utility functions to reduce the amount of typing of 'document.querySelector...',
// these work as simple aliases for the querySelector(All) functions, though getAll() does return
// an Array instead of a NodeList, this may break expectations:
get = (selector, context = D) => context.querySelector(selector),
getAll = (selector, context = D) => [...context.querySelectorAll(selector)],
// again, to reduce typing document.createElement(...):
create = (tag) => D.createElement(tag),
// named function to create the tag-cloud of <button> elements with which you want to filter
// the <table>; this function accepts an opts Object to be passed in:
createTagCloud = (opts = {}) => {
// the default settings of the function:
let settings = {
// the element from which the tags will be formed from its contents:
tagSource: get('table tbody'),
// the class-name to add to the element containing the tag-cloud:
wrapperClass: 'tagCloud',
// the first required argument:
// this argument must reference an existing element-node in the document, which will
// contain the tag-cloud:
outputNode: null,
// the final required argument:
// this argument takes a function that is to be called when clicking on the created
// tag <button> elements:
execute: null,
},
// creating a <button> element:
button = create('button'),
// creating a document-fragment:
fragment = D.createDocumentFragment(),
// defining two variables for later use:
wrapper, clone;
// using the Object.keys() method to return an Array of the keys of the opts Object (which
// requires a minimum of two properties), and iterating over that Array using
// Array.prototype.forEach():
Object.keys(opts).forEach(
// using an Arrow function, which passes one argument (a reference to the current key
// of the Array of keys over which we're iterating) into the function body; in the
// body of the function we're updating the property of the settings Object to be
// equal to the property-value of the opts Object:
(key) => settings[key] = opts[key]
);
// if we don't have a
if (!settings.outputNode) {
// here we log a message to the console (I chose not to throw an Error because I didn't want to
// completely break things):
console.log(`%c Error: %c "outputNode" must be passed to the function, and specify a parent element into which the tag-cloud is to be inserted.`, "color: black; background-color: red; border-radius: 5px; font-weight: bold;", "all: unset;")
return false;
}
// passing a value to the wrapper variable, so that it becomes a reference to the
// DOM element-node which will hold the tag-cloud:
wrapper = settings.outputNode;
// if there is no settings.execute property-value:
if (!settings.execute) {
// we log the following message to the console:
console.log(`%c Error: %c "execute" must specify a function to call in order to perform a search using the created tags..`, "color: black; background-color: red; border-radius: 5px; font-weight: bold;", "all: unset;")
}
// here we retrieve the text-content of the element which will serve as the 'haystack' to be searched, trim
// leading/trailing white-space from that string of text and then split it on a sequence of one or more
// white-spaces using String.prototpe.split() with a regular expression:
let words = settings.tagSource.textContent.trim().split(/\s+/);
// we iterate over the Array of words, using Array.prototype.forEach():
words.forEach(
// with an Arrow function that passes in a reference to the current word of the Array
// of words over which we're iterating:
(word) => {
// we clone the <button> we created earlier, along with any descendants:
clone = button.cloneNode(true);
// we bind the named function - the property-value of the 'settings.execute' property that
// was passed into the function - as the event-handler for the 'click' event:
clone.addEventListener('click', settings.execute);
// we set the value-property of the cloned <button>, and its text-content, to
// the curent word:
clone.value = word;
clone.textContent = word;
// we then append that cloned <button> to the document-fragment:
fragment.append(clone);
});
// if a class-name was passed in to be assigned to the wrapper element:
if (settings.wrapperClass) {
// we use the Element.classList API to add that class:
wrapper.classList.add(settings.wrapperClass);
}
// here we append the document-fragment to the wrapper:
wrapper.append(fragment);
},
// the named function to handle the filtering and toggling of the <tr> elements
// of the <table>; this takes on argument passed automatically from the later
// use of the EventTarget.addEventListener() function:
find = (evt) => {
// we retrieve the value of the <button> that was fired:
let needle = evt.currentTarget.value,
// and we retrieve the <tr> elements from the <tbody> of the <table>:
haystack = getAll('table tbody tr');
// we use Array.prototype.forEach() to iterate over those <tr> elements:
haystack.forEach(
// passing a reference to the current <tr> to the function, in which
// we again use the Element.classList API to toggle the 'hidden' class
// on the current <tr> ('row').
// if the current row does *not* include the current word ('needle'),
// we invert the false (from String.prototype.includes()) using the
// NOT operator ('!') which means the assessment becomes truthy which
// in turn means that the class will be added to those elements which
// do not contain the current word; this causes them to be hidden.
// If the current <tr> does contain the 'needle', then the true (from
// String.prototype.includes()) is inverted to false, so the class-name
// is *not* added:
(row) => row.classList.toggle('hidden', !row.textContent.includes(needle))
)
};
// calling the function:
createTagCloud({
// passing in the required arguments, first the outputNode,
// which is the <div> following the <table>:
outputNode: get('table + div'),
// and a reference to the function to be called when the
// tags are clicked:
execute: find,
});
*,
::before,
::after {
box-sizing: border-box;
font-family: Roboto, Montserrat, system-ui;
font-size: 16px;
font-weight: 400;
line-height: 1.4;
margin: 0;
padding: 0;
}
main {
border: 1px solid #000;
display: flex;
margin-block: 1em;
margin-inline: auto;
padding: 0.5em;
width: 70vw;
}
main>* {
border: 1px solid currentColor;
flex-basis: 50%;
flex-grow: 1;
}
table {
border-collapse: collapse;
}
thead th {
border-block-end: 0.1em solid currentColor;
}
tbody tr {
background-color: #fff;
}
tbody tr:nth-child(odd) {
background-color: lightblue;
}
td {
padding-block: 0.25em;
padding-inline: 0.5em;
}
button {
padding-block: 0.25em;
padding-inline: 0.5em;
}
.tagCloud {
display: flex;
align-content: start;
flex-flow: row wrap;
gap: 0.5em;
justify-content: space-between;
padding: 0.5em;
}
.hidden {
opacity: 0.2;
}
<!-- wrapped all content in a <main> tag to identify the main content of the site: -->
<main>
<table id="myTable">
<!-- used a <thead> element to wrap the header rows that contain the <th> elements: -->
<thead>
<tr>
<th>Name</th>
<th>Country</th>
</tr>
</thead>
<!-- using a <tbody> to wrap the content of the <table> to make it easier to create the
tag-cloud: -->
<tbody>
<tr>
<td>Alfreds Futterkiste</td>
<td>Germany</td>
</tr>
<tr>
<td>Berglunds snabbkop</td>
<td>Sweden</td>
</tr>
<tr>
<td>Island Trading</td>
<td>UK</td>
</tr>
<tr>
<td>Koniglich Essen</td>
<td>Germany</td>
</tr>
<tr>
<td>Laughing Bacchus Winecellars</td>
<td>Canada</td>
</tr>
<tr>
<td>Magazzini Alimentari Riuniti</td>
<td>Italy</td>
</tr>
<tr>
<td>North/South</td>
<td>UK</td>
</tr>
<tr>
<td>Paris specialites</td>
<td>France</td>
</tr>
</tbody>
</table>
<!-- added a <div> to contain the created tag-cloud: -->
<div></div>
</main>
JS Fiddle demo.
References:
Array.prototype.forEach().
console API.
document.createElement().
document.createDocumentFragment().
document.querySelector().
document.querySelectorAll().
Element.append().
Element.classList API.
Element.querySelector().
Element.querySelectorAll().
Event.currentTarget.
EventTarget.addEventListener().
Node.cloneNode().
Node.textContent.
Object.keys().
String.prototype.includes().
String.prototype.split().
String.prototype.trim().
Related
I have a table dynamically built like this:
<table>
<colgroup>
<col/>
<col/>
</colgroup>
<tr>
<th>#</th>
<th>Name</th>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-2</td>
</tr>
<tr>
<td>☐</td>
<td>piece-of-data-3</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-4</td>
</tr>
</table>
☐ indicates an empty ballot box (☐) whereas ☑ indicate a checked ballot box (☑).
The table is produced by PowerShell's ConvertTo-Html function which prevents using ids - for example - to differentiate specific rows or columns. Also, the PS scripts are used in environments with no internet connectivity which limits me to use only native JavaScript (no jQuery or such, no way to place .js file alongside either). How can I color the entire tr red based on the first td in that row having an empty ballot box? Example of the desired outcome: https://jsfiddle.net/b1pgezfm/
Traversing the DOM is basic Javascript - in your example, you'd check each <td> element for the content you're looking for. If you find it, grab its parent <tr> element and change the parent's background color.
const rows = document.querySelectorAll('td');
rows.forEach((row) => {
if (row.innerHTML === '☐') {
const parent = row.parentNode;
parent.style.backgroundColor = 'red';
}
});
<table>
<colgroup>
<col/>
<col/>
</colgroup>
<tr>
<th>#</th>
<th>Name</th>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-2</td>
</tr>
<tr>
<td>☐</td>
<td>piece-of-data-3</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-4</td>
</tr>
</table>
My first thought was also: Javascript. But if you have the controll over your DOM, you can also do it with pure CSS. Like here:
tr[class*="isUnchecked"] {
background: #ffff00;
}
<table>
<colgroup>
<col/>
<col/>
</colgroup>
<tr>
<th>#</th>
<th>Name</th>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-2</td>
</tr>
<tr class="isUnchecked">
<td>☐</td>
<td>piece-of-data-3</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-4</td>
</tr>
</table>
One approach I could suggest is the following:
// using a named function to perform the highlighting,
// supplying default values for the available options
// (these are, of course, customisable by passing in
// new property-values to the function):
const highlight = (opts = {
// String, CSS selector to identify the elements
// from which highlighting should be determined:
elementSelector: 'td:first-child',
// String, CSS selector to identify the element
// to which highlighing should be applied:
ancestorSelector: 'tr',
// Number, the unicode character code of the character
// that should lead to highlighting being present:
characterCode: 9744,
// String, the class to apply to the ancestor to
// show highlighting:
highlightClass: 'highlight'
}) => {
// retrieving the property-values from the opts Object,
// to use those values within the function (without having
// to preface with the 'opts' Object-name, so we can use
// 'elementSelector' instead of 'opts.elementSelector'):
const {
elementSelector,
ancestorSelector,
characterCode,
highlightClass
} = opts;
// here we select all elements in the document, using
// document.querySelectorAll() with the elementSelector
// variable; then we use NodeList.prototype.forEach()
// to iterate over that NodeList, using an Arrow function:
document.querySelectorAll(elementSelector).forEach(
// here we pass in a reference to the current element/node
// of the NodeList over which we're iterating, and
// and navigate from that element to its ancestor using
// the CSS selector passed to the function:
(el) => el.closest(ancestorSelector)
// here we use the Element.classList API to toggle a
// class-name on that element:
.classList.toggle(
// we toggle the class-name that was passed to the function:
highlightClass,
// if this assessment evaluates to true/truthy the class-name
// is applied, if it evaluates to false/falsey it is removed;
// in the assessment we retrieve the textContent of the current
// element, remove the leading/trailing whitespace (using
// String.prototype.trim()) and then retrieve the character code
// of the character at the zeroth (first) position in the String.
// that character-code is then compared to see if it is
// exactly-equal to the character code passed in to the function:
el.textContent.trim().charCodeAt(0) === characterCode)
)
};
// calling the function:
highlight();
*,
::before,
::after {
box-sizing: border-box;
font: 1rem / 1.5 sans-serif;
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
}
.highlight {
background-color: red;
}
td:last-child {
padding-inline-start: 1em;
}
<table>
<colgroup>
<col />
<col />
</colgroup>
<tr>
<th>#</th>
<th>Name</th>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-2</td>
</tr>
<tr>
<td>☐</td>
<td>piece-of-data-3</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-4</td>
</tr>
</table>
JS Fiddle demo.
If, however, you'd rather use a function to retrieve the elements to be highlighted and then use another function to apply that highlighting, the following can be used:
// defining named functions the retrieve() function
// taking optional arguments as before (in the previous
// highlight() function):
const retrieve = (opts = {
elementSelector: 'td:first-child',
characterCode: 9744
}) => {
const {
elementSelector,
ancestorSelector,
characterCode,
highlightClass
} = opts;
// here we use Array.from() to convert the NodeList returned
// from document.querySelector() into an Array, in order to
// use Array.prototype.filter() to filter the collection of
// Nodes:
return Array.from(
document.querySelectorAll(elementSelector)
// within Array.prototype.filter() we use an Arrow function to:
).filter(
// to trim the text-content of the current element of the Array
// of elements of its leading/trailing white-space, and then
// establish if the zeroth character is exactly-equal to the
// characterCode passed to the function; a true/truthy result
// retains the current array-element in the new array returned,
// and a false/falsey value leads to the array-element being
// discarded:
(el) => el.textContent.trim().charCodeAt(0) === characterCode
);
},
// the function expects an iterable collection ('collection'),
// and also allows custom options to be passed in by the user
// (as in the previous code/demo):
highlight = (collection, opts = {
'ancestor': 'tr',
'highlightClass': 'highlight'
}) => {
// here we convert the iterable collection into an Array, using
// Array.from(), and then use Array.prototype.forEach() to iterate
// over that collection:
Array.from(collection).forEach(
// again, using an Arrow function we navigate to the closest ancestor
// element that matches the supplied CSS selector, and we add the
// supplied highlightClass-name to apply that class to the
// matched ancestor element:
(el) => el.closest(opts.ancestor).classList.add(opts.highlightClass)
);
};
// calling the functions:
highlight(retrieve());
*,
::before,
::after {
box-sizing: border-box;
font: 1rem / 1.5 sans-serif;
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
}
.highlight {
background-color: red;
}
td:last-child {
padding-inline-start: 1em;
}
<table>
<colgroup>
<col />
<col />
</colgroup>
<tr>
<th>#</th>
<th>Name</th>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-2</td>
</tr>
<tr>
<td>☐</td>
<td>piece-of-data-3</td>
</tr>
<tr>
<td>☑</td>
<td>piece-of-data-4</td>
</tr>
</table>
JS Fiddle demo.
References:
Arrow functions.
Array.from().
Array.prototype.filter().
Array.prototype.forEach().
document.querySelectorAll().
Element.classList API.
Element.closest().
NodeList.prototype.forEach().
Object destructuring: {variable1,variable2} = Object.
String.prototype.charCodeAt().
String.prototype.trim().
I'm trying to move an HTML element to a specific location within a table. For example, I have a disabled button labeled "abcd" and a table with cell value of "xyz". I want to move the button "abcd" on top of the cell with the value "xyz" by referencing that value.
So far, the code I have for the javascript function looks like this:
<script type="text/javascript">
function moveObject() {
var label = prompt("Please enter object to move", "");
var location = prompt("Please enter cell location", "");
var element = document.getElementById(label);
}
</script>
How do I reference the cell value so that I can tell the object to move there?
To achieve expected result, use below option using document.getElementsByTagName and prepend to add button to cell value
Get all tds using document.getElementsByTagName('TD')
Loop all td elements using for of
Look for cell with text xyz and prepend disabled button- abcd
function moveObject() {
let tds = document.getElementsByTagName('TD')
let btn = document.getElementById('abcd')
for(let cell of tds){
if(cell.innerHTML ==='xyz'){
cell.prepend(btn)
}
}
}
table tr td{
border: 1px solid black;
}
<table>
<tr>
<td>test</td>
<td>xyz</td>
</tr>
</table>
<button id="abcd" disabled>abcd</button>
<button onclick="moveObject()">Move</button>
Say you have the following structure for a html table:
<table id="myTable">
<tr>
<td>Cell A</td>
<td>Cell B</td>
</tr>
<tr>
<td>Cell C</td>
<td>Cell D</td>
</tr>
</table>
As you can see every cell is a <td> element.
Since our table has an unique id - myTable - we can get all it's TD elements by calling:
var cells=document.getElementById("myTable").getElementsByTagName("td");
The variable cells holds a html collection of all the TD elements of myTable but what we are really interested in is the actual content of a cell - so we need to loop over this array and refer to each cells content using .firstChild.data.
If we compare this to a string we can see which cell matches.
Here's an example:
function getCell(myString) {
var cells = document.getElementById("myTable").getElementsByTagName("td");
for (var a = 0; a < cells.length; a++) {
if (cells[a].firstChild.data == myString) {
return cells[a];
}
}
return null;
}
console.log(getCell("Cell C"));
<table id="myTable">
<tr>
<td>Cell A</td>
<td>Cell B</td>
</tr>
<tr>
<td>Cell C</td>
<td>Cell D</td>
</tr>
</table>
prompt() is a crappy interface. Do this instead:
Wrap everything in a block level element (div, section, main, etc), we'll refer to it as the "parent"
Register the parent to the click event either by on-event property:
parent.onclick = callback
or by Event Listener:
parent.addEventListener('click', callback)
now the parent will detect all clicks on anything within the parent.
Use event.target to determine what was clicked. No id, class, or tagName is needed to identify event.target, but use .matches() to narrow down event.target in if/else control.
function addButton(event) {
const clicked = event.target;
const buttons = this.querySelectorAll('button');
const prompt = this.querySelector('legend');
if (clicked.matches('button')) {
if (clicked.matches('.selected')) {
clicked.classList.remove('selected');
prompt.textContent = 'Click a Button';
prompt.style.color = 'black';
} else {
for (let button of buttons) {
button.classList.remove('selected');
}
clicked.classList.add('selected');
prompt.textContent = 'Click a Cell';
prompt.style.color = 'red';
}
} else if (clicked.matches('td')) {
let button = this.querySelector('.selected');
if (button) {
clicked.appendChild(button);
} else {
return false;
}
} else {
return false;
}
}
document.querySelector('main').onclick = addButton;
table {
table-layout: fixed;
border-spacing: 3px;
width: 100%;
}
th,
td {
width: 33%;
border: 1px solid #000;
padding: 1px
}
th::before,
td::before {
content: '\a0';
}
legend {
font-size: 1.75rem
}
button {
display: inline-block;
margin: 0 5px;
line-height: 100%;
}
.selected {
color: red;
outline: 2px solid red;
}
<main>
<table>
<thead>
<tr><th>A</th><th>B</th><th>C</th></tr>
</thead>
<tbody>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</tbody>
</table>
<fieldset>
<legend>Click a Button</legend>
<button>X</button>
<button>X</button>
<button>X</button>
<button>X</button>
</fieldset>
</main>
I am admittedly very inexperienced, but I'm trying to enhance the workflow at my company. I'm using the code from here to create internal site to search for current part numbers. My employees are searching by description, but the javascript included in the example searches by exact input instead of individual words.
For example, using the example code on the site, I would like a search for "Trading Island" to return the same result as "Island Trading". I know this is possible, but can't figure out how to make it work.
<!DOCTYPE html>
<html>
<head>
<style>
* {
box-sizing: border-box;
}
#myInput {
background-image: url('/css/searchicon.png');
background-position: 10px 10px;
background-repeat: no-repeat;
width: 100%;
font-size: 16px;
padding: 12px 20px 12px 40px;
border: 1px solid #ddd;
margin-bottom: 12px;
}
#myTable {
border-collapse: collapse;
width: 100%;
border: 1px solid #ddd;
font-size: 18px;
}
#myTable th, #myTable td {
text-align: left;
padding: 12px;
}
#myTable tr {
border-bottom: 1px solid #ddd;
}
#myTable tr.header, #myTable tr:hover {
background-color: #f1f1f1;
}
</style>
</head>
<body>
<h2>My Customers</h2>
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search for names.." title="Type in a name">
<table id="myTable">
<tr class="header">
<th style="width:60%;">Name</th>
<th style="width:40%;">Country</th>
</tr>
<tr>
<td>Alfreds Futterkiste</td>
<td>Germany</td>
</tr>
<tr>
<td>Berglunds snabbkop</td>
<td>Sweden</td>
</tr>
<tr>
<td>Island Trading</td>
<td>UK</td>
</tr>
<tr>
<td>Koniglich Essen</td>
<td>Germany</td>
</tr>
<tr>
<td>Laughing Bacchus Winecellars</td>
<td>Canada</td>
</tr>
<tr>
<td>Magazzini Alimentari Riuniti</td>
<td>Italy</td>
</tr>
<tr>
<td>North/South</td>
<td>UK</td>
</tr>
<tr>
<td>Paris specialites</td>
<td>France</td>
</tr>
</table>
<script>
function myFunction() {
var input, filter, table, tr, td, i;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[0];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
</script>
</body>
</html>
When searching for word matches, it's best to place the search string
into an array and the cell contents to be searched into an array.
Then you can easily use the Array.prototype.every() or the Array.prototype.some() methods to see if all or some of the search words are present in the cell.
See my comments inline below for additional details.
// Get a reference to the input
var searchElement = document.getElementById("myInput");
// Hook element up to event handler:
searchElement.addEventListener("keyup", search);
// Event handler:
function search(){
// Now, break the search input into an array of words by splitting the string where
// there are spaces (regular expression used here to locate the strings).
// Also, note that strings are case-sensitive, so we are taking the input and
// forcing it to lower case and we'll match by lower case later.
var searchWords = searchElement.value.toLowerCase().split(/\s+/);
// Test to see the individual words
//console.clear();
//console.log(searchWords);
// Now you have to have some content to search against.
// So, we'll get all the cells, which will be our data source:
var theCells = document.querySelectorAll("td");
// Loop over each cell
theCells.forEach(function(cell){
// Reset any previous cell matches
cell.style.backgroundColor = "inherit";
// Get the words in the cell as an array (note: we are forcing lower
// case again here to match lower case against lower case
var cellWords = cell.textContent.toLowerCase().split(/\s+/);
// See if the cell contains all of the search words (You can substitute
// "some" for "every" to just see if the cell contains some of the search words
var result = cellWords.every(elem => searchWords.indexOf(elem) > -1);
// every and some return a boolean letting you know if your condition was met:
if(result){
console.clear();
console.log("Match found in: " + cell.textContent);
cell.style.backgroundColor = "#ff0";
}
});
}
<input type="text" id="myInput" placeholder="Search for names.." title="Type in a name">
<table id="myTable">
<tr class="header">
<th style="width:60%;">Name</th>
<th style="width:40%;">Country</th>
</tr>
<tr>
<td>Alfreds Futterkiste</td>
<td>Germany</td>
</tr>
<tr>
<td>Berglunds snabbkop</td>
<td>Sweden</td>
</tr>
<tr>
<td>Island Trading</td>
<td>UK</td>
</tr>
<tr>
<td>Koniglich Essen</td>
<td>Germany</td>
</tr>
<tr>
<td>Laughing Bacchus Winecellars</td>
<td>Canada</td>
</tr>
<tr>
<td>Magazzini Alimentari Riuniti</td>
<td>Italy</td>
</tr>
<tr>
<td>North/South</td>
<td>UK</td>
</tr>
<tr>
<td>Paris specialites</td>
<td>France</td>
</tr>
</table>
I am making a form to add players into an event.
The form searches a db of players with search criteria specified by the user and then lists all matching players with an add button next to them.
I also have a table with all the table headers done and then a
<div id="PlayerAdded">
tag before the end of the table.
I have written a function to output the data for the next row to the table when a players "Add" button is clicked. My function says:
function add(){
document.getElementById("PlayerAdded").innerHTML += "<tr><td>success</td></tr>";
}
I expected this to add a row, but instead it adds just the word "success" above the table (Perhaps I was a little optimistic when I used the word success as my test string lol).
Can someone please tell me why it is not adding the code inside the div "PlayerAdded"?
If it helps, here is some of the HTML:
<table border='1px'>
<tr><th colspan='6'> <?php echo ($eName . " - " . $vn); ?></th></tr>
<tr><th>Player ID</th>
<th>Player Name</th>
<th>Place</th>
<th>Points</th>
<th>Cash</th>
<th>Ticket?</th></tr>
<div id="PlayerAdded"> </div>
<tr><td colspan='3'>Search <input type='text' id='newsearch'></input>
</table>
There were a couple of problems with your existing HTML - which therefore broke your DOM when the browser attempted to assemble things.
a <div> element – in a <table> – must be contained within either a <th> or <td> element; no other element is a valid child of a <tr> element, and the only valid children of a <table> element are <thead>, <tfoot>, <tbody> and <tr> elements.
neither your last <tr>, or its child <td>, element were closed – the browser will automatically close these elements when it encounters another <td> (since neither a <td>, nor a <tr>, can be directly nested within another <td>).
That said, I'd correct your HTML to the following:
<table>
<tbody>
<tr>
<th colspan='6'>« php response »</th>
</tr>
<tr>
<th>Player ID</th>
<th>Player Name</th>
<th>Place</th>
<th>Points</th>
<th>Cash</th>
<th>Ticket?</th>
</tr>
<tr>
<td colspan='3'>Search
<input type='text' id='newsearch' />
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<button id="addNewRow">Add a new row</button>
And your JavaScript to the following:
function addNewRow() {
// creating the relevant elements to be added:
var row = document.createElement('tr'),
td = document.createElement('td');
// setting the text of the created-<td> element:
td.textContent = 'Success';
// setting the colSpan property (the colspan attribute):
td.colSpan = '6';
// adding a class-name to the created-<td>, to make it
// visually obvious which are the newly-added <td>
// elements:
td.classList.add('addedRow');
// appending the created-<td> to the created-<tr>:
row.appendChild(td);
// finding the last <tr> of the table, using
// document.querySelector() which will match
// only the first element that matches the
// supplied CSS selector (or null, if no
// element exists that matches):
var lastRow = document.querySelector('table tr:last-child');
// inserting the created-<tr> (and its descendant
// elements parentNode of the lastRow node before
// the lastRow node):
lastRow.parentNode.insertBefore(row, lastRow);
}
// using unobtrusive JavaScript to add the 'click'
// event-listener to the <button> element with the
// id of 'addNewRow':
document.getElementById('addNewRow').addEventListener('click', addNewRow);
function addNewRow() {
var row = document.createElement('tr'),
td = document.createElement('td');
td.textContent = 'Success';
td.colSpan = '6';
td.classList.add('addedRow');
row.appendChild(td);
var lastRow = document.querySelector('table tr:last-child');
lastRow.parentNode.insertBefore(row, lastRow);
}
document.getElementById('addNewRow').addEventListener('click', addNewRow);
table,
td,
th {
border: 1px solid #000;
min-height: 2em;
}
td.addedRow {
font-weight: bold;
text-align: center;
border-color: limegreen;
}
<table>
<tbody>
<tr>
<th colspan='6'>« php response »</th>
</tr>
<tr>
<th>Player ID</th>
<th>Player Name</th>
<th>Place</th>
<th>Points</th>
<th>Cash</th>
<th>Ticket?</th>
</tr>
<tr>
<td colspan='3'>Search
<input type='text' id='newsearch' />
</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<button id="addNewRow">Add a new row</button>
External JS Fiddle demo, for experimentation or development.
References:
document.createElement().
document.getElementById().
document.querySelector().
Element.classList.
EventTarget.addEventListener().
Node.appendChild().
Node.insertBefore().
Try doing as user #Barmar said:
<script type="text/javascript">
function add(){
var _tr = document.createElement("tr");
var _textNode = document.createTextNode("Success");
_tr.appendChild(_textNode);
var _child = document.getElementById("botTr");
var parentDiv = _child.parentNode;
parentDiv.insertBefore(_tr, botTr);
}
</script>
And then:
<table border='1px'>
<tr><th colspan='6'> <?php echo ($eName . " - " . $vn); ?> </th></tr>
<tr id="topTr"><th>Player ID</th>
<th>Player Name</th>
<th>Place</th>
<th>Points</th>
<th>Cash</th>
<th>Ticket?</th>
</tr>
<tr id="botTr"><td colspan='3'>Search <input type='text' id='newsearch' />
</table>
<input type="button" name="hitme" id="hitme" value="hitme" onclick="add();" />
I have an HTML table created with dynamic data and cannot predict the number of rows in it. What I want to do is to get the value of a cell when a row is clicked. I know to use td onclick but I do not know how to access the cell value in the Javascript function.
The value of the cell is actually the index of a record and it is hidden in the table. After the record key is located I can retrieve the whole record from db.
How to get the cell value if I do not know the row index and column index of the table that I clicked?
Don't use in-line JavaScript, separate your behaviour from your data and it gets much easier to handle. I'd suggest the following:
var table = document.getElementById('tableID'),
cells = table.getElementsByTagName('td');
for (var i=0,len=cells.length; i<len; i++){
cells[i].onclick = function(){
console.log(this.innerHTML);
/* if you know it's going to be numeric:
console.log(parseInt(this.innerHTML),10);
*/
}
}
var table = document.getElementById('tableID'),
cells = table.getElementsByTagName('td');
for (var i = 0, len = cells.length; i < len; i++) {
cells[i].onclick = function() {
console.log(this.innerHTML);
};
}
th,
td {
border: 1px solid #000;
padding: 0.2em 0.3em 0.1em 0.3em;
}
<table id="tableID">
<thead>
<tr>
<th>Column heading 1</th>
<th>Column heading 2</th>
<th>Column heading 3</th>
<th>Column heading 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>43</td>
<td>23</td>
<td>89</td>
<td>5</td>
</tr>
<tr>
<td>4</td>
<td>3</td>
<td>0</td>
<td>98</td>
</tr>
<tr>
<td>10</td>
<td>32</td>
<td>7</td>
<td>2</td>
</tr>
</tbody>
</table>
JS Fiddle proof-of-concept.
A revised approach, in response to the comment (below):
You're missing a semicolon. Also, don't make functions within a loop.
This revision binds a (single) named function as the click event-handler of the multiple <td> elements, and avoids the unnecessary overhead of creating multiple anonymous functions within a loop (which is poor practice due to repetition and the impact on performance, due to memory usage):
function logText() {
// 'this' is automatically passed to the named
// function via the use of addEventListener()
// (later):
console.log(this.textContent);
}
// using a CSS Selector, with document.querySelectorAll()
// to get a NodeList of <td> elements within the #tableID element:
var cells = document.querySelectorAll('#tableID td');
// iterating over the array-like NodeList, using
// Array.prototype.forEach() and Function.prototype.call():
Array.prototype.forEach.call(cells, function(td) {
// the first argument of the anonymous function (here: 'td')
// is the element of the array over which we're iterating.
// adding an event-handler (the function logText) to handle
// the click events on the <td> elements:
td.addEventListener('click', logText);
});
function logText() {
console.log(this.textContent);
}
var cells = document.querySelectorAll('#tableID td');
Array.prototype.forEach.call(cells, function(td) {
td.addEventListener('click', logText);
});
th,
td {
border: 1px solid #000;
padding: 0.2em 0.3em 0.1em 0.3em;
}
<table id="tableID">
<thead>
<tr>
<th>Column heading 1</th>
<th>Column heading 2</th>
<th>Column heading 3</th>
<th>Column heading 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>43</td>
<td>23</td>
<td>89</td>
<td>5</td>
</tr>
<tr>
<td>4</td>
<td>3</td>
<td>0</td>
<td>98</td>
</tr>
<tr>
<td>10</td>
<td>32</td>
<td>7</td>
<td>2</td>
</tr>
</tbody>
</table>
JS Fiddle proof-of-concept.
References:
Array.prototype.forEach().
document.getElementById().
document.getElementsByTagName().
document.querySelectorAll().
EventTarget.addEventListener().
Function.prototype.call().
This is my solution
var cells = Array.prototype.slice.call(document.getElementById("tableI").getElementsByTagName("td"));
for(var i in cells){
console.log("My contents is \"" + cells[i].innerHTML + "\"");
}
You can use:
<td onclick='javascript:someFunc(this);'></td>
With passing this you can access the DOM object via your function parameters.
I gave the table an id so I could find it. On onload (when the page is loaded by the browser), I set onclick event handlers to all rows of the table. Those handlers alert the content of the first cell.
<!DOCTYPE html>
<html>
<head>
<script>
var p = {
onload: function() {
var rows = document.getElementById("mytable").rows;
for(var i = 0, ceiling = rows.length; i < ceiling; i++) {
rows[i].onclick = function() {
alert(this.cells[0].innerHTML);
}
}
}
};
</script>
</head>
<body onload="p.onload()">
<table id="mytable">
<tr>
<td>0</td>
<td>row 1 cell 2</td>
</tr>
<tr>
<td>1</td>
<td>row 2 cell 2</td>
</tr>
</table>
</body>
</html>
.......................
<head>
<title>Search students by courses/professors</title>
<script type="text/javascript">
function ChangeColor(tableRow, highLight)
{
if (highLight){
tableRow.style.backgroundColor = '00CCCC';
}
else{
tableRow.style.backgroundColor = 'white';
}
}
function DoNav(theUrl)
{
document.location.href = theUrl;
}
</script>
</head>
<body>
<table id = "c" width="180" border="1" cellpadding="0" cellspacing="0">
<% for (Course cs : courses){ %>
<tr onmouseover="ChangeColor(this, true);"
onmouseout="ChangeColor(this, false);"
onclick="DoNav('http://localhost:8080/Mydata/ComplexSearch/FoundS.jsp?courseId=<%=cs.getCourseId()%>');">
<td name = "title" align = "center"><%= cs.getTitle() %></td>
</tr>
<%}%>
........................
</body>
I wrote the HTML table in JSP.
Course is is a type. For example Course cs, cs= object of type Course which had 2 attributes: id, title.
courses is an ArrayList of Course objects.
The HTML table displays all the courses titles in each cell. So the table has 1 column only:
Course1
Course2
Course3
......
Taking aside:
onclick="DoNav('http://localhost:8080/Mydata/ComplexSearch/FoundS.jsp?courseId=<%=cs.getCourseId()%>');"
This means that after user selects a table cell, for example "Course2", the title of the course- "Course2" will travel to the page where the URL is directing the user: http://localhost:8080/Mydata/ComplexSearch/FoundS.jsp . "Course2" will arrive in FoundS.jsp page. The identifier of "Course2" is courseId. To declare the variable courseId, in which CourseX will be kept, you put a "?" after the URL and next to it the identifier.
It works.