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).
Related
I'm trying to create a chrome extension. I had a problem with the affectation of event for the new element that i append to the dom of site with content. Js
If I add an event to an element' 'for example class' exist already in the page, it works correctly. Just for my new appended element((in the code iadded a button ,the event is just an alert to test))
function tst() {
myclass = $("._3hg-._42ft");
myclass = myclass.not(".supp");
myclass.addClass("supp");
var patt = /https:\/\/(.)*\.facebook\.com\/(.)*\/(posts|photos|videos)\/(\w|\.|\d)*/g;
for (i = 0; i < myclass.length; i++) {
result = patt.exec(myclass[i]);
myclass.append('<button class="fact" id=' + result[0] + ' style="position: absolute;">fact</button>');
};
/* this is a simple event*/
/***********************/
$(".fact").on('click', function() {
alert("no event work ");
});
Making somewhat broad assumption here in my answer that it is JavaScript/jQuery related and is NOT an extension...or is so still in that context.
You need to attach the event to the container here perhaps for the dynamically created elements. Lots of global stuff, suggested to not do that, updated there.
Appends a lot of buttons perhaps? might need to only hit DOM once but left as-is in this isolated function.
function tst() {
let myclass = $("._3hg-._42ft")
.not(".supp");
myclass.addClass("supp");
//let result = {};
var patt = /https:\/\/(.)*\.facebook\.com\/(.)*\/(posts|photos|videos)\/(\w|\.|\d)*/g;
var i = 0; //avoid global
for (i; i < myclass.length; i++) {
// broad assumption of the returned value from patt.exec() here
// not even sure why it needs an id, have a class, use for css
let result = patt.exec(myclass[i]);
myclass.append('<button class="fact" id="' + result[0] + '">fact</button>');
}
/* attache event to pre-existing element */
/***********************/
myclass.on('click', ".fact", function() {
alert("event works");
});
}
button.fact {
position: absolute;
}
I'm trying to create multiple lists on the same page with multiple "Add" buttons.
Entering text in text field 1 and clicking button1 should only add things into list1 (Monday). But button1 is adding text from text field 2 into the last loaded JS which is list2 (Tuesday). There are 8 lists in total I'm only trying to get the first 2 lists working atm.
Here's the JSFiddle: https://jsfiddle.net/sarwech/vrn5s2ns/4/
This looks like there isn't proper closure and I've considered creating separate, local functions but I'm not too sure...
The below worked when I only wanted to add items into each list:
document.getElementById("add1").onclick = function() {
var node = document.createElement("Li");
var text = document.getElementById("user_input1").value;
var textnode=document.createTextNode(text);
node.appendChild(textnode);
document.getElementById("list_item1").appendChild(node);
localStorage.setItem('monday', JSON.stringify(list_item1));
show();
return false; }
document.getElementById("add2").onclick = function() {
var node = document.createElement("Li");
var text = document.getElementById("user_input2").value;
var textnode=document.createTextNode(text);
node.appendChild(textnode);
document.getElementById("list_item2").appendChild(node); }
Appreciate any help!
This happens because you have two add function definitions. Because of hoisting the latter definition will suppress the former one. That's why you are actually binding tuesday click handler to both tuesday's and monday's add buttons.
You should consider rewriting your code to a reusable component, something like
function DayMeals(id, title) {
var self = this;
this.id = id;
this.$el = document.createElement('div');
this.$el.classList.add('row');
this.$el.innerHTML = '<h4>' + title + '</h4><input type="text"/><button>Add</button><ol></ol>';
this.$list = this.$el.querySelector('ol');
this.$input = this.$el.querySelector('input');
this.$addButton = this.$el.querySelector('button');
this.meals = this.getFromStorage();
this.updateView();
this.$addButton.addEventListener('click', function() {
self.add(self.$input.value);
});
this.$el.addEventListener('click', function(e) {
if (e.target.dataset.remove) {
self.remove(e.target.dataset.remove)
}
});
}
DayMeals.prototype.getFromStorage = function() {
var storage = localStorage.getItem(this.id);
return storage ? JSON.parse(storage) : [];
};
DayMeals.prototype.putToStorage = function() {
localStorage.setItem(this.id, JSON.stringify(this.meals));
};
DayMeals.prototype.add = function(meal) {
this.meals.push(meal);
this.putToStorage();
this.updateView();
};
DayMeals.prototype.remove = function(index) {
this.meals.splice(index, 1);
this.putToStorage();
this.updateView();
};
DayMeals.prototype.updateView = function() {
var listContent = '';
this.meals.forEach(function(meal, i) {
listContent += '<li>' + meal + '<button data-remove="' + i + '">x</button></li>';
});
this.$list.innerHTML = listContent;
};
fiddle
You have two functions named 'add', and last one is overriding first one. Name them properly, i.e. mondayAdd, tuesdayAdd. Same thing goes for other functions that share the same name: 'show', 'remove'.
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
My JavaScript code is:
<script type="text/javascript">
var current = 0;
var cars = new Array(5);
cars[0] = "Audi";
cars[1] = "Bentley";
cars[2] = "Mercedes";
cars[3] = "Mini";
cars[4] = "BMW";
document.getElementById("addCarBtn").onclick = function () {
if (!(current > cars.length - 1)) {
document.getElementById("carsDiv").innerHTML += cars[current] + "<br />";
current++;
}
}
</script>
I want to display the value of each array item one by one on button click the div.
But when i click the button, the array[0] i.e "Audi" is displayed but just for fraction of seconds. then it disappears and only the button is visible.
You can use a loop like:-
for(var i=0; i< cars.length;i++)
{
alert(cars[i]);
}
//It will show alert 5 times. You'll need to click through ok to traverse all array elements.
//I think it is what you're thinking, or have I interpreted it wrong.
// I'm assuming you're completely new to javascript then on your button write onclick="yourFuncName();"
function YourfuncName()
{
//Initailze your array here, like you have done or like kamituel has done
// then just print each array element one by one
for(var i=0; i< cars.length;i++)
{
alert(cars[i]);
}
}
How about the every method?
cars.every( function(c) {
alert("car: " + c);
});
You're almost there. Since the JS code was located before the HTML, the button element still doesn't exist. Best just wrap the code with window.onload and it should work:
window.onload = function() {
document.getElementById("addCarBtn").onclick = function() {
if (!(current > cars.length - 1)) {
document.getElementById("carsDiv").innerHTML += cars[current] + "<br />";
current++;
}
}
};
Live test case.
Edit: just noticed your button doesn't have type. This means that some browsers might make it a submit button by default, which will cause a page reload. To avoid it, make it a plain button:
<button id="addCarBtn" type="button">
The is(:focus) was the aproach. The final code is listed below:
setInterval(function(){
if($j("SELECT[name='cf20_field_7']").is(":focus")) return false;
var information = '';
var i = 1;
$j("#cf20_field_1").html();
//add new information to hidden field
$j("#cforms20form .info_for_email").each(function(){
var name = $j(this).find("INPUT[name='cf20_field_5']").val();
var inn = $j(this).find("INPUT[name='cf20_field_6']").val();
var view = $j(this).find("SELECT[name='cf20_field_7']").val();
//render
information += i + ")";
information += "Наименование организации: " + name + ".<br/>\n";
information += "Реквизиты организации: " + inn + ".<br/>\n";
information += "Стоимость заказа: выписка " + view + ".<br/>\n";
i++;
})
$j("#cf20_field_1").html(information);
hovered = true;
}
,100
);
Is there some possibility to fire function when there is no hover in SELECT field.
And also there may be aproach that to check is there is no hover on SELECT field.
It cause problemms. When you are trying to select another option cursor is begging while setInterval is working.
The best approach that i find is listed below:
//every 100 mil secconds update info
setInterval(function(){
$j("SELECT[name='cf20_field_7']").trigger('change');
if ( $j("SELECT[name='cf20_field_7']").on("change")) return false;
var information = '';
var i = 1;
$j("#cf20_field_1").html();
//add new information to hidden field
$j("#cforms20form .info_for_email").each(function(){
var name = $j(this).find("INPUT[name='cf20_field_5']").val();
var inn = $j(this).find("INPUT[name='cf20_field_6']").val();
var view = $j(this).find("SELECT[name='cf20_field_7']").attr("value");
//render
information += i + ")";
information += "Наименование организации: " + name + ".<br/>\n";
information += "Реквизиты организации: " + inn + ".<br/>\n";
information += "Стоимость заказа: выписка " + view + ".<br/>\n";
i++;
})
$j("#cf20_field_1").html(information);
}
,100
);
More information:
I can discribe situation more. So i had a form. onsubmit event didn`t work because there is another event is attachet. So i deside to update value of first field of form every 100 milisecs. The value is containing all dynamictly created "selects and inputs". But when i try to change value of the select by mouse. The function is fired and function check value of select and cause mouse begging. So i need somehow to check if that select is hovered to prevent firing of the function.
Invalid here:
if ( SELECT[name='cf20_field_7'].on("change"))
I guess you need this:
if ( $("SELECT[name='cf20_field_7']").on("change"))
But still, the above is invalid. You need some handler like:
$("SELECT[name='cf20_field_7']").on("change", function(){
return false;
});
if ($j("SELECT[name='cf20_field_7']").on("change")) return false
Not clear what should be checked here. I assume you want to run some function attached to onchange even of select. In that case you should use .trigger instead of .on. But in both cases return value will be jquery object (for chaining purposes) so basically your statement will always be true both with trigger and on If you want to test some value of select, you should do something like next:
if(someTestFunct($j("SELECT[name='cf20_field_7']"))) return false;
function someTestFunct(jObj) {
//some other code?
return jObj.val() == "some value to test";
}
Possibly some better approach may be used, but without more details it is hard to suggest something.