I have the following function which I use to populate a Select control with options. I am grabbing values from objects on the document, and if a condition is met, throwing another value into a Select Control as an option...
function dispatchList() {
//grab list element
var list = document.getElementById("techName");
//foreach div assigned the .square class,
$('.square').each(function () {
//convert each div with .square class toString
var square = $(this).html().toString();
//grab availability value
var availability = $(this).find('tr:eq(4)').find('td').text();
//grab IP
var online = $(this).find('tr:eq(3)').find('td').text()
//if availability and IP values meet below condition...
if ((availability === "True") && (online.indexOf("10.") === 0)) {
//grab the name value from this div
var availableName = $(this).find('tr:eq(0)').find('td').text();
//create a new option element
var item = document.createElement("option");
//create a new text node containing the name of the tech
item.appendChild(document.createTextNode(availableName));
//append the new text node (option) to our select control
list.appendChild(item);
}
})
}
This function works great, but it runs when the document is ready. I need it to run when the document is ready, but also to recreate this list without refreshing the page. Ideally the select control could be emptied and recreated with a click event on a div.
This is the part I have struggled with. I have the following click event which it would make sense to chain this to, but I have not been able to work it out...
function availability() {
//for each element with a class of .square...
$('.square').each(function () {
//grab the id of each input element (button) contained in each .square div...
var btnId = $(this).find("input").attr("id");
//when .square div is clicked, also click it's associated asp button...
$(this).on('click', function (clickEvent) {
document.getElementById(btnId).click();
//****AND ALSO TRIGGER THE dispatchList() FUNCTION TO REBUILD THE #techName LIST****
})
})
}
Can this be done without AJAX or some other post back on the select control?
Does the #techName list need to be emptied first, and then rebuilt?
Thank you for any advice!
$(document).ready(function(){
$(".square").on('click', function (clickEvent) {
var el = clickEvent.target || clickEvent.srcElement
document.getElementById($(el).find('input').attr("id")).click();
dispatchList();
})
})
That's all i can do with the given question. I didn't test the code. You can give fiddle or anything to test. Also this function is written in the browser.
Related
I've added an 'Find an online stockist' button to all product pages. If you click on it, it takes you to a different page. I need to set a filter in the page to 'Online Store'. It will look like this when done:
I'm trying to select this box, so that I can add a to it. (It's the same box as in the previous screenshot but without any filters in it yet.)
Please note I can't change the filter box - it's 3rd party code.
When I inspect the page with no filters in it, it looks like this:
If I inspect it with a filter, it looks like this:
I just need to add a new <li> to the 'chosen-choices' but I can't select it.
I can select the store-filter-sec div:
var storefiltersec = $(".store-filter-sec");
If I view it in the console, it shows the right children:
But I can't select item 2 - div.chosen-container.chosen-container-multi.
var chosenContainer = storefiltersec.find(".chosen-container");
var chosenContainer1 = storefiltersec.find(".chosen-container.chosen-container-multi");
var chosenContainer2 = storefiltersec.find("div.chosen-container.chosen-container-multi");
var chosenContainer3 = $(".chosen-container");
var chosenContainer4 = $(".chosen-container.chosen-container-multi");
var chosenContainer5 = $("div.chosen-container.chosen-container-multi");
var chosenContainer6 = $(".chosen-container .chosen-container-multi");
The plan is that once I can get hold of div.chosen-container.chosen-container-multi, I can then get to
ul.chosen-choices (one of its children):
I tried to get chosen-choices by tag.
var elements = document.getElementsByTagName('ul');
'chosen-choices' shows up in the console:
but I can specify just that one. I tried this:
console.log('specific element', elements[12]);
and get:
Please tell me how I can select ul.chosen-choices in either javascript or jquery.
in javascript to select .store-filter-sec and to put it in a variable:
const el_filtersec = document.querySelector('.store-filter-sec');
Now from this selected element you select .chosen-container
const chosen-container = document.querySelector('.chosen-container');
by the way you can select directly chosen-container
Now the problem is you have several, so:
Array.from(document.querySelectorAll('.chosen-container')).forEach(el => {
// do what you have to do...
}
If you still want store-filter-sec and for each finding chosen-container:
Array.from(document.querySelectorAll('.store-filter-sec')).forEach(el1 => {
Array.from(el1.querySelectorAll('.chosen-container')).forEach(el2 => {
// do what you have to do...
}
}
I am trying to itirate through a list of links on a table and ensure the next page has ht ecorrect url but running into issues. One problem is that there are no good class names to work with so I have been using cy.xpath.
//Loop through each element (This is a dynamic amount of elements)
cy.xpath('//span[text()="Id"]//following::a[contains(#href,"maps")]'.each($el) => {
cy.get($el).then(($btn) => {
let id_text = $btn.text()
//Check that the element is visible and click on it
cy.get($el)
.should('be.visible')
.click()
//Check that the url contains the text value of the element that was clicked on
cy.url()
.should('contain', id_text)
})
})
It works one time through and then gets tripped up saying the DOM element became detached
When you see DOM element became detached it means an action has made the page refresh and a previous query is no longer pointing at a valid element.
In you case, the action is the .click() and the list of elements selected by cy.xpath('//span[text()="Id"]//following::a[contains(#href,"maps")]') has been refreshed, so the list that Cypress is iterating over is no longer valid.
One approach to solving this is to separate the test into two loops.
const links = []; // save link info here
const selector = '//span[text()="Id"]//following::a[contains(#href,"maps")]';
cy.xpath(selector)
.each(($el, index) => {
const id_text = $el.text()
links.push(id_text)
cy.xpath(selector).eq(index)
.as(`maps${index}`) // save a unique alias for this link
})
cy.then(function() {
links.forEach((link, index) => {
// Check that the element is visible and click on it
cy.get(`#maps${index}`) // get the element from the alias
.should('be.visible')
.click()
//Check that the url contains the text value of the element that was clicked on
cy.url().should('contain', link)
cy.go('back') // return to start page
})
})
You can shorten your code like this:
cy.get('[href*="maps"]').each(($el) => {
let id_text = $el.text().trim()
//Check that the element is visible and click on it
cy.wrap($el).should('be.visible').click()
//Check that the url contains the text value of the element that was clicked on
cy.url().should('contain', id_text)
//Wait half a sec
cy.wait(500)
})
Found a solution, it may (probably isn't) the best way but hey it works! This loops through all numbers in the column I needed, goes into the next page, checks that the url contains the number it clicked on, then goes back.
//Get length of table
cy.get('.MuiTableBody-root')
.find('tr')
.then((row) => {
let num_rows = row.length
for(let i=1; i < num_rows; i++){
cy.get(`:nth-child(${i}) > .column-id > a`).then(($btn) => {
let id_txt = $btn.text()
//Click on ID Number
cy.get(`:nth-child(${i}) > .column-id > a`)
.click()
//Check that the url has the id_txt
cy.url()
.and('contain',id_txt)
cy.go(-1) //Go back
}) //Ends the Numbers of IDs loop
} //Ends for loop
}) //Ends the then((row)) loop
My list is being populated with this block of code:
function addToHistory(cityName) {
let searchHistory = JSON.parse(localStorage.getItem("Weather Search History")) || [];
searchHistory.push(cityName);
localStorage.setItem("Weather Search History", JSON.stringify(searchHistory));
};
function updateHistory() {
let searchHistory = JSON.parse(localStorage.getItem("Weather Search History")) || [];
$("#searchHistory").html(searchHistory
.map(searchHistoryList => {
return (`<li><button class="btn btn-link"> ` + searchHistoryList + `</button></li>`);
})
.join(""));
};
and that works great. It pulls from an array in local storage that is created each time the user enters a search term. Then populates the site's sidebar with said list.
However, I'm not sure how to then take the text values of the buttons out so that I may manipulate it.
Currently have:
$('#searchHistory').on('click', function () {
console.log($(???).val());
});
You want .text() or innerText (plain JavaScript). this refers to the current element. You can also use event.target.
$('#searchHistory').on('click', function () {
console.log($(this).text());
});
Try this in your function:
console.log($(this).innerHTML());
"this" refers to the specific element that triggered the click event.
I'm generating a div dynamically and I've to check whether a dynamically generated div exists or not ? How can I do that?
Currently I'm using the following which does not detects the div generated dynamically. It only detects if there is already an element with the id contained in the HTML template.
$(function() {
var $mydiv = $("#liveGraph_id");
if ($mydiv.length){
alert("HHH");
}
});
How can I detect the dynamically generated div?
If mutation observes aren't an option due to their browser compatibility, you'll have to involve the code that's actually inserting the <div> into the document.
One options is to use a custom event as a pub/sub.
$(document).on('document_change', function () {
if (document.getElementById('liveGraph_id')) {
// do what you need here
}
});
// without a snippet to go on, assuming `.load()` for an example
$('#container').load('/path/to/content', function () {
$(this).trigger('document_change');
});
If it is added dinamically, you have to test again. Let's say, a click event
$("#element").click(function()
{
if($("#liveGraph_id").length)
alert("HHH");
});
How you inserting your dynamic generated div?
It works if you do it in following way:
var div = document.createElement('div');
div.id = 'liveGraph_id';
div.innerHTML = "i'm dynamic";
document.getElementsByTagName('body')[0].appendChild(div);
if ($(div).length > 0) {
alert('exists'); //will give alert
}
if ($('#liveGraph_id').length > 0) {
alert('exists'); //will give alert
}
if ($('#liveGraph_id_extra').length > 0) {
alert('exists'); //wont give alert because it doesn't exist.
}
jsfiddle.
Just for interest, you can also use a live collection for this (they are provided as part of the DOM). You can setup a collection of all divs in the page (this can be done in the head even before the body is loaded):
var allDivs = document.getElementsByTagName('div');
Any div with an id is available as a named property of the collection, so you can do:
if (allDivs.someId) {
// div with someId exists
}
If the ID isn't a valid identifier, or it's held in a variable, use square bracket notation. Some play code:
<button onclick="
alert(!!allDivs.newDiv);
">Check for div</button>
<button onclick="
var div = document.createElement('div');
div.id = 'newDiv';
document.body.appendChild(div);
">Add div</button>
Click the Check for div button and you'll get false. Add the div by clicking the Add div button and check again—you'll get true.
is very simple as that
if(document.getElementById("idname")){
//div exists
}
or
if(!document.getElementById("idname")){
// don't exists
}
The function below allows users to filter products by data-attributes, and accommodates filtering by multiple values simultaneously. It does this by creating an array of the values selected, and when any of the values are clicked (in this case checked/unchecked) it hides all the items and then re-shows those that match the values in the updated array.
It works correctly when filtering for one data-attribute, but when combined to filter by more than one attribute it no longer shows all results matching any of the values and instead only shows results matching all the specified values.
I've posted a fiddle which demonstrates the problem here: http://jsfiddle.net/chayacooper/WZpMh/94/ All but one of the items have the values of both data-style="V-Neck" and data-color="Black" and they should therefore remain visible if either of the filters are selected, but if another value from a different data-attribute some of the items are hidden.
$(document).ready(function () {
var selected = [];
$('#attributes-Colors *').click(function () {
var attrColor = $(this).data('color');
var $this = $(this);
if ($this.parent().hasClass("active")) {
$this.parent().removeClass("active");
selected.splice(selected.indexOf(attrColor),1);
}
else {
$this.parent().addClass("active");
selected.push(attrColor);
}
$("#content").find("*").hide();
$.each(selected, function(index,item) {
$('#content').find('[data-color *="' + item + '"]').show();
});
return false;
});
$('#attributes-Silhouettes *').click(function () {
var attrStyle = $(this).data('style');
var $this = $(this);
if ($this.parent().hasClass("active")) {
$this.parent().removeClass("active");
selected.splice(selected.indexOf(attrStyle),1);
}
else {
$this.parent().addClass("active");
selected.push(attrStyle);
}
$("#content").find("*").hide();
$.each(selected, function(index,item) {
$('#content').find('[data-style *="' + item + '"]').show();
});
return false;
});
});
Both of your handlers are updating the selected array, but only one handler executes on a click. The first one if a color was (de)selected, the second if a style. Let's say you've clicked on "Black" and "Crew Neck". At that time your selected array would look like this: [ "Black", "Crew_Neck" ]. The next time you make a selection, let's say you click "Short Sleeves", the second (style) handler executes. Here's what is happening:
Short_Sleeves gets added to the selected array.
All of the items are hidden using $("#content").find("*").hide();
The selected array is iterated and items are shown again based on a dynamic selector.
Number 3 is the problem. In the above example, a style was clicked so the style handler is executing. Any items in the selected array that are colors will fail because, for example, no elements will be found with a selector such as $('#content').find('[data-style *="Black"]').show();.
I would suggest 2 things.
Keep 2 arrays of selections, one for color, one for style.
Combine your code to use only a single handler for both groups.
Here's a (mostly) working example.
Note that I added a data-type="color|style" to your .filterOptions containers to allow for combining to use a single handler and still know which group was changed.
Here's the full script:
$(document).ready(function () {
// use 2 arrays so the combined handler uses correct group
var selected = { color: [], style: [] };
// code was similar enough to combine to 1 handler for both groups
$('.filterOptions').on("click", "a", function (e) {
// figure out which group...
var type = $(e.delegateTarget).data("type");
var $this = $(this);
// ...and the value of the checkbox checked
var attrValue = $this.data(type);
// same as before but using 'type' to access the correct array
if ($this.parent().hasClass("active")) {
$this.parent().removeClass("active");
selected[type].splice(selected[type].indexOf(attrValue),1);
}
else {
$this.parent().addClass("active");
selected[type].push(attrValue);
}
// also showing all again if no more boxes are checked
if (attrValue == 'All' || $(".active", ".filterOptions").length == 0) {
$('#content').find('*').show();
}
else {
// hide 'em all
$("#content").find("*").hide();
// go through both style and color arrays
for (var key in selected) {
// and show any that have been checked
$.each(selected[key], function(index,item) {
$('#content').find('[data-' + key + ' *="' + item + '"]').show();
});
}
}
});
});
UPDATE: incorporating suggestions from comments
To make the handler work with checkboxes instead of links was a small change to the event binding code. It now uses the change method instead of click and listens for :checkbox elements instead of a:
$('.filterOptions').on("change", ":checkbox", function (e) {
// handler code
});
The "All" options "hiccup" was a little harder to fix than I thought it would be. Here's what I ended up with:
// get a jQuery object with all the options the user selected
var checked = $(":checked", ".filterOptions");
// show all of the available options if...
if (checked.length == 0 // ...no boxes are checked
|| // ...or...
checked.filter(".all").length > 0) // ...at least one "All" box is checked...
{
// remainder of code, including else block, unchanged
}
I also added an all class to the appropriate checkbox elements to simplify the above conditional.
Updated Fiddle