I have been working on this question for several days, and have researched it on SO as well as the web at large and was unable to find material that helped me solve it.
I am trying to create a weather app that can toggle the weather units displayed between Fahrenheit and Celsius. I start by appending the weather in Fahrenheit, and then I have created an event handler that conditionally changes the inner content of the associated element based on whether that element is currently displaying "F" or "C".
As it is, my app successfully loads with the Fahrenheit temperature, and toggles to Celsius on click, but it will not toggle back to Fahrenheit. I assume there is some issue with how the events are registered, but for the life of me I cannot figure it out.
Here is my code:
var fahr = document.createElement("a");
fahr.attr = ("href", "#");
fahr.className = "tempUnit";
fahr.innerHTML = tempf + "°F" + "<br/>";
$("#currentWeather").append(fahr);
var cels = document.createElement("a");
cels.attr = ("href", "#");
cels.className = "tempUnit";
cels.innerHTML = tempc + "°C" + "<br/>";
var units = document.getElementsByClassName("tempUnit");
$(".tempUnit").click(function() {
if (units[0].innerHTML.indexOf("F") != -1) {
$(".tempUnit").replaceWith(cels);
} else {
$(".tempUnit").replaceWith(fahr);
}
})
Thank you so much in advance! Happy to provide additional information if necessary.
Currently what you are using is called a direct binding which will only attach to element that exist on the page at the time your code makes the event binding call.
As you using replaceWith(), existing element is replaced with new element and event handlers are not attached with them.
You need to use Event Delegation using .on() delegated-events approach.
General Syntax
$(parentStaticContainer).on('event','selector',callback_function)
Example, Also use this i.e. current element context and use setAttribute() to update href element
$("#currentWeather").on("click", ".tempUnit", function() {
if (this.innerHTML.indexOf("F") != -1) {
$(this).replaceWith(cels);
}
else {
$(this).replaceWith(fahr);
}
})
var tempf = 212;
var tempc = 100;
var fahr = document.createElement("a");
fahr.setAttribute("href", "#");
fahr.className = "tempUnit";
fahr.innerHTML = tempf + "°F" + "<br/>";
$("#currentWeather").append(fahr);
var cels = document.createElement("a");
cels.setAttribute("href", "#");
cels.className = "tempUnit";
cels.innerHTML = tempc + "°C" + "<br/>";
$("#currentWeather").on("click", ".tempUnit", function() {
if (this.innerHTML.indexOf("F") != -1) {
$(this).replaceWith(cels);
} else {
$(this).replaceWith(fahr);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="currentWeather"></div>
Related
I would like to set up event handlers for all “Add” buttons.
are you fine with a Vanilla JS snippet?
Something along these lines would do:
function registerHandlers () {
var buttons = document.querySelectorAll('.button');
[].slice.call(buttons).forEach(function (button) {
button.addEventListener('click', onClick, false);
});
}
function onClick (event) {
event.preventDefault();
var button = event.target;
var id = button.id;
var desc = document.getElementById(id + '-img').getAttribute('title');
var qty = document.getElementById(id + '-qty').value;
addToTable(desc, qty);
}
function addToTable (desc, qty) {
var row = '<tr><td align="left">' + desc + '</td><td align="right">' + qty + '</td></tr>';
var tbody = document.querySelector('#orderlist tbody');
tbody.innerHTML = tbody.innerHTML + row;
}
registerHandlers();
The code is untested :-)
But here's how it works:
registerHandlers:
Find all elements on the page which have the class "button" (via CSS Selector)
Turn the results from a NodeList into an Array using [].slice.call.
Go over the list and register an event listener for "click"s on that element.
onClick:
Stop the default behaviour of the browser.
Determine the clicked button by inspecting the target property of an event.
Get the associated ID attribute's value.
Build the selector string for the image, search the whole document for it. Read the title attribute of the found image. (This can crash if no such element was found).
Same with the Quantity.
Update the table.
addToTable:
Build a string with the desc and qty in between. You might want to put more effort by using createElement or DOMParser and get some sanity checks that way.
Find the <tbody> of the orderlist.
Append it's innerHTML with your new row.
Finally call registerHandler to make the above work.
JS only. On mouseover I'm calling a function I made that creates a div element with an image inside.
I pass (this) as a parameter to the function. The function works and onmouseover it creates a child element and I can click it. However, If I add on mouse out of the div to remove itself, it will only do so if I hovered over it. If I didn't, the div stays and on next hover it adds another one. If I add on mouse out of the parent element to remove the div, I cannot get to hover over the child div, cause as soon as I leave the parent, the child div is removed. The parent element is an (a href) inside a "TD" in a table. The code goes like this:
<script type="text/javascript">
function PopPanel(ownerElem) {
var myParent = ownerElem.parentNode;
var popanel = document.createElement("div");
popanel.className = "divPopPanel";
popanel.setAttribute("display", "block")
var phoneimg = document.createElement("img");
phoneimg.src = '/images/ImageAdditions/Phone.png';
phoneimg.className = "popupPhone";
popanel.appendChild(phoneimg);
phoneimg.onclick = function () {
try {
location.replace("Mylauncher:\\\\nas\\vol5\\SYSTEM\\ITR\\Scripts\\SomeProgram.exe" + " " + ownerElem.innerText);
}
catch (err) {
}
};
myParent.appendChild(popanel);
popanel.onmouseout = function (e) { this.parentNode.removeChild(this) }; //this removes itself on mouseout.
myParent.onmouseout = function (e) { popanel.parentNode.removeChild(popanel) }; // this removes the child element of the parent (which is the same element as above) on mouse out.
};
Well, after a long and miserable trial and error session, I've figured this out.
First I've modified the code that generates and populates the gridview with data, like this:
VB.net
dt.Columns.Add("InternalPhoneDialer", Type.GetType("System.String"))
Dim rn As New Random
Dim randNum As Integer = rn.Next(12, 428)
Dim internalphone As String = dr("InternalPhone").ToString
If internalphone.Contains(" ") Then
internalphone = internalphone.Substring(0, internalphone.IndexOf(" "))
internalphone = internalphone & randNum.ToString()
Else
internalphone = internalphone & randNum.ToString()
End If
//Substitute the current column with the newly created one above
dr("InternalPhoneDialer") = "<div id='popPanelWrapper" & internalphone & "' onmouseover='PopPanel(" & "popPanelWrapper" & internalphone & ");' onmouseleave='PopPanelClose(" & "popPanelWrapper" & internalphone & ");'> <a class='popPanelLink' href='javascript:void(0);' >" & dr("InternalPhone") & "</a> </div>"
I have made sure that I concatenate a unique id to each div in case the phone number is the same for another column (where I implement the same solution). So I added the column inner content + a random number and concatenated it to the DIV name.
Then, on client side I've modified my script like this:
JavaScript
<script type="text/javascript">
function PopPanel(ownerElem) {
var myParent = ownerElem;
var phoneimgexist = !!document.getElementById("popupPhone");
if (phoneimgexist) {
return
} else {
var phoneimg = document.createElement("img");
phoneimg.src = '/_layouts/15/images/ImageAdditions/Phone.png';
phoneimg.id = "popupPhone";
phoneimg.setAttribute("display", "block")
myParent.appendChild(phoneimg);
}
phoneimg.onclick = function () {
try {
location.replace("launcher:\\\\drive01\\vol1\\SYSTEM\\ITR\\Scripts\\Jabber.exe" + " " + ownerElem.innerText);
}
catch (err) {
}
};
};
function PopPanelClose(ownerElem) {
var myParent = ownerElem;
var phoneimg = document.getElementById("popupPhone");
var phoneimgexist = !!document.getElementById("popupPhone");
if (phoneimgexist) {
phoneimg.parentNode.removeChild(phoneimg);
} else {
return
}
};
Now, on mouse over a GridVew cell that contains a phone number I get an icon. By clicking it I can call the number.
In my opinion this solution is much better suited for the task, instead of creating a hidden div with image and data for every row in what could be thousands of entries in the GridView.
This no doubt saves a lot of resources.
What I am trying to do is to call a function every time a person scrolls that checks the current class of container and adds +1 to the current value of the current data attribute and then toggles the class relative to the data attribute it is currently changing the class on scroll but giving a "NaN. I am already running this function on click and it works fine.
here is a fiddle.
http://jsfiddle.net/kaL63/1/
This is my function on scroll
var timeout;
$(window).scroll(function() {
if(typeof timeout == "number") {
window.clearTimeout(timeout);
delete timeout;
}
timeout = window.setTimeout( check, 100);
});
My html Looks like this
<div class="container year-1987" data-year-index="1987">
Some Content
</div>
the function I am calling right now that I think should work..
function check(){
var
animationHolder = $('.container'),
currentClass = animationHolder.attr("class").match(/year[\w-]*\b/);
var goToYear = $('.container').data('year-index');
var goToYear2 = parseInt(goToYear,1000) + 1;
animationHolder.toggleClass(currentClass + ' year-' + goToYear2);
animationHolder.attr('data-year-index', goToYear2);
}
My working code on click
$("a").click(function (e) {
e.preventDefault();
var
animationHolder = $('.container'),
currentClass = animationHolder.attr("class").match(/year[\w-]*\b/);
var goToYear = $(this).data('year-index');
animationHolder.toggleClass(currentClass + ' year-' + goToYear);
animationHolder.attr('data-year-index', goToYear);
I rewrote your check method:
function check() {
var $container = $('.container'),
currentYear = $container.data('m-index'),
nextYear = 1 + currentYear;
$container.removeClass('m-' + currentYear).addClass('m-' + nextYear);
$container.data('m-index', nextYear);
}
I made the following changes:
There is no need for the regular expression since we can generate the class name ourselves.
I am not sure why you were originally using two separate data-attributes (m-index and year-index), but I switched them to both match. If you need both of them, some more logic is needed to use year-index after the initial call.
I am now updating m-index via .data() rather than setting a data attribute.
This method seemed to work fine for me.
I'm currently building a small Todo list application using vanilla Javascript but I'm having some issues creating a delete button that onClick removes it's parent element.
From what I have read, when an onClick is called in Javascript the this keyword can be used to refer to the element that called the function. With this in mind I have the following code:
window.onload = initialiseTodo;
function addRecord(){
var title = document.getElementById('issueTitle');
var issueContent = document.getElementById('issueContent');
var contentArea = document.getElementById('contentArea');
if(title.value.length > 0 && issueContent.value.length > 0){
var newItem = document.createElement('div');
newItem.id = 'task' + count++;
newItem.className = 'task';
newItem.innerHTML = '<div class="taskbody"><h1>' + title.value + '</h1>'+ issueContent.value + '</div><div class="deleteContainer">'
+ '<a class="delete">DELETE</a></div>';
contentArea.appendChild(newItem);
assignDeleteOnclick();
}
}
function deleteRecord(){
this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
}
function assignDeleteOnclick(){
var deleteArray = document.getElementsByClassName('delete');
for(var i=0;i<deleteArray.length;i++){
deleteArray[i].onclick= deleteRecord();
}
}
function initialiseTodo(){
var btn_addRecord = document.getElementById('addRecord');
btn_addRecord.onclick = addRecord;
}
Basically I have a form that has two fields. When these fields are filled and the addRecord button is clicked a new div is added at the bottom of the page. This div contains a delete button. After the creation of this I assign an onclick event to the delete button which assigns the deleteRecord function when the delete button is clicked. My issue is with the deleteRecord function. I have used this to refer to the calling element (the delete button) and wish to remove the task div that is the outermost container however I current get a message that says: 'Cannot read property 'parentNode' of undefined ' which suggests to me the this keyword is not working correctly.
Any help would be greatly appreciated.
I've added the full code to a fiddle.
http://jsfiddle.net/jezzipin/Bd8AR/
J
You need to provide the element itself as a parameter. I did so by changing the html to include onclick="deleteRecord(this)" to make it a little easier to deal with. This means you can remove the assignDeleteOnclick() function
function deleteRecord(elem){
elem.parentNode.parentNode.remove();
}
Demo
You might style the .content to be hidden better if there are no elements to prevent that extra white space
Edit
Since you don't want an inline onclick, you can do it with js the same:
function deleteRecord(elem){
elem.parentNode.parentNode.remove();
}
function assignDeleteOnclick(){
var deleteArray = document.getElementsByClassName('delete');
for(var i=0;i<deleteArray.length;i++){
// Has to be enveloped in a function() { } or else context is lost
deleteArray[i].onclick=function() { deleteRecord(this); }
}
}
Demo
I'm working on this menu-system that's very similar to how operating systems do them.
Using jquery etc.
I have 2 comments down in the For Loop. It's basically outputting the last index each in the $(document).on('click')... function. But outside the document.on it works fine.
It's probably just an obvious problem but I've spent about an hour on this.. Thanks in advance!
menu: function(title) {
this.title = title;
this.slug = slugify(title);
this.icon = false;
this.buttons = Object();
this.num_buttons = 0;
this.visible = false;
this.timeout_id = null;
this.is_hovering_dropdown = false;
this.is_hovering_menu = false;
this.render = function() {
var that = this;
var slug = that.slug;
var str = '<li id="menu-' +slug +'">' + this.title + '';
if (this.num_buttons > 0) {
str += '<ul id="menu-dropdown-' + slug + '" style="display: none;" class="dropdown">';
for (var button in this.buttons) {
str += '<li>' +that.buttons[button]['title'] +'</li>'
alert(button) //new project, open project, save as etc.
$(document).on("click", "#menu-dropdown-" +slug + '-' + that.buttons[button]['slug'], function() {
$("#menu-dropdown-" + slug).hide("fade", 200);
that.visible = false;
alert(button);//save as, save as, save as, save as etc.
});
}
}
}
}
Here you go:
Thanks to the order of operations, and scoping, all of your buttons are being saved with a reference to the LAST value of button.
What you want to do is put that assignment inside of an immediately-invoking function, and pass the button into that particular function-scope.
(function (button) { $(document). //...... }(button));
Everything inside of the immediate function should still have access to the static stuff outside of the immediate-function's scope (ie: that), AND it will also have a reference to the current value of button, as it's being invoked then and there.
The longer version of the story is that your buttons, when being created are being given a reference to button, rather than the value of button, therefore, when they're actually invoked at a later time, they reference the value of button as it currently exists (ie: the last value it was assigned in the loop).