I want to get the text inside the div in the class "atName".
I am looping though the table td's like this:
var searchString = document.getElementById("search").value;
if (searchString !== "") {
var cols = document.querySelectorAll('#theTable td'),
colslen = cols.length,
i = -1;
while (++i < colslen) {
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
Here i want to access the text inside the div
}
Every td is set up like this:
<td id="H" class="element nonmetal gasI">
<div class="atN">1</div>
<div class="atS gas"><a class="gas" href="https://en.wikipedia.org/wiki/Hydrogen" target="_blank">H</a></div>
<div class="atName">Hydrogen</div>
<div class="atW">1.00794</div>
</td>
I want the text inside the "atName" div.
Does anyone know how?
Thanks!
The same way you selected the tds:
cols[i].querySelector('.atName').textContent;
btw. you should give different IDs to your tds or use classes because IDs should be unique
UPDATE
To avoid any confusion, I'm already assuming we're looping the tds (from your code), and this line goes here:
while (++i < colslen) {
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
var divText = cols[i].querySelector('.atName').textContent; // <--- here
}
...
}
You can get the object by class name :
document.getElementsByClassName('atName')
But this return you a list of object with this class.
So you can do in your while:
while (++i < colslen)
{
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
var text = cols[i].getElementsByClassName('atName')[0].textContent;
}
}
Maybe this will help?
var searchString = "Hy";
if (searchString !== "") {
var cols = document.querySelectorAll('#theTable tr td .atName');
for (var i=0;i<cols.length;i++)
{
if (cols[i].innerHTML.indexOf(searchString)>-1)
{
alert(cols[i].innerHTML);
}
}
}
What you're looking for is element.innerHTML but hopefully this selector trick will help you too.
Edit: element.textContent is different but you might desire to use it instead.
Related
I created an HTML table with a lot of information about a country. Now I want the user to be able to search in this table for a piece of information like the Area.
function selectRow() {
var input, filter, table, trs, td;
input = document.getElementById("search");
filter = input.value.toUpperCase();
table = document.getElementById("dataRows");
trs = table.getElementsByTagName("tr");
for (let index = 0; index < trs.length; index++) {
td = trs[index].getElementsByTagName("td")[0];
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
trs[index].display = "";
} else {
trs[index].display = "none";
}
}
}
<input type="text" id="search" onkeyup="selectRow()" placeholder="Search.." />
<table id="dataRows">
<tr>
<th>Attributes</th>
<th>Value</th>
</tr>
<tr>
<td>Name</td>
<td>Australia</td>
</tr>
<tr>
<td>Area</td>
<td>7,741,220.00</td>
</tr>
<tr>
<td>Population</td>
<td>25,466,459</td>
</tr>
</table>
But when I try to use it I get the error: "Uncaught TypeError: Cannot read property 'innerHTML' of undefined"
I can't figure out why the td is undefined.
The most helpful thing to demonstrate first, I think, is a method that will let you diagnose this yourself in future. This sort of difficulty will occur all the time, so here is one method to help you generally problem solve these types of issues.
You know that <td> is not the value you expect, so check your expectation by outputting the values that you use to acquire <td>. You can do that by adding these console.log lines at the top of your loop:
for (let index = 0; index < trs.length; index++) {
console.log("trs[index]",trs[index]);
console.log("trs[index].getElementsByTagName(td)", trs[index].getElementsByTagName("td"));
With that, you should see that the first <tr> has <th> elements, not <td>! These surprises happen all the time, it's great to learn tricks to check your assumptions the quickest way you can.
Here's a very simple solution, the first and last line of this block are the same in your code:
for (let index = 0; index < trs.length; index++) {
var tds = trs[index].getElementsByTagName("td");
if(tds.length == 0) {
continue;
}
td = tds[0];
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
Looks like you've just started working through building this, I hope this helps!
<script>
function myFunction() {
// Declare variables
var input, filter, ul, li, a, i, txtValue;
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("myUL");
li = ul.getElementsByTagName('li');
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
</script>
use ur code like this to get best result and without any error
edit
I think the tricky part of this is actually the accepting of user input intelligently. Therefore, I'd say the best thing to do is to pass off your searching to an autocomplete-type plugin. Once the page is ready, you pass the focus to an input text box, and then let the plugin do its magic as you search...
For example, you could use the quicksearch plugin.
Then given a table of data and an input like this:
<input id="searcher" type="text" name="searcher">
You could have a ready function that looks like this:
$('#searcher').quicksearch('table tbody tr', {
'delay': 100,
'bind': 'keyup keydown',
'show': function() {
if ($('#searcher').val() === '') {
return;
}
$(this).addClass('show');
},
'onAfter': function() {
if ($('#searcher').val() === '') {
return;
}
if ($('.show:first').length > 0){
$('html,body').scrollTop($('.show:first').offset().top);
}
},
'hide': function() {
$(this).removeClass('show');
},
'prepareQuery': function(val) {
return new RegExp(val, "i");
},
'testQuery': function(query, txt, _row) {
return query.test(txt);
}
});
$('#searcher').focus();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Try it out here: http://jsfiddle.net/ZLhAd/369/
This is supposed to be a shopping cart inspired by jeasyui.com. I have three problems with it.
I can't get the value of the quantity when it's changed in the input box.
When I remove an item from the cart, that service must be available in its appropriate tab again.
The services get indented for each new tab. I need them to start at the beginning everytime.
This is the code I have so far:codepen.io. I'm sorry if it's messy, it's my first time working with javascript/jquery. Any help is appreciated. Thank you!
function removeProduct(el) {
var name1 = $(el).closest('tr').find('td').eq(0).text();
var price1;
for (var i = 0; i < data.length; i++) {
if (name1 == data[i][0]) {
price1 = data[i][2];
}
var className = $(el).parent().attr('class');
/**Add a new list item in item class with the class name and the service name and price in paragraphs.*/
$(el).closest('tr').remove();
}
}
function changeQuantity(el) {
var name1 = $(el).closest('tr').find('td').eq(0).text();
var quantity1 = $(el).closest('tr').find('td').eq(1).value;
var price1;
for (var i = 0; i < data.length; i++) {
data[i][1] = quantity1;
data[i][2] = price * quantity1;
if (name1 == data[i][0]) {
price1 = data[i][2];
}
}
$(el).closest('tr').find('td').eq(1).html("<input type='number' value='" + quantity1 + "' style='width:100%;'>");
$(el).closest('tr').find('td').eq(3).html(price1);
}
To get the quantity do:
$(el).closest('tr').find('td').eq(1).find('input').val();
Because you are physically removing the element from the DOM with source.remove(); you can't easily. I would suggest adding a class to the element that will hide it. And then when you remove it from the card, remove that class.
For your third issue, remove the CSS that hides the product classes and instead add this:
Remove:
.Biostatistics {
display: none;
}
.ClinicalLaboratory {
display:none;
}
.Something{
display:none;
}
Add:
.Products li {
display: none;
}
Also change your js to be like this (adding the closest('li'):
x[i].closest('li').style.display = 'none';
Am struggling hard to bind an array object with list of span values using watcher in Angularjs.
It is partially working, when i input span elements, an array automatically gets created for each span and when I remove any span element -> respective row from the existing array gets deleted and all the other rows gets realigned correctly(without disturbing the value and name).
The problem is when I remove a span element and reenter it using my input text, it is not getting added to my array. So, after removing one span element, and enter any new element - these new values are not getting appended to my array.
DemoCode fiddle link
What am I missing in my code?
How can I get reinserted spans to be appended to the existing array object without disturbing the values of leftover rows (name and values of array)?
Please note that values will get changed any time as per a chart.
This is the code am using:
<script>
function rdCtrl($scope) {
$scope.dataset_v1 = {};
$scope.dataset_wc = {};
$scope.$watch('dataset_wc', function (newVal) {
//alert('columns changed :: ' + JSON.stringify($scope.dataset_wc, null, 2));
$('#status').html(JSON.stringify($scope.dataset_wc));
}, true);
$(function () {
$('#tags input').on('focusout', function () {
var txt = this.value.replace(/[^a-zA-Z0-9\+\-\.\#]/g, ''); // allowed characters
if (txt) {
//alert(txt);
$(this).before('<span class="tag">' + txt.toLowerCase() + '</span>');
var div = $("#tags");
var spans = div.find("span");
spans.each(function (i, elem) { // loop over each spans
$scope.dataset_v1["d" + i] = { // add the key for each object results in "d0, d1..n"
id: i, // gives the id as "0,1,2.....n"
name: $(elem).text(), // push the text of the span in the loop
value: 3
}
});
$("#assign").click();
}
this.value = "";
}).on('keyup', function (e) {
// if: comma,enter (delimit more keyCodes with | pipe)
if (/(188|13)/.test(e.which)) $(this).focusout();
if ($('#tags span').length == 7) {
document.getElementById('inptags').style.display = 'none';
}
});
$('#tags').on('click', '.tag', function () {
var tagrm = this.innerHTML;
sk1 = $scope.dataset_wc;
removeparent(sk1);
filter($scope.dataset_v1, tagrm, 0);
$(this).remove();
document.getElementById('inptags').style.display = 'block';
$("#assign").click();
});
});
$scope.assign = function () {
$scope.dataset_wc = $scope.dataset_v1;
};
function filter(arr, m, i) {
if (i < arr.length) {
if (arr[i].name === m) {
arr.splice(i, 1);
arr.forEach(function (val, index) {
val.id = index
});
return arr
} else {
return filter(arr, m, i + 1)
}
} else {
return m + " not found in array"
}
}
function removeparent(d1)
{
dataset = d1;
d_sk = [];
Object.keys(dataset).forEach(function (key) {
// Get the value from the object
var value = dataset[key].value;
d_sk.push(dataset[key]);
});
$scope.dataset_v1 = d_sk;
}
}
</script>
Am giving another try, checking my luck on SO... I tried using another object to track the data while appending, but found difficult.
You should be using the scope as a way to bridge the full array and the tags. use ng-repeat to show the tags, and use the input model to push it into the main array that's showing the tags. I got it started for you here: http://jsfiddle.net/d5ah88mh/9/
function rdCtrl($scope){
$scope.dataset = [];
$scope.inputVal = "";
$scope.removeData = function(index){
$scope.dataset.splice(index, 1);
redoIndexes($scope.dataset);
}
$scope.addToData = function(){
$scope.dataset.push(
{"id": $scope.dataset.length+1,
"name": $scope.inputVal,
"value": 3}
);
$scope.inputVal = "";
redoIndexes($scope.dataset);
}
function redoIndexes(dataset){
for(i=0; i<dataset.length; i++){
$scope.dataset[i].id = i;
}
}
}
<div ng-app>
<div ng-controller="rdCtrl">
<div id="tags" style="border:none;width:370px;margin-left:300px;">
<span class="tag" style="padding:10px;background-color:#808080;margin-left:10px;margin-right:10px;" ng-repeat="data in dataset" id="4" ng-click="removeData($index)">{{data.name}}</span>
<div>
<input type="text" style="margin-left:-5px;" id="inptags" value="" placeholder="Add ur 5 main categories (enter ,)" ng-model="inputVal" />
<button type="submit" ng-click="addToData()">Submit</button>
<img src="../../../static/app/img/accept.png" ng-click="assign()" id="assign" style="cursor:pointer;display:none" />
</div>
</div>
<div id="status" style="margin-top:100px;"></div>
</div>
</div>
I have a list with about 10 000 customers on a web page and need to be able to search within this list for matching input. It works with some delay and I'm looking for the ways how to improve performance. Here is simplified example of HTML and JavaScript I use:
<input id="filter" type="text" />
<input id="search" type="button" value="Search" />
<div id="customers">
<div class='customer-wrapper'>
<div class='customer-info'>
...
</div>
</div>
...
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#search").on("click", function() {
var filter = $("#filter").val().trim().toLowerCase();
FilterCustomers(filter);
});
});
function FilterCustomers(filter) {
if (filter == "") {
$(".customer-wrapper").show();
return;
}
$(".customer-info").each(function() {
if ($(this).html().toLowerCase().indexOf(filter) >= 0) {
$(this).parent().show();
} else {
$(this).parent().hide();
}
});
}
</script>
The problem is that when I click on Search button, there is a quite long delay until I get list with matched results. Are there some better ways to filter list?
1) DOM manipulation is usually slow, especially when you're appending new elements. Put all your html into a variable and append it, that results in one DOM operation and is much faster than do it for each element
function LoadCustomers() {
var count = 10000;
var customerHtml = "";
for (var i = 0; i < count; i++) {
var name = GetRandomName() + " " + GetRandomName();
customerHtml += "<div class='customer-info'>" + name + "</div>";
}
$("#customers").append(customerHtml);
}
2) jQuery.each() is slow, use for loop instead
function FilterCustomers(filter) {
var customers = $('.customer-info').get();
var length = customers.length;
var customer = null;
var i = 0;
var applyFilter = false;
if (filter.length > 0) {
applyFilter = true;
}
for (i; i < length; i++) {
customer = customers[i];
if (applyFilter && customer.innerHTML.toLowerCase().indexOf(filter) < 0) {
$(customer).addClass('hidden');
} else {
$(customer).removeClass('hidden');
}
}
}
Example: http://jsfiddle.net/29ubpjgk/
Thanks to all your answers and comments, I've come at least to solution with satisfied results of performance. I've cleaned up redundant wrappers and made grouped showing/hiding of elements in a list instead of doing separately for each element. Here is how filtering looks now:
function FilterCustomers(filter) {
if (filter == "") {
$(".customer-info").show();
} else {
$(".customer-info").hide();
$(".customer-info").removeClass("visible");
$(".customer-info").each(function() {
if ($(this).html().toLowerCase().indexOf(filter) >= 0) {
$(this).addClass("visible");
}
});
$(".customer-info.visible").show();
}
}
And an test example http://jsfiddle.net/vtds899r/
The problem is that you are iterating the records, and having 10000 it can be very slow, so my suggestion is to change slightly the structure, so you won't have to iterate:
Define all the css features of the list on customer-wrapper
class and make it the parent div of all the list elements.
When your ajax request add an element, create a variable containing the name replacing spaces for underscores, let's call it underscore_name.
Add the name to the list as:
var customerHtml = "<div id='"+underscore_name+'>" + name + "</div>";
Each element of the list will have an unique id that will be "almost" the same as the name, and all the elements of the list will be on the same level under customer-wrapper class.
For the search you can take the user input replace spaces for underscores and put in in a variable, for example searchable_id, and using Jquery:
$('#'+searchable_id).siblings().hide();
siblings will hide the other elements on the same level as searchable_id.
The only problem that it could have is if there is a case of two or more repeated names, because it will try to create two or more divs with the same id.
You can check a simple implementation on http://jsfiddle.net/mqpsppxm/
I am completely new to web design and I am experimenting with few things.
Lets assume that I can't use jquety and I can't upload any file to the page, instead I can use simple javascript.
What I have now is a table with few tags, and a javascript function to show based on checkboxes.
Script
<script language="javascript">
function toggleTR(trId) {
var trArray = document.getElementsByTagName("tr");
for(i = 0; i < trArray.length; i++){
if(trArray[i].id == trId){
if(trArray[i].style.display == 'none'){
trArray[i].style.display = '';
}else{
trArray[i].style.display = 'none';
}
}
}
}
</script>
Checkbox
<input type="checkbox" onClick="toggleTR('TR1');"/> TR1
And simple table
<table>
<tr id="TR1" style="display: none;">
<td>content</td>
</tr>
</table>
It works as intended, however as the page gets bigger the load time is horrible.
Is there a way for those tags to be loaded on demand when display attribute is changed? I've read about lazy load, but could not get it to work with this.
Please try to explain it as easy as it could be, as I am totally inexperienced :-)
There can't be lazy load.
Your loop will keep on running till the end. Give a break statement if the id is found., so that the rest of th loop doesn't run.
<script language="javascript">
function toggleTR(trId) {
var trArray = document.getElementsByTagName("tr");
for(i = 0; i < trArray.length; i++){
if(trArray[i].id == trId){
if(trArray[i].style.display == 'none'){
trArray[i].style.display = '';
}else{
trArray[i].style.display = 'none';
}
break;
}
}
}
</script>
OR
You can find the particular element by id and toggle as desired. This would be recommended though.
var trobj = document.getElementById(trId);
if (var != null)
{
// toggle code here
}
As ID should be unique, this should be more optimised
function toggleTR(trId) {
var tr= document.getElementById(trId);
if(tr != null){
if(tr.style.display == 'none'){
tr.style.display = '';
}else{
tr.style.display = 'none';
}
}
}
And if you don't want to hide/show unique element, then you can use classes.
<table>
<tr class="TR1" style="display: none;">
<td>content</td>
</tr>
</table>
and
function toggleTR(trClass) {
var tr= document.querySelectorAll('.'+trClass);
if(tr != null){
for(var i = 0, l = tr.length; i < l ; i++){
if(tr[i].style.display == 'none'){
tr[i].style.display = '';
}else{
tr[i].style.display = 'none';
}
}
}
}