My code generates an infinite scroll but when I touch each one it appears alerts many times and that isn't right.
$(window).scroll(function() {
if ($(window).scrollTop() == $(document).height() - $(window).height()) {
var variable = "<div class='corazon'>hi</div>";
$(".hola").append(variable);
$('.corazon').on('click', function() {
alert("hola");
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Thank you.
$('.corazon').on('click', function() {
alert("hola");
});
You're adding a new click listener to all the elements with class "corazon" every time you reach the scroll limit.
You should add the click listener on the created element instead of performing a new query $(".corazon") .
Try this:
var variable = $("<div class='corazon'>hi</div>");
$(".hola").append(variable);
variable.on('click', function() {
alert("hola");
});
This has already been asked, use jquery clone to clone an existing element with listener.
.clone( [withDataAndEvents ] [, deepWithDataAndEvents ] )
Provide both with true.
And yes
$('.corazon').on('click', function(){
alert("hola");
});
Should not be in the scoll listener
Full documentation: https://api.jquery.com/clone/
Instead of:
$('.corazon').on('click', function() {
you shuld have:
$(variable ).on('click', function() {
Now the clickhandler is only added to the new element.
Edit: I think I got the solution! I want to try and fix this myself before I ask for further help = )
First script inhibits the second one from functioning as the click event from the first one overides the second one. Because the second one does not function it is impossible to open the drop down menu to select a list item to trigger the first scripts click.
What I tried was replacing all return false statements with event.stopPropagation(). Didnt work however. Tried re-ordering my scripts but that failed as well. I was thinking of making my second script target another parent div but that didnt work either.I also tried event.stopImmediatePropagation() and .bind methods.
Any idea?
First script that makes the drop down function. Contains click event.
function DropDown(el) {
this.f = el;
this.placeholder = this.f.children('span');
this.opts = this.f.find('ul.dropdown > li');
this.val = '';
this.index = -1;
this.initEvents();
}
DropDown.prototype = {
initEvents : function() {
var obj = this;
obj.f.on('click', function(event){
$(this).toggleClass('active');
return false;
});
obj.opts.on('click',function(){
var opt = $(this);
obj.val = opt.text();
obj.index = opt.index();
obj.placeholder.text(obj.val);
});
},
getValue : function() {
return this.val;
},
getIndex : function() {
return this.index;
}
}
$(function() {
var f = new DropDown( $('#f') );
$(document).click(function() {
// all dropdowns
$('.filter-buttons').removeClass('active');
});
});
Second script that does the filtering, also contains click event:
jQuery(document).ready(function(e) {
var t = $(".filter-container");
t.imagesLoaded(function() {
t.isotope({
itemSelector: "figure",
filter: "*",
resizable: false,
animationEngine: "jquery"
})
});
$(".filter-buttons a").click(function(evt) {
var n = $(this).parents(".filter-buttons");
n.find(".selected").removeClass("selected");
$(this).addClass("selected");
var r = $(this).attr("data-filter");
t.isotope({
filter: r
});
evt.preventDefault();
});
$(window).resize(function() {
var n = $(window).width();
t.isotope("reLayout")
}).trigger("resize")
});
html structure
<div id="f" class="filter-buttons" tabindex="1">
<span>Choose Genre</span>
<ul class="dropdown">
<li>All</li>
<li>Electronic</li>
<li>Popular</a></li>
</ul>
</div>
This doesn't really solve your problem but I was bored while drinking my coffee and felt like helping you write your dropdown plugin a little nicer
My comments below are inline with code. For uninterrupted code, see DropDown complete paste.
We start with your standard jQuery wrapper (function($){ ... })(jQuery)
(function($) {
// dropdown constructor
function DropDown($elem) {
First we'll make some private vars to store information. By using this.foo = ... we expose things (probably) unnecessarily. If you need access to these vars, you can always create functions to read them. This is much better encapsulation imo.
// private vars
var $placeholder = $elem.children("span");
var $opts = $elem.find("ul.dropdown > li")
var value = "";
var index = -1;
Now we'll define our event listeners and functions those event listeners might depend on. What's nice here is that these functions don't have to access everything via this.* or as you were writing obj.f.* etc.
// private functions
function onParentClick(event) {
$elem.toggleClass("active");
event.preventDefault();
}
function onChildClick(event) {
setValue($(this));
event.preventDefault();
}
function setValue($opt) {
value = $opt.text();
index = $opt.index();
$placeholder.text(value);
}
Here's some property descriptors to read the index and value
// properties for reading .index and .value
Object.defineProperty(this, "value", {
get: function() { return value; }
});
Object.defineProperty(this, "index", {
get: function() { return index; }
});
Lastly, let's track each instance of DropDown in an array so that the user doesn't have to define a special listener to deactivate each
// track each instance of
DropDown._instances.push(this);
}
This is the array we'll use to track instances
// store all instances in array
DropDown._instances = [];
This event listener deactivate each "registered" instance of DropDown
// deactivate all
DropDown.deactiveAll = function deactiveAll(event) {
$.each(DropDown._instances, function(idx, $elem) {
$elem.removeClass("active");
});
}
Here's the document listener defined right in the plugin! The user no longer has to set this up
// listener to deactiveAll dropdowns
$(document).click(DropDown.deactiveAll);
Might as well make it a jQuery plugin since everything in our DropDown constructor relies upon jQuery. This let's the user do var x = $("foo").dropdown();
// jQuery plugin
$.fn.dropdown = function dropdown() {
return new DropDown($(this));
};
Close the wrapper
})(jQuery);
Now here's how you use it
$(function() {
var x = $('#f').dropdown();
// get the value
f.value;
// get the index
f.index;
});
Anyway, yeah I know this doesn't really help you with your click listeners, but I hope this is still useful information to you. Off to the Post Office now!
I think you're going to need to simplify this to figure out what's going on. There's actually not enough information to see what elements the events are being attached to here.
For argument's sake, open the console and try the following:
$(document).on('click', function() { console.log('first'); return false; });
$(document).on('click', function() { console.log('second'); return false; });
Then click in the page. You'll see that both events are triggered. It might well be that your code is actually attaching the events to different elements (you don't say anywhere). If that's the case then you need to understand how event bubbling works in the DOM.
When you trigger an event, say a click on an element, that event will fire on that element, and then on it's parent, then grandparent etc all the way to the root node at the top.
You can change this behaviour by calling functions in the event itself. evt.stopPropagation tells the event to not bubble up to the ancestor nodes. evt.preventDefault tells the browser not to carry out the default behaviour for a node (eg, moving to the page specified in the href for an A tag).
In jQuery, return false from an event handler is a shortcut for, evt.preventDefault and evt.stopPropagation. So that will stop the event dead in its tracks.
I imagine you have something like:
<div event_two_on_here>
<a event_one_on_here>
</div>
If the thing that handles event_one_on_here calls stopPropagation then event_two_on_here will never even know it has happened. Calling stopPropagation explicitly, or implicitly (return false) will kill the event before it travels to the parent node/event handler.
UPDATE: In your case the issue is that the handler on .filter-buttons a is stopping the propagation (so #f doesn't get to run its handler).
$(".filter-buttons a").click(function(evt) {
// your code here...
// Don't do this - it stops the event from bubbling up to the #f div
// return false;
// instead, you'll probably just want to prevent the browser default
// behaviour so it doesn't jump to the top of the page ('url/#')
evt.preventDefault();
});
I want to run a function when a user edits the content of a div with contenteditable attribute. What's the equivalent of an onchange event?
I'm using jQuery so any solutions that uses jQuery is preferred. Thanks!
2022 update
As pointed out in the comments, this doesn't answer the question asked, which wanted the equivalent of the change event rather than the input event. However, I'll leave it here as is.
Original answer
I'd suggest attaching listeners to key events fired by the editable element, though you need to be aware that keydown and keypress events are fired before the content itself is changed. This won't cover every possible means of changing the content: the user can also use cut, copy and paste from the Edit or context browser menus, so you may want to handle the cut copy and paste events too. Also, the user can drop text or other content, so there are more events there (mouseup, for example). You may want to poll the element's contents as a fallback.
UPDATE 29 October 2014
The HTML5 input event is the answer in the long term. At the time of writing, it is supported for contenteditable elements in current Mozilla (from Firefox 14) and WebKit/Blink browsers, but not IE.
Demo:
document.getElementById("editor").addEventListener("input", function() {
console.log("input event fired");
}, false);
<div contenteditable="true" id="editor">Please type something in here</div>
Demo: http://jsfiddle.net/ch6yn/2691/
Here is a more efficient version which uses on for all contenteditables. It's based off the top answers here.
$('body').on('focus', '[contenteditable]', function() {
const $this = $(this);
$this.data('before', $this.html());
}).on('blur keyup paste input', '[contenteditable]', function() {
const $this = $(this);
if ($this.data('before') !== $this.html()) {
$this.data('before', $this.html());
$this.trigger('change');
}
});
The project is here: https://github.com/balupton/html5edit
Consider using MutationObserver. These observers are designed to react to changes in the DOM, and as a performant replacement to Mutation Events.
Pros:
Fires when any change occurs, which is difficult to achieve by listening to key events as suggested by other answers. For example, all of these work well: drag & drop, italicizing, copy/cut/paste through context menu.
Designed with performance in mind.
Simple, straightforward code. It's a lot easier to understand and debug code that listens to one event rather than code that listens to 10 events.
Google has an excellent mutation summary library which makes using MutationObservers very easy.
Cons:
Requires a very recent version of Firefox (14.0+), Chrome (18+), or IE (11+).
New API to understand
Not a lot of information available yet on best practices or case studies
Learn more:
I wrote a little snippet to compare using MutationObserers to handling a variety of events. I used balupton's code since his answer has the most upvotes.
Mozilla has an excellent page on the API
Take a look at the MutationSummary library
non jQuery quick and dirty answer:
function setChangeListener (div, listener) {
div.addEventListener("blur", listener);
div.addEventListener("keyup", listener);
div.addEventListener("paste", listener);
div.addEventListener("copy", listener);
div.addEventListener("cut", listener);
div.addEventListener("delete", listener);
div.addEventListener("mouseup", listener);
}
var div = document.querySelector("someDiv");
setChangeListener(div, function(event){
console.log(event);
});
I have modified lawwantsin 's answer like so and this works for me. I use the keyup event instead of keypress which works great.
$('#editor').on('focus', function() {
before = $(this).html();
}).on('blur keyup paste', function() {
if (before != $(this).html()) { $(this).trigger('change'); }
});
$('#editor').on('change', function() {alert('changed')});
Two options:
1) For modern (evergreen) browsers:
The "input" event would act as an alternative "change" event.
https://developer.mozilla.org/en-US/docs/Web/Events/input
document.querySelector('div').addEventListener('input', (e) => {
// Do something with the "change"-like event
});
or
<div oninput="someFunc(event)"></div>
or (with jQuery)
$('div').on('click', function(e) {
// Do something with the "change"-like event
});
2) To account for IE11 and modern (evergreen) browsers:
This watches for element changes and their contents inside the div.
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
var div = document.querySelector('div');
var divMO = new window.MutationObserver(function(e) {
// Do something on change
});
divMO.observe(div, { childList: true, subtree: true, characterData: true });
const p = document.querySelector('p')
const result = document.querySelector('div')
const observer = new MutationObserver((mutationRecords) => {
result.textContent = mutationRecords[0].target.data
// result.textContent = p.textContent
})
observer.observe(p, {
characterData: true,
subtree: true,
})
<p contenteditable>abc</p>
<div />
Here's what worked for me:
var clicked = {}
$("[contenteditable='true']").each(function(){
var id = $(this).attr("id");
$(this).bind('focus', function() {
// store the original value of element first time it gets focus
if(!(id in clicked)){
clicked[id] = $(this).html()
}
});
});
// then once the user clicks on save
$("#save").click(function(){
for(var id in clicked){
var original = clicked[id];
var current = $("#"+id).html();
// check if value changed
if(original != current) save(id,current);
}
});
This thread was very helpful while I was investigating the subject.
I've modified some of the code available here into a jQuery plugin so it is in a re-usable form, primarily to satisfy my needs but others may appreciate a simpler interface to jumpstart using contenteditable tags.
https://gist.github.com/3410122
Update:
Due to its increasing popularity the plugin has been adopted by Makesites.org
Development will continue from here:
https://github.com/makesites/jquery-contenteditable
Non JQuery answer...
function makeEditable(elem){
elem.setAttribute('contenteditable', 'true');
elem.addEventListener('blur', function (evt) {
elem.removeAttribute('contenteditable');
elem.removeEventListener('blur', evt.target);
});
elem.focus();
}
To use it, call on (say) a header element with id="myHeader"
makeEditable(document.getElementById('myHeader'))
That element will now be editable by the user until it loses focus.
In Angular 2+
<div contentEditable (input)="type($event)">
Value
</div>
#Component({
...
})
export class ContentEditableComponent {
...
type(event) {
console.log(event.data) // <-- The pressed key
console.log(event.path[0].innerHTML) // <-- The content of the div
}
}
To avoid timers and "save" buttons, you may use blur event wich fires when the element loses focus. but to be sure that the element was actually changed (not just focused and defocused), its content should be compared against its last version. or use keydown event to set some "dirty" flag on this element.
Here is the solution I ended up using and works fabulously. I use $(this).text() instead because I am just using a one line div that is content editable. But you may also use .html() this way you dont have to worry about the scope of a global/non-global variable and the before is actually attached to the editor div.
$('body').delegate('#editor', 'focus', function(){
$(this).data('before', $(this).html());
});
$('#client_tasks').delegate('.task_text', 'blur', function(){
if($(this).data('before') != $(this).html()){
/* do your stuff here - like ajax save */
alert('I promise, I have changed!');
}
});
You need to use input event type
Demo
HTML
<div id="editor" contenteditable="true" >Some text here</div>
JS
const input = document.getElementById('editor');
input.addEventListener('input', updateValue);
function updateValue(e) {
console.log(e.target);
}
know more
The onchange event doesn't fires when an element with the contentEditable attribute is changed, a suggested approach could be to add a button, to "save" the edition.
Check this plugin which handles the issue in that way:
Creating a quick and dirty jQuery contentEditable Plugin
Using DOMCharacterDataModified under MutationEvents will lead to the same. The timeout is setup to prevent sending incorrect values (e.g. in Chrome I had some issues with space key)
var timeoutID;
$('[contenteditable]').bind('DOMCharacterDataModified', function() {
clearTimeout(timeoutID);
$that = $(this);
timeoutID = setTimeout(function() {
$that.trigger('change')
}, 50)
});
$('[contentEditable]').bind('change', function() {
console.log($(this).text());
})
JSFIDDLE example
I built a jQuery plugin to do this.
(function ($) {
$.fn.wysiwygEvt = function () {
return this.each(function () {
var $this = $(this);
var htmlold = $this.html();
$this.bind('blur keyup paste copy cut mouseup', function () {
var htmlnew = $this.html();
if (htmlold !== htmlnew) {
$this.trigger('change')
}
})
})
}
})(jQuery);
You can simply call $('.wysiwyg').wysiwygEvt();
You can also remove / add events if you wish
A simple answer in JQuery, I just created this code and thought it will be helpful for others too
var cont;
$("div [contenteditable=true]").focus(function() {
cont=$(this).html();
});
$("div [contenteditable=true]").blur(function() {
if ($(this).html()!=cont) {
//Here you can write the code to run when the content change
}
});
For me, I want to check the input is valid or not.
If valid, then update, Otherwise show an error message and keep the value as same as before.
Skill: When you edit done, usually, it will trigger the blur event.
Example
<span contenteditable="true">try input somethings.</span>
<script>
const elem = document.querySelector(`span`)
let oldValue = elem.innerText
elem.onkeydown = (keyboardEvent) => {
if (keyboardEvent.key === "Enter") {
elem.blur() // set focusout
}
}
elem.onblur = (e) => {
const curValue = elem.innerText
if (curValue === oldValue) {
return
}
if (curValue.length <= 50) { // 👈 Input your conditions.
// 👇 fail
elem.innerText = oldValue
// (Optional) Add error message
elem.insertAdjacentHTML("beforeend", `<span style="margin-left:5px;color:red">error length=${curValue.length}. Must greater than 50. undo to the previous value.</span>`)
const errMsg = elem.querySelector(`span`)
setTimeout(() => errMsg.remove(), 3500) // wait 3.5 second, and then remove it.
return
}
// 👇 OK, update
oldValue = curValue
}
</script>
Check this idea out.
http://pastie.org/1096892
I think it's close. HTML 5 really needs to add the change event to the spec. The only problem is that the callback function evaluates if (before == $(this).html()) before the content is actually updated in $(this).html(). setTimeout don't work, and it's sad. Let me know what you think.
Based on #balupton's answer:
$(document).on('focus', '[contenteditable]', e => {
const self = $(e.target)
self.data('before', self.html())
})
$(document).on('blur', '[contenteditable]', e => {
const self = $(e.target)
if (self.data('before') !== self.html()) {
self.trigger('change')
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
the code is:
$(document).ready(function () {
$('#url0, #url1, #url2, #url3, #url4, #url5, #url6, #url7, #url8, #url9, #url10').each(function(index, element) {
$(element).blur(function() {
var vals = this.value.split(/\s+/);
var $container = $(this).hide().prev().show().empty();
$.each(vals, function(i, val) {
if (i > 0) {
$("<span> </span>").appendTo($container);
}
$("<a />")
.html(val)
.attr('href',/^https?:\/\//.test(val) ? val : 'http://' + val)
.appendTo($container)
.click(handleClickEvent);
});
});
}).trigger('blur');
// ms to wait for a doubleclick
var doubleClickThreshold = 300;
// timeout container
var clickTimeout;
$('.aCustomDiv a').click(handleClickEvent);
$('.aCustomDiv').click(handleDivClick);
function handleClickEvent(e) {
var that = this;
var event;
if (clickTimeout) {
try {
clearTimeout(clickTimeout);
} catch(x) {};
clickTimeout = null;
handleDoubleClick.call(that, e);
return false;
}
clickTimeout = setTimeout(function() {
clickTimeout = null;
handleClick.call(that, event);
}, doubleClickThreshold);
return false;
}
function handleDivClick(e) {
var $this = $(this);
$this.parent()
.find('input,textarea')
.show()
.focus();
$this.hide();
}
function handleDoubleClick(e) {
var $this = $(this).parent();
$this.parent()
.find('input,textarea')
//.val($a.text())
.show()
.focus();
$this.hide();
}
function handleClick(e) {
window.open(this.href, '_blank')
}
});
HTML CODE:
<div style="padding:0 !important;margin-top:8px !important;">
<div class="aCustomDiv" style="padding: 0px ! important; display: block;">
www.google.com<span></span>www.facebook.com<span></span>www.wikipedia.org
</div>
<input type="text" value="www.google.com www.facebook.com www.wikipedia.org" onchange="immediateEditItemInCart(this.value,'url',0,'pr','35')" class="mandatory0" id="url0" style="display: none;" readonly="readonly">
this script does the following:
converts the text to url for those ids (url0 ...)
double click on the link makes it editable
one click on the div area, next to link makes it editable
one click on the link => goes to the page
my problem : for some reason i don't know, the one click on the link doesn't go to the page but edits it, only the FIRST time , after that works great, so i want the first function to be called also onload not only when blur. how can i do this ?
As far as loading on startup, javascript is single threaded so just firing a method will work if you keep things in order (... just add a couple of parens). But because you are trying to access the DOM you do want to wait for the elements to be available (otherwise you will get nothing back from a selector).
But you do already have:
$(document).ready( function(){} );
Which does exactly what you are asking for, it also has a shorthand of:
$( function(){} );
So I would have to agree with rodneyrehm that is it probably some collision that you have with other js on your page. You might want to encapsulate it a bit in some namespace to make sure that is the not the problem.
I wrote up a quick version that should get your on your way as a starting point if you are still having problems: http://jsfiddle.net/scispear/dUWwB/. I pulled the 'updateURL ' method out in case your ajax call (you mentioned) was just pre-populating the field (that way you could pass in the value also).
It also works with multiple inputs/displays which is what I think you were going for was not 100% sure.
I'm new to the whole JavaScript and jQuery coding but I'm currently doing this is my HTML:
<a id="tog_table0"
href="javascript:toggle_table('#tog_table0', '#hideable_table0');">show</a>
And then I have some slightly ponderous code to tweak the element:
function toggle_table(button_id, table_id) {
// Find the elements we need
var table = $(table_id);
var button = $(button_id);
// Toggle the table
table.slideToggle("slow", function () {
if ($(this).is(":hidden"))
{
button.text("show");
} else {
button.text("hide");
}
});
}
I'm mainly wondering if there is a neater way to reference the source element rather than having to pass two IDs down to my function?
Use 'this' inside the event. Typically in jQuery this refers to the element that invoked the handler.
Also try and avoid inline script event handlers in tags. it is better to hook those events up in document ready.
NB The code below assumes the element invoking the handler (the link) is inside the table so it can traverse to it using closest. This may not be the case and you may need to use one of the other traversing options depending on your markup.
$(function(){
$('#tog_table0').click( toggle_table )
});
function toggle_table() {
//this refers to the element clicked
var $el = $(this);
// get the table - assuming the element is inside the table
var $table = $el.closest('table');
// Toggle the table
$table.slideToggle("slow", function () {
$el.is(":hidden") ? $el.text("show") : $el.text("hide");
}
}
You can do this:
show
and change your javascript to this:
$('a.tableHider').click(function() {
var table = $(this.name); // this refers to the link which was clicked
var button = $(this);
table.slideToggle("slow", function() {
if ($(this).is(':hidden')) { // this refers to the element being animated
button.html('show');
}
else {
button.html('hide');
}
});
return false;
});
edit: changed script to use the name attribute and added a return false to the click handler.
I'm sure this doesn't answer your question, but there's a nifty plugin for expanding table rows, might be useful to check it out:
http://www.jankoatwarpspeed.com/post/2009/07/20/Expand-table-rows-with-jQuery-jExpand-plugin.aspx