The purpose of this code is to open two different tabs where one tab is nested inside main tab. When user click a link which passes a query string hash values like the following
http://127.0.0.1:5500/products/virtual-machines.html#Get_Started#v-pills-02-tab
I need to open both tabs at once when page loads.Bootstrap 5 is the plugin used for. I tried using the following code.
var hash = location.hash.split('?')[0];
console.log(hash);
if (hash) {
var triggerEl = document.querySelector("#" + hash + '');
triggerEl.click();
}
result
#Get_Started#v-pills-02-tab
I need to slipt in two var
Get_Started
v-pills-02-tab
Your example code is splitting on a question mark (?), which doesn't exist in your hash.
Perhaps you could write your hash as a comma separated list, the first item in the list would be the parent tab element id, and the 2nd item would be the child tab element id, etc.
http://127.0.0.1:5500/products/virtual-machines.html#Get_Started,v-pills-02-tab
Then parse it, splitting on the comma.
// substring out the leading '#'
// and convert the comma-separated items into an array
var tabIds = location.hash.substring(1).split(',');
if(tabIds) {
var $tab = $('#' + tabIds[0]);
// Ensure that the element exists before clicking it
if($tab.length > 0) {
$tab.click();
// Check for subsequent tab-ids
if(tabIds.length > 1) {
var $subTab = $('#' + tabIds[1]);
if($subTab.length > 0) {
$subTab.click();
}
}
}
}
(Sorry if you're allergic to jQuery. It shouldn't be difficult to translate it to vanilla JS.)
Related
I have a search results page in a PHP site that returns a list of results using pagination. The URL looks like this:
findProducts.php?action=searchAssets&orderNumber=xxxx&productName=zzz&skip=20
I have a select menu that allows the user to modify/filter the search results which triggers a script like this:
$(document).ready(function() {
$('#productType').change(function() {
window.location.href = window.location.href + '&productType=' + $(this).val();
});
});
This is working well except for one thing - I need to reset the 'skip' parameter to 0 for the new filter search as the pagination values from the previous search won't be valid or applicable. Is there a way I can change:
skip=20
to:
skip=0
as part of this script?
You could do a RegExp replace on the URL:
window.location.href = window.location.href.replace(/((?:\?|&)skip)=\d+/, '$1=0') + '...';
(untested)
Note that you should do the same with the productType because otherwise you'll add it again and again.
Better solution would possibly be to have a base URL and then add all necessary parameters instead of doing search and replace...
You can get the query from the URL by splitting the URL using ?
This will give you the base url in the first index and the query in the second.
You can then get the query parameters by splitting the query using &.
You can loop through all of the parameters checking if it is the skip parameter. If the parameter is the skip parameter push your new value to an output array. Otherwise push the unchanged parameter to an output array.
You can then use join to join all of your output elements using & to reconstruct the query and return your original base url with your new query string.
<script>
function fixQuery(qstr) {
var parts = qstr.split('?');
var query = parts[1];
var a= query.split("&");
var out=[];
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
if(decodeURIComponent(b[0])=="skip")
{
out.push("skip=0")
}
else {
out.push(a[i]);
}
}
return parts[0] + '?' + out.join("&");
}
var result= fixQuery("http://example.com/findProducts.php?param1=test+thing¶m2=hello&skip=10");
console.log(result)
//http://example.com/findProducts.php??param1=test+thing¶m2=hello&skip=0
</script>
I'm trying to assign class and id to items in an array I created in js and input into my html. I'm doing this so I can style them in my stylesheet. Each item will not be styled the same way.
I'm a beginner so trying to keep it to code I can understand and make it as clean as possible, i.e. not making each of these items an element in the html.
This part works fine:
var pool =['A','B','3','J','R','1','Q','F','5','T','0','K','N','C','R','U']
var letters = pool.join('');
document.getElementById('key').innerHTML = letters;
This part not so much:
var char1 = letters[1];
char1.classList.add('hoverRed');
There is a similar question here that didn't work for me, it just showed [object][object][object] when I ran it.
Your code attempts to apply a style to an array element, but CSS only applies to HTML. If you wish to style one character in a string, that character must be wrapped in an HTML element (a <span> is the best choice for wrapping an inline value).
This code shows how to accomplish this:
var pool =['A','B','3','J','R','1','Q','F','5','T','0','K','N','C','R','U']
var letters = pool.join('');
// Replace a specific character with the same character, but wrapped in a <span>
// so it can be styled
letters = letters.replace(letters[1], "<span>" + letters[1] + "</span>");
// Insert the letters string into the div
var theDiv = document.getElementById('key');
// Inject the string into the div
theDiv.innerHTML = letters;
// Get a reference to the span:
var theSpan = theDiv.querySelector("span");
// Add the style to the <span> that wraps the character, not the character itself
theSpan.classList.add('hoverRed');
.hoverRed {
color:red;
}
<div id="key"></div>
And, this snippet shows how you could apply CSS to any letter:
var pool =['A','B','3','J','R','1','Q','F','5','T','0','K','N','C','R','U'];
// Leave the original array alone so that it can be manipulated any way needed
// in the future, but create a new array that wraps each array element within
// a <span>. This can be accomplished in several ways, but the map() array method
// is the most straight-forward.
var charSpanArray = pool.map(function(char){
return "<span>" + char + "</span>";
});
// Decide which character(s) need CSS applied to them. This data can come from anywhere
// Here, we'll just say that the 2nd and 5th ones should.
// Loop through the new array and on the 2nd and 5th elements, apply the CSS class
charSpanArray.forEach(function(element, index, array){
// Check for the particular array elements in question
if(index === 1 || index === 4){
// Update those strings to include the CSS
array[index] = element.replace("<span>","<span class='hoverRed'>");
}
});
// Now, turn the new array into a string
var letters = charSpanArray.join('');
// For diagnostics, print the string to the console just to see what we've got
console.log(letters);
// Get a reference to the div container
var theDiv = document.getElementById('key');
// Inject the string into the div
theDiv.innerHTML = letters;
.hoverRed {
color:red;
}
<div id="key"></div>
You're on the right track, but missed one key thing.
In your example, pool contains characters. When you combine them using join, you get a string. Setting that string as the innerHTML of an element doesn't give the string super powers, it's still just a string.
In order to get a classList, you need to change your letters into elements and work with them.
I've included an es6 example (and a working plunker) of how to get the functionality you want below.
let pool = ['A','B','3','J','R','1','Q','F','5','T','0','K','N','C','R','U']
const letterToElement = function(char) {
//Create the element
let e = document.createElement("SPAN");
//Create the text node
let t = document.createTextNode(char);
//Put the text node on the element
e.appendChild(t);
//Add the class name you want
e.className += "hoverRed";
return e;
};
//create your elements from your pool and append them to the "key" element
window.onload = function() {
let container = document.getElementById("key");
pool.map(l => letterToElement(l))
.forEach(e => container.appendChild(e));
}
https://plnkr.co/edit/mBhA60aUCEGSs0t0MDGu
I have not been able to find a working example or a good explanation of how I can achieve the following: (I would appreciate if anyone can point me in the right direction.
I have a query string: **"/api/bla?sources=[1,2]&plans=[1,2]&codes=[1,2,3]"**
I will be updating the query string via either javascript or jquery when certain events occur on my page, doesnt matter which.
For example, there is a multi select dropdown on the page which houses [sources] and [plans] and [codes]... These dropdowns have IDs which i am to update my request url with upons selecting items in teh dropdowns.
When a source with ID "3" is selected from the dropdown (or checkbox, doesnt matter what page controls are being used) the query string parameter sources[1,2] will need a "3" appended. Likewise then if the item with an ID of "2" is unselected, it will likewise be removed from the query string leaving the new string as sources[1,3]
I am somewhat new to javascript/jquery and especially more advanced string manipulation. I have been attempting to recreate something to demonstrate this and have gotten to the following which is not fully working.
Basically my initial if statement works as intended, but the moment the else is hit (when another ID needs to be added to an existing model in the query string - like a second ID for [sources] or [codes]) it returns wonky output - seeng as I couldnt get the right formula to update everything correctly.
//TIMEMTABLE QUERY
function updateCalendar(filter_id, filter_element) {
//Run through the filter checks before making final call to query and update timetable?
//GET THE MODEL/OBJECT NAME
var queryName = filter_element.attr('data-owner');
//GET THE IDs //this is either an array of all selected IDs or a single id which is used in the else statement
var queryId = filter_element.attr('value');
var queryIds = $('#'+filter_id).val();
var modelCheckIndex = requestString.toLowerCase().indexOf(queryName.toLowerCase());
//build a request string
if (modelCheckIndex < 0) {
console.info('ADD MODEL TO QUERY STRING');
requestString = requestString + "&" + (queryName.toLowerCase() + "[" + queryIds + "]");
console.log(requestString);
}
else{
console.info('UPDATE MODEL ON QUERY STRING');
var position = requestString.toLowerCase().indexOf(queryName.toLowerCase());
//requestString = requestString.substr(modelCheckIndex -1, requestString.length -1) + "," + queryId + "]";
requestString = requestString.slice(modelCheckIndex.indexOf("]"), modelCheckIndex) + "," + queryId;
console.log(requestString);
}
//MAKE THE API CALL USING CREATED QUERY STRING
}
If anyone has any examples or fiddles lying around I would also appreciate it.
Fiddle I am trying to get to work
It looks like you are just having trouble parsing and updating a query string. In which case, I have a function I've been using for that (thank you Google)
function getUriParams(string) {
var params = {},
queryString = string.slice(string.lastIndexOf('?')).substring(1),
regex = /([^&=]+)=([^&]*)/g,
m;
while (m = regex.exec(queryString)) {
params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
return params;
}
The input is your requestString and the output is an object of key value pairs of the query string.
To make the object a string, jQuery makes it easy with $.param().
//get key value pairs
var obj = getUriParams(requestString);
//make query string
var str = $.param(obj);
I suggest to change the logic a bit. I would use some data storage for the wanted parameter and rebuild the request string every time when it's necessary, like the below example.
It is much more better, than rebuild the string each time when some value has changed.
var data = {
sources: [1, 2],
plans: [],
codes: [1, 3, 4]
};
function buildStr() {
function get(key) { return key + '=' + JSON.stringify(data[key]); }
return '/api/bla?' + ['sources', 'plans', 'codes'].map(get).join('&');
}
document.write('<pre>' + buildStr() + '</pre>');
I have implemented searchbox using jQuery. Here is the code which sends search term and
after that I receive Json which I use to make list of matched searched items.
The problem is that on each keyup I delete all matched items :
$("#realPlaceForSearchItems").html("");
because if I don't that I get duplications when searching for product if I enter "pro" and then type "d". (I am appending list items to the list) Is it possible to achieve that I somehow just delete elements that do not match "prod" (which previously matched "pro" ofcourse) and that elements that match prod stay untouched after typing "d".
$("#searchInput").keyup(function () {
$this = $(this);
$('#realPlaceForSearchItems').show();
$("#realPlaceForSearchItems").html("");
var seachedTerm=$this.val();
if ($this.val().length> 2)
{
$("#realPlaceForSearchItems").html("");
$('#realPlaceForSearchItems').show();
$.ajax({
type: "POST",
url: ROOT + "Filter/Search/",
data: {term: $this.val()},
success: function (data)
{
var Link = $("#searchTemplate>li>a");
var placeForProductId=$("#searchTemplate>li>a>input");
var placeForPicture = $("#searchTemplate>li>a>div>img");
var placeForProductName = $("#searchTemplate>li>a>div>div");
var placeForPrice= $("#searchTemplate>li>a>div>span");
$.each(data.productsWereSeached, function () {
console.log("sddsd", data.totalrows);
var imagesFolder="/Content/images/";
var pathToProduct="/ProductDetails/Index/"
var slash = "/";
Link.attr("href", pathToProduct + this.Id);
placeForProductId.val(this.Id);
if (this && this.Picture) //for the case there is no any picture there would be error cant read propery or undefined
placeForPicture.attr("src", imagesFolder + this.Id + slash + this.Picture.FileName);
else
placeForPicture.attr("src", "");
placeForProductName.html(this.Name);
placeForPrice.html((parseFloat(this.Price) / 100.0).toString() + " kn");
$listItem = $("#searchTemplate").html();
$("#realPlaceForSearchItems").append($listItem);
});
$("#nOfMatchedProducts").val(data.totalrows);
if (data.totalrows > 2)
{
var searchurl="/Search/ShowMoreSearched?term="
$showMoreItem = $("#showMoreItem").html();
$("#showMoreItem>li>a").attr("href",searchurl+seachedTerm);
$("#realPlaceForSearchItems").append($showMoreItem);
}
},
failure: function ()
{
}
});
}
});
$.each(data.productsWereSeached, function () {
if($('a[href="'+pathToProduct + this.Id+'"]').length == 0) {
console.log("sddsd", data.totalrows);
var imagesFolder="/Content/images/";
var pathToProduct="/ProductDetails/Index/"
var slash = "/";
Link.attr("href", pathToProduct + this.Id);
placeForProductId.val(this.Id);
if (this && this.Picture) //for the case there is no any picture there would be error cant read propery or undefined
placeForPicture.attr("src", imagesFolder + this.Id + slash + this.Picture.FileName);
else
placeForPicture.attr("src", "");
placeForProductName.html(this.Name);
placeForPrice.html((parseFloat(this.Price) / 100.0).toString() + " kn");
$listItem = $("#searchTemplate").html();
$("#realPlaceForSearchItems").append($listItem);
}
});
Psst... I'm assuming you also want to limit calls to the server and that your search result list is not wildly huge!
So! Benefit to your current approach is you don't have to manage/compare any existing data set. This makes things easier when a search for "pro" changes to a search for "cro" or any other change that makes the previous AJAX call irrelevant. But, like you said, it leaves you with this clear then re-add items inefficiency when you search for "prod" after "pro".
Idea:
Store the most recent AJAX call criteria in a global.
If new search value includes the latest AJAX search value, filter/hide items in the existing data set which do not match the new criteria. Do not perform a new search.
If new value does not include the latest AJAX search value: clear current data set, update AJAX search value, execute new AJAX call
Pass the index from $.each (http://api.jquery.com/jQuery.each/) into your function, then use it to select the search result you will replaceWith (http://api.jquery.com/replaceWith/) the element you just built. In using this method, your four LI elements within the search results UL must exist before a search.keyup is executed.
Do this by changing two lines...
$.each(data.productsWereSeached, function (index) {
... all of the existing code in the loop stays the same except ...
$("#realPlaceForSearchItems LI:eq(" + index + ")").replaceWith($listItem);
});
So, I'll admit to being a bit of a JS noob, but as far as I can tell, this should be working and it is not.
Background:
I have a form with 3 list boxes. The list boxes are named app1, db1, and db2. I'm using javascript to allow the user to add additional list boxes, increasing the name tag for each additional select box.
When I add additional app named boxes, the value increments properly for each additional field. If I try to add addtional db named selects, it fails to recognize the 2nd tag on the first loop through the array. This causes me to end up with 2 elements named db2. On each subsequent tag, it is recognized properly and is properly incremented.
Here is the HTML for the db1 tag:
<select name="db1">
*options*
</select>
And db2:
<select name="db2">
*options*
</select>
The tags are identical. Here is the function that I am using to figure out the next number in the sequence (note: tag is either app or db, tags is an array of all select tag names in the DOM, if I inspect tags, it gives me ['app1', 'db1', 'db2', '']):
function return_select_name(tag, tags) {
matches = new Array();
var re = new RegExp(tag + "\\d+", "g");
for (var i = 0; i < tags.length; i++) {
var found = re.exec(tags[i]);
if (found != null) {
matches.push(found[0]);
}
}
matches = matches.sort();
index = parseInt(/\d+/.exec(matches.last())) + 1;
index = tag + index;
return index;
}
If I add an app tag, it will return 'app2'. If I search for a db tag, it will return 'db2' on the first time through, db3 on the 2nd, etc, etc.
So basically, I'm sure I'm doing something wrong here.
I'd handle it by keeping a counter for db and a counter for app to use to generate the names.
var appCounter = 1;//set this manually or initialize to 0 and
var dbCounter = 2;//use your create function to add your elements on pageload
Then, when you go to create your next tag, just increment your counter and use that as the suffix for your name:
var newAppElement = document.createElement('select');
newAppElement.name = 'app' + (++appCounter);
..
// --OR for the db element--
var newDbElement = document.createElement('select');
newDbElement.name = 'db' + (++dbCounter );
..
The problem you are getting is that regex objects are stateful. You can fix your program by putting the regex creation inside the loop.
function return_select_name(tag, tags) {
matches = new Array();
// <-- regex was here
for (var i = 0; i < tags.length; i++) {
var re = new RegExp(tag + "\\d+", "g"); //<--- now is here
var found = re.exec(tags[i]);
if (found != null) {
matches.push(found[0]);
}
}
matches = matches.sort();
index = parseInt(/\d+/.exec(matches[matches.length-1])) + 1; //<--- I dont think matches.last is portable, btw
index = tag + index;
return index;
}
In any case, if I were to do this myself, I would probably prefer to avoid the cmplicated text matching and just store the next tag indices in a variable or hash map.
Another suggestion: if you put parenthesis in your regex:
// /tag(\d+)/
var re = new RegExp(tag + "(\\d+)", "g");
Then you can use found[1] to get your number directly, without the extra step afterwards.
I know this has already been answered, but I put this together as a proof of concept.
http://jsfiddle.net/zero21xxx/LzyTf/
It's an object so you could probably reuse it in different scenarios. Obviously there are ways it could be improved, but I thought it was cool so I thought I would share.
The console.debug only works in Chrome and maybe FF.