i got an anchor in the DOM and the following code replaces it with a fancy button. This works well but if i want more buttons it crashes. Can I do it without a for-loop?
$(document).ready(buttonize);
function buttonize(){
//alert(buttonAmount);
//Lookup for the classes
var button = $('a.makeabutton');
var buttonContent = button.text();
var buttonStyle = button.attr('class');
var link = button.attr('href');
var linkTarget = button.attr('target');
var toSearchFor = 'makeabutton';
var toReplaceWith = 'buttonize';
var searchButtonStyle = buttonStyle.search(toSearchFor);
if (searchButtonStyle != -1) {
//When class 'makeabutton' is found in string, build the new classname
newButtonStyle = buttonStyle.replace(toSearchFor, toReplaceWith);
button.replaceWith('<span class="'+newButtonStyle
+'"><span class="left"></span><span class="body">'
+buttonContent+'</span><span class="right"></span></span>');
$('.buttonize').click(function(e){
if (linkTarget == '_blank') {
window.open(link);
}
else window.location = link;
});
}
}
Use the each method because you are fetching a collection of elements (even if its just one)
var button = $('a.makeabutton');
button.each(function () {
var btn = $(this);
var buttonContent = btn.text();
var buttonStyle = btn.attr('class');
var link = btn.attr('href');
var linkTarget = btn.attr('target');
var toSearchFor = 'makeabutton';
var toReplaceWith = 'buttonize';
var searchButtonStyle = buttonStyle.search(toSearchFor);
...
};
the each method loops through all the elements that were retrieved, and you can use the this keyword to refer to the current element in the loop
var button = $('a.makeabutton');
This code returns a jQuery object which contains all the matching anchors. You need to loop through them using .each:
$(document).ready(buttonize);
function buttonize() {
//alert(buttonAmount);
//Lookup for the classes
var $buttons = $('a.makeabutton');
$buttons.each(function() {
var button = $(this);
var buttonContent = button.text();
var buttonStyle = button.attr('class');
var link = button.attr('href');
var linkTarget = button.attr('target');
var toSearchFor = 'makeabutton';
var toReplaceWith = 'buttonize';
var searchButtonStyle = buttonStyle.search(toSearchFor);
if (searchButtonStyle != -1) {
newButtonStyle = buttonStyle.replace(toSearchFor, toReplaceWith);
button.replaceWith('<span class="'
+ newButtonStyle
+ '"><span class="left"></span><span class="body">'
+ buttonContent
+ '</span><span class="right"></span></span>');
$('.buttonize').click(function(e) {
if (linkTarget == '_blank') {
window.open(link);
} else window.location = link;
}); // end click
} // end if
}); // end each
}
Related
I have a loop in which I am calling rec_append() recursively, apparently the first pass alone works, then the loop stops.
I have an array of 4 elements going into that $.each loop but I see only the first element going into the function recursively. Help!
I switched it for a element.forEach but that gives me only the second element and I am stuck, is there a better solution to process a tree of elements? My array is a part of a tree.
var data = JSON.parse(JSON.stringify(result))
var graph = $(".entry-point");
function rec_append(requestData, parentDiv) {
var temp_parent_details;
$.each(requestData, function (index, jsonElement) {
if (typeof jsonElement === 'string') {
//Element construction
//Name and other details in the form of a : delimited string
var splitString = jsonElement.split(':');
var details = document.createElement("details");
var summary = document.createElement("summary");
summary.innerText = splitString[0];
details.append(summary);
temp_parent_details = details;
parentDiv.append(details);
var kbd = document.createElement("kbd");
kbd.innerText = splitString[1];
summary.append(' ');
summary.append(kbd);
var div = document.createElement("div");
div.className = "col";
details.append(div);
var dl = document.createElement("dl");
div.append(dl);
var dt = document.createElement("dt");
dt.className = "col-sm-1";
dt.innerText = "Path";
div.append(dt);
var dd = document.createElement("dd");
dd.className = "col-sm-11";
dd.innerText = splitString[2];
div.append(dd);
var dt2 = document.createElement("dt");
dt2.className = "col-sm-1";
dt2.innerText = "Type";
div.append(dt2);
var dd2 = document.createElement("dd");
dd2.className = "col-sm-11";
dd2.innerText = splitString[1];
div.append(dd2);
} else {
$.each(jsonElement, function (jsonElementArrIndx, jsonChildElement) {
rec_append(jsonChildElement, temp_parent_details); //Only 1 pass works, rest skip
});
}
});
}
rec_append(data, graph);
Sample data:enter image description here
So I'm trying to get a list of JSON objects and then loop through its elements via fori loop, creating <li> and <a> elements.
Everything works smoothly except when I try to add an onclick function to the in each iteration. I'm trying to pass the json object to the onclick function, but all tags have are showing the last json object.
What I need is for every <a> to show its corresponding json object when clicked.
Please see the code:
var url = "https://restcountries.eu/rest/v2/all"
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var countries = JSON.parse(xhttp.responseText)
var ul = document.getElementById('countries')
for (var i = 0; i < countries.length; i++) { //start iteration
var li = document.createElement("LI")
var img = document.createElement("IMG")
var a = document.createElement("A")
var textNode = document.createTextNode(countries[i].name)
img.setAttribute("src", countries[i].flag)
ul.appendChild(li)
li.appendChild(a)
a.appendChild(img)
a.appendChild(textNode);
a.setAttribute("title", countries[i].name)
var country = countries[i]
a.onclick = function(){ CountryClicked(country)} //pass the current JSON to the onclick function of the current <a> Node, this isn't working correctly.
}
}
}
xhttp.open('GET', url, true);
xhttp.send();
function CountryClicked(country){
var div = document.getElementById("parent-country-container")
var h1 = document.createElement("h1")
// div.appendChild(h1)
h1.appendChild(document.createTextNode(country.name + " Clicked"))
div.innerHTML = ""
div.appendChild(h1)
}
Thank you for your time.
It is neccesary to use a closures function to be able to send the right country.
function CountryClicked(el) {
alert(el.name);
}
var countries = [{'flag':'xx',name:'name'}, {'flag':'xx',name:'text 2'}];
var ul = document.getElementById('countries')
for (var i = 0; i < countries.length; i++) { //start iteration
var li = document.createElement("LI")
var img = document.createElement("IMG")
var a = document.createElement("A")
var textNode = document.createTextNode(countries[i].name)
img.setAttribute("src", countries[i].flag)
ul.appendChild(li)
li.appendChild(a)
a.appendChild(img)
a.appendChild(textNode);
a.setAttribute("title", countries[i].name)
var country = countries[i];
a.onclick = (function(country){
return function(){ CountryClicked(country); }
})(country);
}
<ul id="countries"></ul>
I developed the store locator using open street map and leaflet. The problem is when I want to type in searchbox it will become lagging to finish the word. That store locator read from the CSV file that has 300++ data. Below is the code for the searchbox:
var locationLat = [];
var locationLng = [];
var locMarker;
var infoDiv = document.getElementById('storeinfo');
var infoDivInner = document.getElementById('infoDivInner');
var toggleSearch = document.getElementById('searchIcon');
var hasCircle = 0;
var circle = [];
//close store infor when x is clicked
var userLocation;
$("#infoClose").click(function() {
$("#storeinfo").hide();
if (map.hasLayer(circle)) {
map.removeLayer(circle);
}
});
var listings = document.getElementById('listingDiv');
var stores = L.geoJson().addTo(map);
var storesData = omnivore.csv('assets/data/table_1.csv');
function setActive(el) {
var siblings = listings.getElementsByTagName('div');
for (var i = 0; i < siblings.length; i++) {
siblings[i].className = siblings[i].className
.replace(/active/, '').replace(/\s\s*$/, '');
}
el.className += ' active';
}
function sortGeojson(a,b,prop) {
return (a.properties.name.toUpperCase() < b.properties.name.toUpperCase()) ? -1 : ((a.properties.name.toUpperCase() > b.properties.name.toUpperCase()) ? 1 : 0);
}
storesData.on('ready', function() {
var storesSorted = storesData.toGeoJSON();
//console.log(storesSorted);
var sorted = (storesSorted.features).sort(sortGeojson)
//console.log(sorted);
storesSorted.features = sorted;
//console.log(storesSorted)
stores.addData(storesSorted);
map.fitBounds(stores.getBounds());
toggleSearch.onclick = function() {
//var s = document.getElementById('searchbox');
//if (s.style.display != 'none') {
//s.style.display = 'yes';
//toggleSearch.innerHTML = '<i class="fa fa-search"></i>';
//$("#search-input").val("");
//search.collapse();
//document.getElementById('storeinfo').style.display = 'none';
//$('.item').show();
//} else {
//toggleSearch.innerHTML = '<i class="fa fa-times"></i>';
//s.style.display = 'block';
//attempt to autofocus search input field when opened
//$('#search-input').focus();
//}
};
stores.eachLayer(function(layer) {
//New jquery search
$('#searchbox').on('change paste keyup', function() {
var txt = $('#search-input').val();
$('.item').each(function() {
if ($(this).text().toUpperCase().indexOf(txt.toUpperCase()) != -1) {
$(this).show();
} else {
$(this).hide();
}
});
});
I dont know what is the cause of the lag in the search box. It is something wrong in code or the csv file? Thank you
Every iteration of $('.item').each is causing a layout change because $(this).hide() or $(this).show() causes the item to removed/added to the DOM as the style is set to display:none back and forth. DOM manipulations and the corresponding layout changes are expensive.
You can consider accumulating the changes and doing one batch update to the DOM using a function like appendChild
In the following code, I have some divs with <a> tags, each containing a href of /User.aspx?ID=[some ID]. I wish to click the Delete <a> tag under the parent of the parent of the parent of any divs with duplicate ID's in the href.
Here is my code:
var z = 0;
var info = {};
$("a:contains('Delete')").each(function() {
z++;
var x = $(this).parent().parent().parent().find("span.UserLink a").attr("href");
var id = x.replace("/User.aspx?ID=", "");
info[z] = id;
console.log(info[z]);
});
var uniqueIds = {};
$.each(info, function(i, el){
if($.inArray(el, uniqueIds) === -1) { uniqueIds.push(el) }
else { $("html").find("span.UserLink a[href='/User.aspx?ID='"+info[i]+"']").parent().parent().parent().find("a:contains('Delete')").click() }
});
Use arrays, not objects
Maybe just a typo, I think you wanted to use arrays for info and uniqueIds
var info = [];
var uniqueIds = [];
jQuery.each already provides an index
You don't need z
$("a:contains('Delete')").each(function(index) {
var x = $(this).parent().parent().parent().find("span.UserLink a").attr("href");
var id = x.replace("/User.aspx?ID=", "");
info[index] = id;
console.log(info[z]);
});
Use meaningful names
x and info aren't very good names, you could try (for example) userLinkHref and foundIds
You could store the delete button in the first loop and use it in the second loop
var foundDeleteLinks = [];
$("a:contains('Delete')").each(function() {
var $deleteLink = $(this);
var userLinkHref = $deleteLink.parent().parent().parent().find("span.UserLink a").attr("href");
var id = userLinkHref.replace("/User.aspx?ID=", "");
foundDeleteLinks.push({id:id,$deleteLink:$deleteLink});
console.log(id);
});
var uniqueIds = [];
$.each(foundDeleteLinks, function(i, deleteLink){
var id = deleteLink.id;
if($.inArray(id, uniqueIds) === -1) { uniqueIds.push(id) }
else {
deleteLink.$deleteLink.click();
}
});
You may be able to do it in one loop
var foundIds = [];
$("a:contains('Delete')").each(function() {
var $deleteLink = $(this);
var userLinkHref = $deleteLink.parent().parent().parent().find("span.UserLink a").attr("href");
var id = userLinkHref.replace("/User.aspx?ID=", "");
if($.inArray(id, foundIds) === -1) { foundIds.push(id) }
else { $deleteLink.click(); }
});
I hope that helps enough to find your problems.
I am trying to make a debugger that will be dynamiaclly created with some variables. The names on the left div need to show a div for the corresponding variables Description,Variable ID, and initial Value as well as another div that will show history and lock status when variables are updated later. Where I am having trouble is properly adding the show/hide to the dom I think. Everything starts hidden and then when I click a name the Variables for that name show up but the next click doesn't hide the values from the former. Also any cleanup/optimization advice?
<script type="text/javascript">
var variableIDArray = {};
function loadVariables(variables) {
if (typeof variables != "object") { alert(variables); return; }
var namearea = document.getElementById('namearea');
var description = document.getElementById('description');
var varid = document.getElementById('varid');
var initialvalue = document.getElementById('initialvalue');
var valuelock = document.getElementById('valuelock');
for (var i = 0; i < variables.length - 1; i++) {
var nameDiv = document.createElement('div');
nameDiv.id = variables[i].variableID + "namearea";
nameDiv.className = "nameDiv";
nameDiv.onclick = (function (varid) {
return function () { showvariable(varid); };
})(variables[i].variableID);
nameDiv.appendChild(document.createTextNode(variables[i].name));
namearea.appendChild(nameDiv);
var descriptionDiv = document.createElement('div');
descriptionDiv.id = variables[i].variableID + "description";
descriptionDiv.className = "descriptionDiv";
descriptionDiv.appendChild(document.createTextNode("Description : " + variables[i].description));
description.appendChild(descriptionDiv);
var varidDiv = document.createElement('div');
varidDiv.id = variables[i].variableID + "varid";
varidDiv.className = "varidDiv";
varidDiv.appendChild(document.createTextNode("Var ID : " + variables[i].variableID));
varid.appendChild(varidDiv);
var initialvalueDiv = document.createElement('div'); ;
initialvalueDiv.id = variables[i].variableID + "initialvalue";
initialvalueDiv.className = "initialvalueDiv";
initialvalueDiv.appendChild(document.createTextNode("Initial Value : " + variables[i].value));
initialvalue.appendChild(initialvalueDiv);
var valuelockDiv = document.createElement('div');
valuelockDiv.id = variables[i].variableID + "valuelock";
valuelockDiv.className = "valuelockDiv ";
valuelockDiv.appendChild(document.createTextNode("Value : " + variables[i].value));
valuelockDiv.appendChild(document.createTextNode("Lock : " + variables[i].locked.toString()));
valuelock.appendChild(valuelockDiv);
variableIDArray[variables[i].variableID];
}
};
function showvariable(varid) {
for (v in variableIDArray)
hide(variableIDArray[v]);
show(varid + "description");
show(varid + "varid");
show(varid + "initialvalue");
show(varid + "valuelock");
}
function show(elemid) {
document.getElementById(elemid).style.display = "block";
}
function hide(elemid) {
document.getElementById(elemid).style.display = "none";
}
Yes. jQuery. Will reduce your code to about 6 lines. :) http://jquery.com