How to clone an element and not fire events in Chrome? - javascript

I am attempting to clone an input element of type="file". In this input element there is an onchange event. The clone method is called in the function that is called from the onchange event.
The problem I am having is that when I clone the file upload input, the onchange event is fired again. This only happens in Chrome, but works as intended in IE.
<input type="file" id="fileUpLoad" onchange="doFunction();" />
doFunction = function() {
var oldEl = $('#fileUpLoad');
var newEl = $(oldEl).clone();
$(oldEl).attr('id','fileUpLoadOLD');
$(oldEl).before(newEl);
}
I have created a fiddle duplicating this issue. Here is the fiddle.
How can I clone this file upload input without firing the onchange event again and again? I am using jQuery 1.7.1. This is actually a simplified a snippet from ajaxfileuploader.js:
var oldElement = jQuery('#' + fileElementId);
var newElement = jQuery(oldElement).clone();
jQuery(oldElement).attr('id', fileId);
jQuery(oldElement).before(newElement);
jQuery(oldElement).appendTo(form);
UPDATE: fiddle I think this is accomplishing what I need based on #MichaelAngstadt answer. Still testing though.
This is what seems to be working for me in the ajaxfileuploader.js extension:
var oldElement = jQuery('#' + fileElementId);
var oldElEvents = oldElement.data("events");
var newElement = jQuery(oldElement).attr('id', fileId).data("events", null).clone();
jQuery(oldElement).attr('id', fileElementId).before(newElement);
jQuery(oldElement).data("events", oldElEvents);
jQuery(newElement).appendTo(form);

You could always hold the events in a temporary object and re-attach them after cloning like:
doFunction = function(){
var oldEl = $('#fileUpLoad');
var oldElEvents = oldEl.data("events");
var newEl = $(oldEl).data("events", null).clone();
$(oldEl).attr('id','fileUpLoadOLD');
$(oldEl).before(newEl.data("events",oldElEvents));
}
This way oldEl doesn't actually have events defined to be cloned. Otherwise you'll be battling some fundamental functionality with regards to how cloning & the DOM work.

Have you tried cloning the object using $.extend instead.
var newEl = $.extend({},oldEl);

It seems the problem is related to onchange attribute. You can remove/reset the attribute before cloning. After cloning, the change event is triggered again and it results in an infinite loop.
var newEl = oldEl.removeAttr('onchange').clone();
You are loading jQuery, why not using it's methods? If you are using onchange attribute for keeping the event handler, you can use event delegation technique instead.
$(document).on('change', '.fileUpLoads', function() {
$(this).clone().insertBefore(this);
});

You don't need anything fancy here. If you attach the onchange event with jQuery/javascript instead of in the HTML, then clone( ) will only create one copy, as desired.
HTML:
<input type="file" id="fileUpLoad" />
JS:
$(document).ready( function() {
$('#fileUpLoad').change( function() {
doFunction();
});
});
doFunction = function() {
var oldEl = $('#fileUpLoad');
var newEl = $(oldEl).clone( );
$(oldEl).attr('id','fileUpLoadOLD');
$(oldEl).before(newEl);
}
P.S fileUpLoad, really? That sneaky capital L in there caused me some problems.

Building on the answer from #undefined (which works) I'd suggest actually making the change event a delegated event on a container object. Otherwise you end up with one input (the old one) which clones itself on change and another (the new one) which doesn't.
HTML:
<div class="container">
<input type="file" class="foo" id="fileUpLoad" />
</div>
Javascript:
$(function () {
$('.container').on('change', '.foo', function () {
var oldEl = $('#fileUpLoad');
var newEl = $(oldEl).clone().val('');
$(oldEl).attr('id', 'fileUpLoadOLD');
$(oldEl).before(newEl);
});
});
Working fiddle (I have also reset the value of the new input to null)
Note: This is still crashing Firefox for me. Not sutre why but might be my FF - the change event hasn't fired (in the other browsers) at the point FF crashes.
Explanation
I think what is happening here is that jQuery does not (by default) clone events when you clone an element. It does, however, clone attributes. So the onchange attribute on the element gets cloned and creates a change event on the cloned element, which for some reason is then called triggering an infinte loop. Not sure why .clone() triggers change events
Events attached via .on() on the object are not cloned - so no extra elements but no event on them either. By making the event a delegated event on a container we avoid the problem (since when the change event is triggered during clone the new element is not part of the page yet and so does not trigger the delegated event) but still have the functionality on the new object (once we insert it into the page).
You need better handling of the IDs if you want to be able to get more than two such inputs.

Related

Polymer 1.0 Local Dom issue with this.$ selector

I understand that we can select elements from the local dom with the this.$ selector. However, if I have a function that fires on a button click, how would I access an element by ID within this function?
For example:
HTML:
<input id = "counter" value = "10">
JavaScript:
var testButton = this.$.testButton;
$(testButton).click(function(){
var counter = this.$.counter;
console.log(counter.value);
}
The issue is that the "this.$.counter" is now referencing the button
The issue is that when you use Polymer it doesn't change how event handlers and this works. this within your event handler corresponds to testButton, since it is the element that fired the click event, not the Polymer element.
Try this:
var testButton = this.$.testButton;
var counter = this.$.counter;
$(testButton).click(function(){
console.log(counter.value);
}
Using Polymer's onTap function instead of onClick would also solve this issue.
If you're using jQuery with polymer there is a good chance you are doing something wrong.
<button on-tap='foo'>
...
Polymer({
...
foo: function(event){
console.log(this.$.counter);
}
});

Two Html select drop down apply class for span element using Javascript

I am working on HTML select Dropdown. I have two dropdowns one is for font size adjust and other is for text alignment.
When I select the fontsize from the dropdown it has to apply along with text-capitalize (bootstrap css) and If I select the font alignment all three should apply for the span element. For Example.
<div>
<span id="Title"class="text-capitalize">check</span>
</div>
Right now the code was like this
function changeFont_size () {
var select = document.getElementById('font_size');
// Bind onchange event
select.onchange = function() {
document.querySelector("#Title").className = this.value += " text-
capitalize";
};
}
function changeAlignment () {
var select = document.getElementById('text_align');
// Bind onchange event
select.onchange = function() {
document.querySelector("#Title").className = this.value;
};
}
Actually I am newbe on Javascript. Some how I am not getting.
The output result would be the combination of :
<span class="h1 left text-capitalize">Text</span>
Everything should be in pure javascript.
Thanks in advance. Kindly help me.
Here is the Link
This jsfiddle makes your code work. You need to run the code when the document is loaded, so that your onchange functions are being hooked in time.
It does not work exactly like you intended though. Your alignment classes need to be on the parent element and when you select your alignment, you disregard the previously set h1 or h2 class.
window.onload = function() {
var font_size = document.querySelector('#font_size');
// Bind onchange event
font_size.onchange = function() {
document.querySelector("#Title").className = this.options[this.selectedIndex].value += " text-capitalize";
};
var text_align = document.querySelector('#text_align');
// Bind onchange event
text_align.onchange = function() {
document.querySelector("#Title").className = this.options[this.selectedIndex].value;
};
};
You are mixing things up. There are two ways to bind events (well, two ways which are still common even with recent browsers).
The first one is to put a function call in the onsomething property of an element in the html code. Whatever is put there will be executed when the event happens.
<button onclick="alert('hi');">Click me</button>
You should pass the event object to an event handler instead of writing inline code.
<button id="helloworld" onclick="helloworld_onclick(event)">Run</button>
...
function helloworld_onclick(e) {
alert("Hello world!");
}
If you want to be able to bind events dynamically, if you want to bind multiple events to an object and if you want to keep the JavaScript outside of your HTML, the modern way to to so is with addEventListener.
document.querySelector("#helloworld").addEventListener("click", function(e) {
alert("Hello world!");
});
The event object passed (called e in my functions) contains information about what triggered the event and can be used to prevent default behavior and to control event propagation. You can't use "this" in event handlers, but the element which called the handler will be stored in e.target.
In your code, you created functions which, when called, bind events to the elements. Then you bound those functions to the elements with the html attributes.
Finally, you seem to be stuck between querySelector and getElementById. Note that querySelector(All) returns a static node/nodelist while getElement(s)By(...) returns a live node/nodelist. A static node is a copy of all the information about the element. A live node is a reference to the real element. If you modify the element, it modifies the live node, but the static node will keep the old information. You should use getElementById over querySelector for that, and because it runs faster. For code simplicity however, you might prefer always using querySelector. Just don't mix using querySelector("#something") on a line and getElementById("something") on another one, it's the best way to get confused and end up wasting time on a bug because you wrote querySelector("something") or getElementById("#something") instead.
function changeFont_size (element) {
if(element.options[element.selectedIndex].value != 'select'){
document.getElementById('Title').className = element.options[element.selectedIndex].value;
} else{
document.getElementById('Title').className = '' }
}
function changeAlignment (element) {
if(element.options[element.selectedIndex].value != 'select'){
document.getElementById('container').className = element.options[element.selectedIndex].value;
} else{
document.getElementById('container').className = '' }
}
Try this, Hope it will work

Call onclick attribute programatically and talk to the event parameter

I have read the other post relating to this matter. Unfortunately it has not resolved my problem. I am happy to use jQuery, so I am looking for the cleanest solution.
I have radio buttons defined as follow:
a = 5;
input.value = "myButton";
input.onclick = (function (a) {
return function (e) {
changeSelectedRadio(e.srcElement, a);
};
})(a);
I need to be able to execute this when user click on the radio button (this works fine), and programatically.
I have tried:
$("input[type='radio'][value='myButton']").triggerHandler("click");
$("input[type='radio'][value='myButton']").click();
Both produce the same result: e (the event parameter) does not exist.
$("input[type='radio'][value='myButton']").onclick();
Does not work
Changing the input.onclick to input.click also did not work. When the use click, the function does not get triggered.
Thanks
If you're using jquery already, might as well build the inputs that way:
var input = $('<input value="something">').click(function(){
var elem = $(this); // the element clicked, as a jquery obj
alert(elem.attr('id');
});
$('body').append(input);
$('input').trigger('click');
Adjust the selectors as needed, and you'll need to actually append the elements to the DOM, but it'll work.
try this:
$("input[type='radio'][value='myButton']").bind( "click", function() {
alert( "clicked" );
});
What is passed to the function is a jQuery event, not a native event. You can use the target element to get at the source that was clicked on or use this to reference the properties of the object directly. See fiddle at http://jsfiddle.net/YQh3Q/
<p><input id="foo1" name="foo" type="radio" value="0" checked="checked"> Foo1</p>
<p><input id="foo2" name="foo" type="radio" value="1"> Foo2</p>
(function ($) {
var input = document.getElementById("foo2");
var a = 5;
input.value = "myButton";
input.onclick = (function (a) {
return function (e) {
alert(e.target + '|' + this.id);
};
})(a);
$("input[type='radio'][value='myButton']").each(function() {
$(this).trigger("click");
});
})(jQuery);
Alternatively (and probably better) you can use a pure jQuery solution
$(function() {
var a = 5;
$('input#foo2').on('click', function() {
changeSelectedRadio(this, a);
})
.val('myButton');
$("input[type='radio'][value='myButton']").trigger('click');
});
Its best to use addEventListener() you can add all types of events. example: "click", "mousemove", "mouseover", "mouseout", "resize" and many more. the false at the end is to stop the event from traversing up the dom. If you want parent dom objects to also receive the event just change it to true. also this example requires no javascript libraries. This is just plain old javascript and will work in every browser with nothing extra needed.
Also addEventListener() is better than onClick() as you can add an unlimited number of event listeners to a dom element. If you have an onClick() on an element and then set another onClick() on the same element you have overwritten the first onClick(). Using addEventListener() if i want multiple click events to trigger when i click on an element i can do it with no problem.
If you want data about the element that is triggering the event you can pass the event to the function. You will see in my example function(e) e is the event and you can use e or this to target the element that is being triggered. Using e or this i can also get more data about the triggered event. for example if the event was a mousemove or mouseclick i can get the x and y position of the mouse at the time of the event.
<!DOCTYPE html>
<html>
<head>
<title>exampe</title>
</head>
<body>
<a id="test" href="">test</a>
<script>
document.getElementById("test").addEventListener("click",function(e){
alert('hello world');
alert('my element '+e);
alert('my element '+this);
},false);
</script>
</body>
</html>
if you want to have addEventListener call a function just change the 2nd value to the function name like this.
document.getElementById("test").addEventListener("click",f1,false);
this will execute the function
function f1(){ ... }
When you want to remove an event listener just call target.removeEventListener(type, listener[, useCapture]). Very simple and easy to manage.

Retrieving previously focused element

I would like to find out, in Javascript, which previous element had focus as opposed to the current focus. I've been looking through the DOM and haven't found what I need, yet. Is there a way to do this any help would be much appreciated
Each time an element is focused, you'd have to store which one it was. Then when another element is focused, you could retrieve the variable for the previous focused element.
So basically, your single focus handler would do 2 things:
Check if previousFocus is defined. If it is, retrieve it.
Set previousFocus to the currently focused element.
Here is a quick demo with jQuery (you can use raw JS too... just fewer lines w jQuery, so it's easier to understand imo):
// create an anonymous function that we call immediately
// this will hold our previous focus variable, so we don't
// clutter the global scope
(function() {
// the variable to hold the previously focused element
var prevFocus;
// our single focus event handler
$("input").focus(function() {
// let's check if the previous focus has already been defined
if (typeof prevFocus !== "undefined") {
// we do something with the previously focused element
$("#prev").html(prevFocus.val());
}
// AFTER we check upon the previously focused element
// we (re)define the previously focused element
// for use in the next focus event
prevFocus = $(this);
});
})();
working jsFiddle
Just found this question while solving the exact same problem and realised it was so old the jQuery world has moved on a bit :)
This should provide a more effective version of Peter Ajtais code, as it will use only a single delegated event handler (not one per input element).
// prime with empty jQuery object
window.prevFocus = $();
// Catch any bubbling focusin events (focus does not bubble)
$(document).on('focusin', ':input', function () {
// Test: Show the previous value/text so we know it works!
$("#prev").html(prevFocus.val() || prevFocus.text());
// Save the previously clicked value for later
window.prevFocus = $(this);
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/EzPfK/80/
Notes:
Uses $() to create an empty jQuery object (allows it to be used immediately).
As this one uses the jQuery :input selector it works with select & button elements as well as inputs.
It does not need a DOM ready handler as document is always present.
As the previously focused control is required "elsehere" is is simply stored on window for global use, so it does not need an IIFE function wrapper.
Well depending on what else your page is doing, it could be tricky, but for starters you could have a "blur" event handler attached to the <body> element that just stashes the event target.
To me this seems a slight improvement on Gone Coding's answer:
window.currFocus = document;
// Catch focusin
$(window).on( 'focusin', function () {
window.prevFocus = window.currFocus;
console.log( '£ prevFocus set to:');
console.log( window.currFocus );
window.currFocus = document.activeElement;
});
... there's no stipulation in the question that we're talking exclusively about INPUTs here: it says "previous elements". The above code would also include recording focus of things like BUTTONs, or anything capable of getting focus.
document.getElementById('message-text-area').addEventListener('focus',
event => console.log('FOCUS!')
);
event.relatedTarget has all the data about the previously focused element.
See also https://developer.mozilla.org/en-US/docs/Web/API/Event/Comparison_of_Event_Targets
Here is a slightly different approach which watches both focusin and focusout, in this case to prevent focus to a class of inputs:
<input type="text" name="xyz" value="abc" readonly class="nofocus">
<script>
$(function() {
var leaving = $(document);
$(document).on('focusout', function(e) {
leaving = e.target;
});
$( '.nofocus' ).on('focusin', function(e) {
leaving.focus();
});
$( '.nofocus' ).attr('tabIndex', -1);
});
</script>
Setting tabIndex prevents keyboard users from "getting stuck".

Issue with selectors & .html() in jquery?

The function associated with the selector stops working when I replace it's contents using .html(). Since I cannot post my original code I've created an example to show what I mean...
Jquery
$(document).ready(function () {
$("#pg_display span").click(function () {
var pageno = $(this).attr("id");
alert(pageno);
var data = "<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
});
});
HTML
<div id="pg_display">
<span id="page1">1</span>
<span id="page2">2</span>
<span id="page3">3</span>
</div>
Is there any way to fix this??...Thanks
Not sure I understand you completely, but if you're asking why .click() functions aren't working on spans that are added later, you'll need to use .live(),
$("#someSelector span").live("click", function(){
# do stuff to spans currently existing
# and those that will exist in the future
});
This will add functionality to any element currently on the page, and any element that is later created. It keeps you have having to re-attach handlers when new elements are created.
You have to re-bind the event after you replace the HTML, because the original DOM element will have disappeared. To allow this, you have to create a named function instead of an anonymous function:
function pgClick() {
var pageno = $(this).attr("id");
alert(pageno);
var data="<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
$("#pg_display span").click(pgClick);
}
$(document).ready(function(){
$("#pg_display span").click(pgClick);
});
That's to be expected, since the DOM elements that had your click handler attached have been replaced with new ones.
The easiest remedy is to use 1.3's new "live" events.
In your situation, you can use 'Event delegation' concept and get it to work.
Event delegation uses the fact that an event generated on a element will keep bubbling up to its parent unless there are no more parents. So instead of binding click event to span, you will find the click event on your #pg_display div.
$(document).ready(
function()
{
$("#pg_display").click(
function(ev)
{
//As we are binding click event to the DIV, we need to find out the
//'target' which was clicked.
var target = $(ev.target);
//If it's not span, don't do anything.
if(!target.is('span'))
return;
alert('page #' + ev.target.id);
var data="<span id='page1'>1</span><span id='page2'>2</span><span id='page3'>3</span>";
$("#pg_display").html(data);
}
);
}
);
Working demo: http://jsbin.com/imuye
Code: http://jsbin.com/imuye/edit
The above code has additional advantage that instead of binding 3 event handlers, it only binds one.
Use the $("#pg_display span").live('click', function....) method instead of .click. Live (available in JQuery 1.3.2) will bind to existing and FUTURE matches whereas the click (as well as .bind) function is only being bound to existing objects and not any new ones. You'll also need (maybe?) to separate the data from the function or you will always add new span tags on each click.
http://docs.jquery.com/Events/live#typefn

Categories

Resources