Stop click event firing last thing that got appended - javascript

I have this fiddle : https://jsfiddle.net/cs3xt67j/3/
It creates new divs with tabs on the click of the button. I have added an eventListener on each tab which is suppose to hide every div inside a container except the one that relates to that tab :
function showThisDiv(selection){
console.log(selection)
var children = document.getElementById('mainContainerDiv').childNodes;
for(i=0;i<children.length;i++){
if(selection!=children[i].id){
document.getElementById(selection).classList.add('hidden');
}
document.getElementById(selection).classList.remove('hidden');
}
console.log(children)
//document.getElementById(selection).classList.remove('hidden');
}
I added this event when i first create the divs (in the function that gets called from button click ) like so :
newDivTab.addEventListener("click",function(){ return showThisDiv('containerDiv'+divCount)});
But every time the event fires, it calls the last selection that got added to event rather than the thing that got added when i created the divs. Probably easier if you see the console log from the fiddle. Just create a few tabs by clicking the button and click the tabs created then see console logs

Working demo.
Basically you need to duplicate value of divCount and store it into other context to preserve it's value.
So, instead of:
newDivTab.addEventListener("click",function(){ return showThisDiv('containerDiv'+divCount)});
Use:
var localCount = divCount;
newDivTab.addEventListener("click",function(){ return showThisDiv('containerDiv'+localCount)});

Every time a div is clicked it calls the function
function(){ return showThisDiv('containerDiv'+divCount)}
so the concatenation of the string 'containerDiv'+divCount doesn't get evaluated until the click, so it is using whatever the current value of divCount is, so the possible div.
These are called closures and a lot smart people have written about them: http://www.mennovanslooten.nl/blog/post/62
To fix wat you're doing i would suggest wrapping the onclick function in another function and pass it the wanted id
newDivTab.addEventListener("click",(function(divID){
return function(){
showThisDiv('containerDiv'+divID)
}
})(divCount))
To quote the link above (with i being equivalent to your divCount):
Calling this function creates a new variable scope for each iteration because variable scope is created at execution time. This allows me to pass on the value of i to this new scope.

Related

How can i force a template-update in Polymer?

How is it possible to show changes of existing items in a dom-repeat template in polymer?
i tried really all i could think of and i could find in the polymer documentation or in the web. but nothing works. below you find a html-page that uses a small list and tries to change one entry in the list to another value when you click the change button. But the only thing that would change the item in the list next to the change-button is the line that is commented out. all the other lines try it, but fail.
i understand that re-rendering a template is a time-consuming task and that it only should take place when it is necessary and that polymer tries to avoid it as much as possible. but why is it not possible for me (from the view of the code the god of their world ^^) to force a render on purpose?
the method of creating a complete new object, deleting the old item from the list and inserting the new object (thats what the commented line does) is a workaround, but it is a really huge effort, when the items are more complex and have properties or arrays that are not even displayed.
What am i missing? What did i not try? I would be very glad if anybody could tell me what i could do to achieve such a (from my point of view) simple and very common task.
EDIT (solved):
the solution of Tomasz Pluskiewicz was partly successful. but i updated the code to show my current problem. the name of the item is bound using the method format(...). when a button is clicked the item will be removed from the list. this works good. but if you remove the last item of the list, then the new last item in the list should get the name "Last". this also works, when the name is bound directly to the property name. but if i want to do some formatting of the name (surround it with # for example) then the display of this item is not updated.
EDIT2 (partially solved):
The next example that doesn't work, occurs when a value inside the method that is called for displaying a value changes. This can be seen in the example if a change-button is clicked multiple times. It increases an internal counter and the corresponding text will display this value. But this is only true for the first change of the counter. Subsequent clicks won't change the display again. The display shows the value of the counter after the first click. But if another change button is clicked, then the text in front of this button shows the increased counter value. But also only once. It also doesn't display changes on subsequent clicks. The notifyPath-method seems to check if the value changed, but doesn't consider that inside the method that is used for displaying the value, something could have been changed to show the data in another way.
i included a partial solution in this example. If the method that gets called has a parameter that changes when something in the method is changed, then the update will be executed. This can be seen in the second variable that is bound with the parameter displayEnforcer - format(item.name,displayEnforcer). This variable is set to a random value everytime the counter is changed. And this triggers the display update.
But this is a really strange solution and should not be necessary. it would be great if someone has a better solution to this problem.
<link rel="import" href="components/bower_components/polymer/polymer.html">
<link rel="import" href="components/bower_components/paper-button/paper-button.html">
<dom-module id="polymer-test">
<template>
<table>
<template id="tpl" is="dom-repeat" items="{{list}}">
<tr>
<td>{{item.id}} - {{format(item.name)}}- {{format(item.name,displayEnforcer)}}</td>
<td><paper-button raised on-tap="tapDelete">delete</paper-button></td>
<td><paper-button raised on-tap="tapChange">change</paper-button></td>
</tr>
</template>
</table>
</template>
</dom-module>
<script>
Polymer(
{
is: "polymer-test",
properties:
{
count: {type: Number, value:0}
,list: {type: Array, value: [{id:0,name:"First"}
,{id:1,name:"Second"}
,{id:2,name:"Third"}
,{id:3,name:"Fourth"}
,{id:4,name:"Fifth"}
,{id:5,name:"Last"}
]}
,displayEnforcer: {type:Number,value:Math.random()}
},
format: function(name,dummy)
{
return "#" + name + " - " + this.count + "#";
},
tapChange: function(e)
{
this.count++;
this.displayEnforcer = Math.random();
this.notifyPath("list." + (e.model.index) + ".name","changed");
},
tapDelete: function(e)
{
if(this.list.length == 1)
return;
this.splice("list",e.model.index,1);
if(this.list.length > 0)
this.list[this.list.length-1].name = "Last";
this.notifyPath("list." + (this.list.length-1) + ".name",this.list[this.list.length-1].name);
}
});
</script>
You can use notifyPath to refresh binding of single list element's property:
tapChange: function(e) {
this.notifyPath('list.' + e.model.index + '.name', 'changed');
}
See: https://github.com/Polymer/polymer/issues/2068#issuecomment-120767748
This way does not re-render the entire repeater but simply updates the necessary data-bound nodes.
EDIT
Your format function is not getting updated because it doesn't use the notified path which is an item's name. When you remove an element, the index of that element within the rendered repeater doesn't change, even though the actual item changes. In other words, when you remove fifth element, the index 4 is still 4.
As a workaround you can add item.name to format call so that it is refreshed when you notify that path:
<td>{{item.id}} - {{format(index, item.name)}}</td>
You don't even have to use that value in the example. It's enough that the binding is reevaluated.

Javascript Generating Text into Textbox?

I wrote this little bit of code but I'm not sure why it's not working? It's supposed to take in the persons name and depending on what they selected it will output a website with their name at the end of it.
JSFiddle
http://jsfiddle.net/tQyvp/135/
JavaScript
function generateDynamicSignature() {
var dynSig = "";
var user = document.getElementById("usernameInput");
var e = document.getElementById("scriptListInput");
var strUser = e.options[e.selectedIndex].text;
if (strUser == "example") {
dynSig = "http://example.com/users/";
}
document.getElementById("generateSignature").addEventListener('click', function () {
var text = document.getElementById('dynamicSignatureOutput');
text.text = (dynSig + user);
});
}
HTML
<select class="form-control" id="scriptListInput">
<option value="example">Example 1</option>
</select>
There are a few problems with your code, I'll try to list them all.
First, you never added the username input to your HTML.
Next, you seem mixed up on the way to access/set the text of an HTML input. You do this through the value field. For the username input, you forgot to access any property, so you'll need to change it to:
var user = document.getElementById("usernameInput").value;
You later used the text property of both the select element and the output. These should also both be value.
Another problem is that you've placed a listener inside a listener. Your outer function, generateDynamicSignature, listens for the onclick event of the button. This function only runs after the button is clicked. But inside this function, you attach a new listener. This inner listener will only run if someone clicks the button twice.
I've included these changes in a new fiddle:
https://jsfiddle.net/zdfnk77u/
where is usernameInput in your html?
in the if, use === instead of ==
If and when you add the missing "usernameInput" element in your HTML, all you'll have to do is...
dynSig='http://example.com/users/'+usernameInput.value;
I think part of the problem is that you want to access the value and not the text of input elements. So for text and strUser, you want to do text.value instead of text.text and such.
Also, based on the JSfiddle, you probably want to rewrite how you're using the document listener and the onclick of the html element. Every time the button is clicked it goes through the generateDynamicSignature and creates a listener to change the value, but doesn't necessarily change the value itself. If you move the logic of the generate function inside the click listener, that should fix most of your problems.
You create your generateDynamicSignature inside $(document).ready.
There are two approaches.
define function generateDynamicSignature outside
$(document).ready
or
bind your button.click to a handler inside $(document).ready
Do not mix these two.

How do I make an OnClick function that will change a word when a user clicks it to another word?

Okay so, I want to make an OnClick function in JavaScript that makes it so when a user clicks on it, it will change the word. Is there a replaceword() function or something that which will let me do so? I know this is not real code, but for example:
<p>Quickly <span onclick="replaceword('Surf');">Search</span> The Web!</p>
If there is, then can someone tell me also how to reverse the code maybe? So when they click on it the second time, it will change back to "Search"?
If you want to jump between multiple words, you'll need to store them someplace. You could have two words in the sentence, and toggle the visibility of one or the other (which doesn't scale well), or you could even store them as values on an attribute placed on the element itself.
<p>Hello, <span data-values="World,People,Stack Overflow">World</span>.</p>
I have placed all possible values within the data-values attribute. Each distinct value is separated from the other values by a comma. We'll use this for creating an array of values next:
// Leverage event-delegation via bubbling
document.addEventListener( "click", function toggleWords ( event ) {
// A few variables to help us track important values/references
var target = event.target, values = [], placed;
// If the clicked element has multiple values
if ( target.hasAttribute( "data-values" ) ) {
// Split those values out into an array
values = target.getAttribute( "data-values" ).split( "," );
// Find the location of its current value in the array
// IE9+ (Older versions supported by polyfill: http://goo.gl/uZslmo)
placed = values.indexOf( target.textContent );
// Set its text to be the next value in the array
target.textContent = values[ ++placed % values.length ];
}
});
The results:
The above listens for clicks on the document. There are numerous reasons why this is a good option:
You don't need to wait for the document to finish loading to run this code
This code will work for any elements added asynchronously later in the page life
Rather than setting up one handler for each element, we have one handler for all.
There are some caveats; you may run into a case where the click is prevented from propagating up past a particular parent element. In that case, you would want to add the eventListener closer to your target region, so the likeliness that bubbling will be prevented is less.
There are other benefits to this code as well:
Logic is separated from markup
Scale to any number of values without adjusting your JavaScript
A demo is available for your review online: http://jsfiddle.net/7N5K5/2/
No, there isn't any native function, but you can create on your own.
function replaceword(that, word, oword) {
that.textContent = that.textContent == oword ? word : oword;
}
You can call it like this:
<p>Quickly<span onclick="replaceword(this,'Surf','Search');">Search</span>The Web!</p>
Live Demo: http://jsfiddle.net/t6bvA/6
<p id="abc">Hello</p>
<input type="submit" name="Change" onclick="change()">
function change(){
var ab=document.getElementById('abc').value;
ab.innerHTML="Hi, Bye";
}
I think so this should help you, you should go to site such as w3schools.com, its basic and it will answer your doubt
You can try something like this if you wanna use jQuery
http://jsfiddle.net/R3Ume/2/
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<body>
<p>Hello <a id='name'>John<a></p>
<input id="clickMe" type="button" value="replace" onclick="onClick();" />
<script>
function onClick() {
$('#name').text('world');
}
</script>

When 'unwrapping' child nodes in a loop, the references become confused

var thumbs = document.getElementsByClassName('thumbnailImage');
for(var i=0,len=thumbs.length;i<len;i++){
var p = thumbs[i].parentNode;
alert('i: '+i+',thumbs[i]: '+thumbs[i].id+',p.tagName: '+p.tagName+',p.class: '+p.className);
//unpackchild(thumbs[i]);
}
function unpackchild(c) {
var g=c.parentNode.parentNode;g.appendChild(c);
}
There are 5 image elements in thumbs (this is correct) and each one is wrapped in an anchor tag. When I run the code above (after document ready) the first three images are unpacked to the grandparent div correctly but on the last two the id I see in the alert is a repeat of the first two as though thumbs[3] now references thumbs[0] and thumbs[4] now references thumbs[1] (the ids are unique).
Does anybody see what I could have done to cause this or is this something intrinsic to the way javascript references elements (by parent perhaps)?
Change your loop to:
for( var i=thumbs.length-1; i>=0; i--) {
This will ensure that the system doesn't get confused, even if you have nested elements with that class name (you probably don't in this case, but it's good to know in general ;) )
HTMLCollection object returned by getElementsByClassName is live - it means it may change whenever underlying DOM document change.
Combining the two answers above, while looping, anything occurring after i moved the elements, was breaking the references to thumbs. So in the original ( i removed it for the question) i was adding an eventlistener after unpackchild was called. After changing to the backward counting loop suggested by Niet, the eventlistener was actually just added 5 times onto the first element in thumbs. By moving addeventlistener to before unpackchild, it was added correctly to each element.
So with this a click on thumbs[0] results in 5 click events firing all referencing thumbs[0]...
var thumbs = document.getElementsByClassName('thumbnailImage'),i=0,len=thumbs.length,c=0,el=[],p;
for(i=len-1;i>=0;i--){
p = thumbs[i].parentNode;
//alert('i: '+i+',thumbs[i]: '+thumbs[i].id+',p.tagName: '+p.tagName+',p.class: '+p.className);
thumbs[i].addEventListener('click',(function(t){return function(e){mzremix(t,e);};})(thumbs[i]),false);
if(p.tagName == 'A'){ unpackchild(thumbs[i]);p.parentNode.removeChild(p); }
thumbs[i].addEventListener('click',(function(t){return function(e){mzremix(t,e);};})(thumbs[i]),false);
}
...while this results in the correct 1 event firing for each thumbs[i] referencing thumbs[i]
var thumbs = document.getElementsByClassName('thumbnailImage'),i=0,len=thumbs.length,c=0,el=[],p;
for(i=len-1;i>=0;i--){
p = thumbs[i].parentNode;
//alert('i: '+i+',thumbs[i]: '+thumbs[i].id+',p.tagName: '+p.tagName+',p.class: '+p.className);
thumbs[i].addEventListener('click',(function(t){return function(e){mzremix(t,e);};})(thumbs[i]),false);
if(p.tagName == 'A'){ unpackchild(thumbs[i]);p.parentNode.removeChild(p); }
}

How can I write this as a JavaScript function?

I have the following code snippet embedded into some of my divs so when those divs get clicked a certain radio button gets checked.
onclick="document.g1.city[0].checked=true;"
However I would like to convert the above call to a function call like below:
onclick="checkRadioButton(city[0]);"
And the function will be something like this
function checkRadioButton(input){
document.g1.input.checked=true;
}
Is there a way I can accomplish this?
You can write any Javascript code inside the onclick attribute. Remember that city[0] is not defined anywhere. To access it, you must specify the full document.g1.city[0]. So the onclick becomes:
onclick="checkRadioButton(document.g1.city[0]);"
Inside your function, you are already receiving the element, and don't have to retrieve it from the document again. You could directly set it's checked property:
function checkRadioButton(input) {
input.checked = true;
}
onclick="checkRadioButton(document.g1.city[0]);"
checkRadioButton(var input){
input.checked=true;
}
You can also reduce the need for supplying document.g1.city[0] if, for example, every DIV that you mark thusly has an attribute "radioID", and the value of the attribute must match the ID given to the radio button:
onclick="checkRadioButton(this);"
checkRadioButton(var div){
document.getElementById(div.radioID).checked=true;
}

Categories