I am trying to build a simple on page search that uses event listeners to look at a containers data and then hides that whole container if it doesn't have the required information.
So far I have:
// get search element
let searchInput = document.getElementById ('searchInput');
// add event listener
searchInput.addEventListener ('keyup', searchPage);
function searchPage(){
//search input detection
let searchValue = document.getElementById('searchInput').value;
//set parameters to search from
let parent = document.getElementById('product-container');
let child = parent.getElementsByTagName('span');
for(let i = 0;i < child.length;i++){
let a = child[i];
if(a.innerHTML.indexOf(searchValue) >= -1) {
child[i].parentNode.style.display = '';
} else {
child[i].parentNode.style.display = 'none';
};
};
};
But this only acts on the first product-container it finds, there are 5 such containers on the page.
How do I make this look through all containers, but hide the ones that don't contain any of the information typed in the search bar.
I am getting products from an API so using html replace to add to the following template:
<script id="template" type="text/template">
<div class="product">
<div class="product--header">{{ type }}</div>
<div class="product--image"><img src="../app/assets/images/no-image.png" alt="no image"> </div>
<div class="product--information" id="product--information">
<div class="product--title"><span>{{ name }}</span></div>
<!--This is just a place holder we would house the prices here if they were on the API -->
<div class="product--price">£55</div>
<div class="product--brand"><strong>Brand:</strong><span> {{ brand }}</span></div>
<div class="product--colour"><strong>Colour:</strong><span> {{ colour }}</span></div>
<div class="product--sizes">
<select>
<option value="" disabled selected>Select Size </option>
{{ options }}
</select>
</div>
<div class="product--description"><strong>Description:</strong><br><div class="product--description__content"><span> {{ description }} </span></div></div>
<div class="product--code"><strong>Product ID:</strong><span> {{ productid }}</span></div>
<div class="product--buttons">
<button class="btn--buy" aria-label="Add to Basket">Add to basket</button>
<button class="btn--save" aria-label="Save for Later">Save for later</button>
</div>
<button class="product--add-to-wishlist" aria-label="Add to Wishlist"><i class="fas fa-heart"></i></button>
</div>
</div>
</script>
The search box code is as follows:
<input type="text" name="search" id="searchInput" placeholder="Enter Search...">
and the code that the template goes into is:
<div id="product-container">
<div class="featured"></div>
<div class="products"></div>
</div>
Because you have multiple product containers, use document.getElementsByClassName() instead of document.getElementById() and provide product-container class as argument.
let searchInput = document.getElementsByClassName ('container');
You need to modify searchPage() method. Instead of using document to find searchValue and parent use this.
let searchValue = this.getElementsByClassName('searchInput')[0].value;
let parent = this.getElementsByClassName('container')[0];
Please, add HTML code.
EDIT: If I understand correctly you have one search input which will search multiple product containers. Here is one simple example, which you can easily apply to your problem.
HTML:
<input type="text" name="search" id="searchInput" placeholder="Enter Search...">
<div class="product-container">
<span class="product">Kiwi</span>
<p>Kiwi description</p>
</div>
<div class="product-container">
<span class="product">Banana</span>
<p>Banana description</p>
</div>
<div class="product-container">
<span class="product">Apple</span>
<p>Apple description</p>
</div>
JS:
let searchInput = document.getElementById ('searchInput');
searchInput.addEventListener ('keyup', searchPage);
function searchPage(){
let searchValue = this.value.toUpperCase();
let products = document.getElementsByClassName('product');
for(let i = 0; i < products.length; i++) {
console.log(products[i].innerHTML.toUpperCase());
if (products[i].innerHTML.toUpperCase().indexOf(searchValue) > -1)
products[i].parentNode.style.display = '';
else
products[i].parentNode.style.display = 'none';
};
};
CSS:
.product-container {
display: flex;
flex-direction: column;
margin-bottom: 10px;
background: grey;
}
.product-container span {
font-size: 20px;
}
.product {
display: block;
}
https://jsfiddle.net/gardelin/koc5eg6v/25/
Related
I know there are already some discussions about this topic, but I can’t find the right solution.In all the approaches you just grab your span and your inputfield, then call a new variable input value and then ,using text content = value it should work, but in my case it doesn’t.Probably I’m missing something.This is my html
<div class="wrapper" id="wrapper">
<div class="settings-panel" id ="settings-panel">
<div class="settings-panel-wrapper">
<!----close button !---->
<div class="firstline">
<h3>Clock settings</h3>
<div id="close-button">
<img id="close-image" src="{% static 'media/cancel.png' %}" alt="cancel">
</div>
</div>
<hr>
<!----close button !---->
<div id="time-box">
<span class="description">Time(in minutes)</span>
<div class="timeslots" id="timeslots">
<div id="pomodoro">
<label>Pomodoro</label>
<input id="time-study-input "type="number" min="0"step="1" value="40">
</div>
<div id="Short-Break">
<label>Short Break</label>
<input id="short-break-input "type="number" min="0" step="1" value="5">
</div>
<div id="Long-Break">
<label>Long Break</label>
<input id="long-break-input "type="number" min="0" step="1" value="15">
</div>
</div>
<button id="save-changes" onclick="savechanges()">Save changes</button>
</div>
</div>
</div>
<div class="clock-container" id ="clock-container">
<div class="features-clock">
<button id="settings-button">Settings</button>
</div>
<div class="countdown-container">
<span id=countdown-timer-study></span>
</div>
</div>
</div>
this is script.js(probably you don’t need css)
const settings_panel = document.getElementById("settings-panel");
const settings_button= document.querySelector("#settings-button");
const wrapper = document.getElementById("wrapper");
const close_button = document.getElementById("close-button");
const body = document.querySelector("body");
const big_wrapper = document.getElementById("big-wrapper");
var time_study_input = document.getElementById("time-study-input");
var short_break_input = document.getElementById("short-break-input");
var long_break_input = document.getElementById("long -break-input");
var countdown_timer_study= document.getElementById("countdown-timer-study");
settings_button.addEventListener("click", ()=>{
settings_panel.style.display="block";
wrapper.style.backgroundColor ="rgba(0,0,0,0.3)";
})
close_button.addEventListener("click", () => {
settings_panel.style.display = "none";
wrapper.style.backgroundColor = "rgba(221, 217, 217, 0)";
})
function savechanges(){
var time_study_value = time_study_input.value;
countdown_timer_study.textContent =time_study_value;
settings_panel.style.display = "none";
wrapper.style.backgroundColor = "rgba(221, 217, 217, 0)";
}
as you can see I am trying to build a pomodoro app so when the user wants to customize the timer I can grab the input value and insert it into the span that is going to represent the time limit(as you can see I also setup a default value of 40, is this the right way to do it?).I am trying to improve my front-end skills which are definitely terrible, so any tip about code simplification (pointing out which parts are redundant or inefficient) is very welcome.
Your problem is extremely subtle. You have an extra space at the end of the id time-study-input. Try this:
<div id="pomodoro">
<label>Pomodoro</label>
<input id="time-study-input" type="number" min="0"step="1" value="40">
</div>
Currently, I am able to search for a hall by typing anything that is within a hall container (including name, location, capacity), however, I only want to be able to filter results for searches for a hall name.
<div id="searchFilter" class="flexColumn">
<label for="searchFilterText">Search for Community Halls</label>
<input type="text" id="searchFilterText" placeholder="Search for the name of a community hall">
</div>
`<div class="hall">
<div class="info">
<p>${data.Halls[halls[i]].Location[0].Address}, ${data.Halls[halls[i]].Location[1].Suburb}<br>Capacity: ${data.Halls[halls[i]].Room[0]["Standing Capacity"]}</p>
</div>
<div class="image">
<img class="img" src="${data.Halls[halls[i]].Description[4].Photos}" alt="${data.Halls[halls[i]]["Hall Name"]}">
</div>
<div class="hallNameBox">
<p class="hallName">${data.Halls[halls[i]]["Hall Name"]}</p>
</div>
</div>`;
$(document).ready(function(){
$("#searchFilterText").on("keyup", function() {
let value = $(this).val().toLowerCase();
$(".hall").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
});
Instead of using toggle use show and hide.
(Use one, two, or three to test this reduced example).
$(function() {
$("#searchFilterText").on('keyup', function() {
// Grab the value
const value = $(this).val().toLowerCase();
// If it's not empty
if (value) {
// `filter` all the hall elements if
// the text of the hallName element doesn't start with
// the value and hide them
$('.hall').filter(function() {
const text = $(this).find('.hallName').text().toLowerCase();
return !text.startsWith(value);
}).hide();
// Otherwise show them
} else {
$('.hall').show();
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="searchFilter" class="flexColumn">
<input type="text" id="searchFilterText" />
</div>
<div class="hall">
<div class="hallNameBox">
<p class="hallName">One</p>
</div>
</div>
<div class="hall">
<div class="hallNameBox">
<p class="hallName">Two</p>
</div>
</div>
<div class="hall">
<div class="hallNameBox">
<p class="hallName">Three</p>
</div>
</div>
$(document).ready(function(){
$("#searchFilterText").on("keyup", function() {
let value = $(this).val().toLowerCase();
$(".hall *").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
});
Is this what you want? If not, let me know.
I need to get selected item but in nested array.
First check my code:
<div class="column-holder" *ngFor="let training of data.trainingExercise
{
"exerasdasd":""
},
{
"isWsadad":""
}
]
}
]
}
]
}
Now i need on click to get only selected set. Single Value!
dsadsa(e){
dsa.log(e)
dsa.dsadas = e;
}
This is good but i need only single value.
In my selectedSetValue i want to show only single value.
Right now i got values in all array.
I want only selected value and selected index. This is important selected values by index
both here
<div class="column-holder" *ngFor="let training of data.trainingExercises; let i = index;">
and here
<div class="second-box-70" *ngFor="let set of training.sets; let i = index;">
you have let i = index;. Change the second one to let j = index; or something, so they are not the same
please try this, change
<div class="second-box-70" *ngFor="let set of training.sets; let i = index;">
<div class="circle-exercise">
<div class="circle-div">
<div class="num-series">
Series
</div>
<div class="circle" (click)="selectedSet(set)">
<input [(ngModel)]="set.value" class="input-invisible-for-sets" type="type">
</div>
</div>
</div>
</div>
<p class="notes" *ngIf="selectedSetValue">
{{ this.selectedSetValue.note }}
</p>
</div>
to this
<div class="second-box-70" *ngFor="let set of training.sets; let j = index;">
<div class="circle-exercise">
<div class="circle-div">
<div class="num-series">
Series
</div>
<div class="circle" (click)="selectedSet(set,j)">
<input [(ngModel)]="set.value" class="input-invisible-for-sets" type="type">
</div>
</div>
</div>
</div>
<p class="notes" *ngIf="selectedSetValue">
{{ this.selectedSetValue.note }}
</p>
</div>
ts
selectedSet(object,index){
this.selectedSetValue = object;
}
I need help with following code. I have to add new li elements and them swap elements by clicking on img up or down. I need to do it dynamically. Thank you so much.
$(document).ready(function(){
$(".p-title").hide().fadeIn(1500);
const nameInput = document.querySelector('#f-name');
nameInput.focus();
$('form').on('submit', e=>{
e.preventDefault();
let name = nameInput.value;
addItem(name);
})
let addItem = (name)=>{
$('#list').append('<li>'+name+'<img src="/assets/img/site/up_arrow.png" class="upArrow"/><img src="/assets/img/site/down_arrow.png" class="downArrow"/</li>');
nameInput.value = '';
nameInput.focus();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main class="container">
<div class="row">
<div>
<h4>
<span class="text-muted">List</span>
</h4>
<ul id='list' class="list-group mb-3">
</ul>
</div>
<div class="col-md-7 order-md-1">
<form class="needs-validation row-form">
<div class="mb-3">
<label for="nazov">Name</label>
<input id="f-name" type="text" class="form-control" placeholder="name" required>
</div>
<hr class="mb-4">
<button>Submit</button>
</form>
</div>
</div>
</main>
To reorder those dynamically added list' items using images (buttons) we set a click handler by selecting their parent container first because those images are also dynamically added !
$('#list').on('click', '.downArrow', function(e) {
Then we use insertAfter() to move Down or insertBefore() to move Up their position in the list.
$(document).ready(function(){
$(".p-title").hide().fadeIn(1500);
const nameInput = document.querySelector('#f-name');
nameInput.focus();
$('form').on('submit', e=>{
e.preventDefault();
let name = nameInput.value;
addItem(name);
})
let addItem = (name)=>{
$('#list').append('<li>'+name+'<img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Green_Arrow_Up_Darker.svg" class="upArrow"/ width="16"><img src="https://upload.wikimedia.org/wikipedia/commons/0/04/Red_Arrow_Down.svg" width="16" class="downArrow"/</li>');
nameInput.value = '';
nameInput.focus();
}
$('#list').on('click', '.downArrow', function(e) {
var curLi = $(this).closest('li');
var tarLi = curLi.next('li');
curLi.insertAfter(tarLi);
});
$('#list').on('click', '.upArrow', function(e) {
var curLi = $(this).closest('li');
var tarLi = curLi.prev('li');
curLi.insertBefore(tarLi);
});
});
body{
font-size:1.2em;
}
li:last-child img.downArrow,li:first-child img.upArrow{
display:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main class="container">
<div class="row">
<div>
<h4>
<span class="text-muted">List</span>
</h4>
<ul id='list' class="list-group mb-3">
</ul>
</div>
<div class="col-md-7 order-md-1">
<form class="needs-validation row-form">
<div class="mb-3">
<label for="nazov">Name</label>
<input id="f-name" type="text" class="form-control" placeholder="name" required>
</div>
<hr class="mb-4">
<button>Submit</button>
</form>
</div>
</div>
</main>
[UPDATE] optimized the answer by changing the styling to hide button on the edges... which is better than doing it with jQuery. thanks to #bhoodream comment
I am very new to the smart table. I have gone through its documentation on Smart Table.
But the I haven't found how to bind data on click event in smart table?
Code is very big but I am trying to post it here.
<div class="table-scroll-x" st-table="backlinksData" st-safe-src="backlinks" st-set-filter="myStrictFilter">
<div class="crawlhealthshowcontent">
<div class="crawlhealthshowcontent-right">
<input type="text" class="crserachinput" placeholder="My URL" st-search="{{TargetUrl}}" />
<a class="bluebtn">Search</a>
</div>
<div class="clearfix"></div>
</div>
<br />
<div class="table-header clearfix">
<div class="row">
<div class="col-sm-6_5">
<div st-sort="SourceUrl" st-skip-natural="true">
Page URL
</div>
</div>
<div class="col-sm-2">
<div st-sort="SourceAnchor" st-skip-natural="true">
Anchor Text
</div>
</div>
<div class="col-sm-1">
<div st-sort="ExternalLinksCount" st-skip-natural="true">
External<br />Links
</div>
</div>
<div class="col-sm-1">
<div st-sort="InternalLinksCount" st-skip-natural="true">
Internal<br />Links
</div>
</div>
<div class="col-sm-1">
<div st-sort="IsFollow" st-skip-natural="true">
Type
</div>
</div>
</div>
</div>
<div class="table-body clearfix">
<div class="row" ng-repeat="backlink in backlinksData" ng-if="backlinks.length > 0">
<div class="col-sm-6_5">
<div class="pos-rel">
<span class="display-inline wrapWord" tool-tip="{{ backlink.SourceUrl }}"><b>Backlink source:</b> <a target="_blank" href="{{backlink.SourceUrl}}">{{ backlink.SourceUrl }}</a></span><br />
<span class="display-inline wrapWord" tool-tip="{{ backlink.SourceTitle }}"><b>Link description:</b> {{ backlink.SourceTitle }}</span> <br />
<span class="display-inline wrapWord" tool-tip="{{ backlink.TargetUrl }}"><b>My URL:</b> <a target="_blank" href="{{backlink.TargetUrl}}">{{ backlink.TargetUrl }}</a></span><br />
</div>
</div>
<div class="col-sm-2">
<div class="pos-rel">
{{ backlink.SourceAnchor }}
</div>
</div>
<div class="col-sm-1">
<div>
{{ backlink.ExternalLinksCount }}
</div>
</div>
<div class="col-sm-1">
<div>
{{ backlink.InternalLinksCount }}
</div>
</div>
<div class="col-sm-1">
<div ng-if="!backlink.IsFollow">
No Follow
</div>
</div>
</div>
<div class="row" ng-if="backlinks.length == 0">
No backlinks exists for selected location.
</div>
</div>
<div class="pos-rel" st-pagination="" st-displayed-pages="10" st-template="Home/PaginationCustom"></div>
</div>
and my js code is here.
module.controller('backlinksController', [
'$scope','$filter', 'mcatSharedDataService', 'globalVariables', 'backlinksService',
function ($scope,$filter, mcatSharedDataService, globalVariables, backlinksService) {
$scope.dataExistsValues = globalVariables.dataExistsValues;
var initialize = function () {
$scope.backlinks = undefined;
$scope.sortOrderAsc = true;
$scope.sortColumnIndex = 0;
};
initialize();
$scope.itemsByPage = 5;
var updateTableStartPage = function () {
// clear table before loading
$scope.backlinks = [];
// end clear table before loading
updateTableData();
};
var updateTableData = function () {
var property = mcatSharedDataService.PropertyDetails();
if (property == undefined || property.Primary == null || property.Primary == undefined || property.Primary.PropertyId <= 0) {
return;
}
var params = {
PropertyId: property.Primary.PropertyId
};
var backLinksDataPromise = backlinksService.getBackLinksData($scope, params);
$scope.Loading = backLinksDataPromise;
};
mcatSharedDataService.subscribeCustomerLocationsChanged($scope, updateTableStartPage);
}
]);
module.filter('myStrictFilter', function ($filter) {
return function (input, predicate) {
return $filter('filter')(input, predicate, true);
}
});
But It is working fine with the direct search on textbox.
but according to the requirement I have to perform it on button click.
Your suggestions and help would be appreciated.
Thanks in advance.
You can search for a specific row by making some simple tweaks.
add a filter to the ng-repeat, and filter it by a model that you will insert on the button click, like so: <tr ng-repeat="row in rowCollection | filter: searchQuery">
in your view, add that model (using ng-model) to an input tag and define it in your controller
then pass the value to the filter when you click the search button
here's a plunk that demonstrates this
you can use filter:searchQuery:true for strict search
EDIT:
OK, so OP's big problem was that the filtered values wouldn't show properly when paginated, the filter query is taken from an input box rather then using the de-facto st-search plug-in, So I referred to an already existing issue in github (similar), I've pulled out this plunk and modified it slightly to fit the questioned use case.