Please refer to my previous question: How to get element in template html?.
Firstly, I follow the solution in How to get element in template html? to obtain the element.
var template = templateContent.content;
Then I try to bind a click event for searchBtn:
searchBtn.addEventListener('click', function() {
console.log('click'); // does not work
});
This binding fails, so I use the event delegation to handle event:
document.addEventListener('click',function(e){
if(e.target && e.target.id== 'searchBtn'){
console.log('click');
console.log(template.querySelector('#results'));
template.querySelector('#results').innerText = 'Foo';
}
});
The two logs are expected, but the last command to update the innerText does not work. On the other hand, I can update its content outside the click event. Any solution?
The final version of my code:
var link = document.querySelector('link[rel="import"][href="search.html"]');
var templateContent = link.import.querySelector('template');
var template = templateContent.content;
var searchBtn = template.querySelector('#searchBtn');
console.log(searchBtn);
template.querySelector('#results').innerText = 'Bar'; // ok
document.addEventListener('click',function(e){
if(e.target && e.target.id== 'searchBtn'){
console.log('click'); // ok
console.log(template.querySelector('#results')); // ok
template.querySelector('#results').innerText = 'Foo'; // does not work
}
});
Updates
Now I retrieve the results element outside the function:
var results = template.querySelector('#results');
Then inside the function, results.innerText = 'Foo'; works. However, this only updates the results itself, and the UI is not updated correspondingly.
How to update the UI inside the template?
You got it right, it is the binding issue. template is not available inside click function. You need to bind this. There are many ways to do this e.g.
document.addEventListener('click',onClick.bind(this));
onClick = function(e){
if(e.target && e.target.id== 'searchBtn'){
console.log('click'); // ok
console.log(template.querySelector('#results')); // ok
this.template.querySelector('#results').innerText = 'Foo'; // should work
}
}
There are many ways to get the HTML elements
You can get use
var sbtnElement = document.getElementById('searchBtn');
then add eventlistener to it
sbtnElement.addEventlistener('click', function() {
console.log('click'); // does not work
});
Naturally getElement by id will give you one element, and getElementsByClassName will return you an array of elements with the same classname. Same goes for getElementsByTagName.
hi i don't know what are you try to create for. but have you ever try to use template in script ? like this one
<script id="sampleTemplate" type="text/template">
// do the html here
</script>
So in that case you can bind event using that one. just a normal creating of html and binding javascript function.
You can refer to this blog. https://jonsuh.com/blog/javascript-templating-without-a-library/
I hope, i helped you thru this. :)
Related
I need to create a new notification div when an event triggers. Normally, I would do this in jQuery and use something like $("myDiv").append(newDiv), however the item selector to be appended to can't be selected with jQuery. Below is the code to explain more.
function notifMaker() {
var notification = $("<div>").addClass("notification", "id-dark", "overtop");
var notifButton = $("<button>").addClass("delete");
notification.append(notifButton);
notification.text("You've already saved this item. Click the x button to remove it from saved items.");
return notification;
}
and
ondragleave: function (event) {
// console.log("Drag Leave");
var draggableElement = event.relatedTarget;
// console.log(notifMaker());
draggableElement.append(notifMaker());
},
As you can see, the element I need to append to is actually from the event object. And if I try to append the notification from notifMaker into this, I just get [Object object] because jQuery is technically created an object in notifMaker. Is there any way around this? Any other methods I should consider using? Thanks. Here is an image of the object.
Using jQuery you can wrap any DOM element into jQuery object, so that you can use any of library methods on that element. In your case the code would look like this:
ondragleave: function (event) {
var draggableElement = $(event.relatedTarget);
draggableElement.append(notifMaker());
}
Using jQuery I need to:
persists list of all event handlers that are added to element,
remove them all for few seconds and
return things to initial state (reassign the same event handlers)
I found that get list of current listeners with (some jQuery inner mechanisms):
var eventsSubmitBtn = $._data(submitButton[0], "events");
Then I can remove all event listeners with
submitButton.off();
But last stem seems not to be working
setTimeout(function () {
$._data(submitButton[0], "events", eventsSubmitBtn);
}, 5000);
eventsSubmitBtn is an empty array.
Is this the way this should be done with initial setting and I'm need something like deep cloning for those objects or this can't be done with $._data?
N.B. I have possibility to add my cistom code after all other system js code, thus I can't place the code assigning to $.fn.on before anything. Code that I write will run the last on startup and other event listeners are attached before my scripts will run.
As you get a reference to the object returned by $._data(), any change to that object will not go unnoticed, i.e. after you invoke .off(), that object will have changed to reflect that there are no handlers attached any more.
You could solve this by taking a shallow copy of the object, (e.g. with Object.assign).
But this is not really a recommended way to proceed. According to a jQuery blog, "jQuery._data(element, "events") ... is an internal data structure that is undocumented and should not be modified.". As you are modifying it when restoring the handlers, this cannot be regarded best practice. But even only reading it should only be used for debugging, not production code.
It would be more prudent to put a condition in your event handling code:
var ignoreEventsFor = $(); // empty list
$("#button").on('click', function () {
if (ignoreEventsFor.is(this)) return;
// ...
});
Then, at the time it is needed, set ignoreEventsFor to the element(s) you want to ignore events for. And when you want to revert back to normal, set it to $() again.
Now adding this to all your event handlers may become a burden. If you stick to using on() for attaching event handlers, then you could instead extend $.fn.on so it will add this logic to the handlers you pass to it.
The following demo has a button which will respond to a click by changing the background color. With a checkbox you can disable this from happening:
/* Place this part immediately after jQuery is loaded, but before any
other library is included
*/
var ignoreEventsFor = $(), // empty list
originalOn = $.fn.on;
$.fn.on = function (...args) {
var f = args[args.length-1];
if (typeof f === 'function') {
args[args.length-1] = function (...args2) {
if (ignoreEventsFor.is(this)) return;
f.call(this, ...args2);
};
}
originalOn.call(this, ...args);
}
/* This next part belongs to the demo, and can be placed anywhere */
$(function () {
$("#colorButton").on('click', function () {
// Just some handler that changes the background
var random = ('00' + (Math.random() * 16*16*16).toString(16)).substr(-3);
$('body').css({ backgroundColor: "#" + random });
});
$("#toggler").on('change', function () {
// Toggle the further handling of events for the color button:
ignoreEventsFor = $(this).is(':checked') ? $("#colorButton") : $();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="colorButton">Change color</button><br>
<input type="checkbox" id="toggler">Disable events
Notice: the above code uses ES6 spread/rest syntax: if you need support for IE then that would have to be written using the arguments variable, apply, ...etc.
been struggling with this for a while now. Really hope someone can help me out.
Im trying to make divs which are removable when you click onto them.
They have a css-class 'tag-show' which is added and removed (this works), so the selector seems to be fine i guess..?
Why is the $(this).remove() not working?
$(document).ready(function() {
// selectors
var module = $(".divCreate");
var list = module.find(".listTag");
var button = module.find(".divButton");
// the actual issue
button.click(function() {
list.append("<div class='tag'>Tag</div>");
setTimeout(function() {
list.find(".tag").last().addClass("tag-show").on("click", function() {
$(this).removeClass("tag-show");
setTimeout(function() {
$(this).remove();
},190);
});
},40);
})
});
"this" loses it's context in your setTimeout.
add a var containing this and use that instead.
like so
var self = $(this);
setTimeout(function() {
$(self).remove();
},190);
I think that would fix it.
You should use the jQuery .on() function instead of .click(), as it works for dynamically created elements. Then you also don't have to apply it to the element each time it is dynamically added.
Somewhere else in your code within $(document).ready(function() {}); add this event handler:
$('.listTag').on('click', 'div.tag', function() {
$(this).remove();
});
and it should work for any element that is matched by the div.tag selector.
this inside the setTimeout callback is not referring to the div anymore. You could bind the callback to the div or do something like this:
var $this = $(this);
setTimeout(function() {
$this.remove();
},190);
I have made some improvments to your code, adding comments where it was needed. Just check the code below and its comments.
Your problem with remove is that the this in your case is "triggering" the closest parent, which is setTimeout, not clicked element.
For a better understanding just try to call console.log(this); inside timeout function and click function, you will see the difference.
I have made an fiddle which can help you understand better (open developer tools to see the console result)
$(document).ready(function() {
// when you keep DOM elements in variables is better to put $ in the beginning
var $module = $(".divCreate");
// faster than .find()
var $list = $(".listTag", $module);
// can not call 'on' with variable
//var $button = $(".divButton", $module);
// called through document since we need to handle dynamic added elements - check event delegation
$(document).on("click", ".divButton", function() {
$list.append("<div class='tag'>Tag</div>");
setTimeout(function() {
// some improvments
$(".tag", $list).last().addClass("tag-show");
}, 40);
// Just keep this if you have divButton attached to an anchor element
// Useful for preventing default behvaiour - in this case adding "#" to url
return false;
});
// Do not need to create the event inside that event
$(document).on("click", ".tag-show", function() {
// Since we use an element more than once is better to
// add it into a variable to avoid performance issues - js caches it and call the variable
var $el = $(this); // our needed 'this'
$el.removeClass("tag-show");
setTimeout(function() {
// 'this' here returns some properties of window (where setTimeout belongs), we need to call element cached above
//(the 'this' above contains what we need)
$el.remove();
}, 190);
});
});
Note : still do not understand why you need these timeouts but it s up to you, maybe you need them with a bigger interval :D
Not sure what you're trying to accomplish, but perhaps this is a better way to accomplish the same task?
// selectors
var module = $(".divCreate");
var list = module.find(".listTag");
var button = module.find(".divButton");
// the actual issue
button.click(function() {
list.append("<div class='tag'>Tag</div>");
})
$(document).on('click','.tag', function(){
$(this).remove();
});
https://jsfiddle.net/7daffjh8/
I have a function, which at the end of its task assigns a button to a new id.
function altChecker() {
var doc = document,
out = [],
appButton = doc.getElementById('appButton'),
//re = /click-me/gi,
output = doc.createElement('p');
output.setAttribute('id', 'output');
EventUtility.addHandler(appButton, 'click', function(e) {
//I have not included all the function details to concentrate on the question
appButton.id = 'appButtonNextChecker';
var appButtonNextChecker = doc.getElementById('appButtonNextChecker');
nextChecker(appButtonNextChecker);
});
}
function nextChecker(newBtnName) {
EventUtility.addHandler(newBtnName, 'click', function(e) {
$('#output').innerHTML = "";
console.log('next Checker, button!')
});
}
So basically there is one button in the DOM assigned to appButton ID initially, and then I change it doing:
appButton.id = 'appButtonNextChecker';
when the altChecker function fires...
Then I assign the button to a new variable, and pass in the variable to the next function...
var appButtonNextChecker = doc.getElementById('appButtonNextChecker');
nextChecker(appButtonNextChecker);
While I can see the buttons' ID change in the DOM, and I see the console.log fire in the nextChecker function,
$('#output').innerHTML = ""; //doesn't fire
AND the altChecker function fires as well (again)?! Haven't I severed the connection to the click function when I reassigned the new ID?
Any help would be appreciated!
Javascript doesn't remember that you initially attached the event through it's id. The event is attached to the element itself, not the ID. It's not like CSS that way.
In fact your variables are still holding the same element as well, so there's no need to create a new variable after changing the ID, either. Since you're using jQuery you can just type $(appButton).unbind(); to remove the event handler. You may also want to look into .on() and .off()
The problem is that you're trying to use the innerHTML property in a jQuery's object.
That property belongs to Element, and it will not work in the way you're using it.
You can use the document.getElementById method, and it will work fine:
document.getElementById('output').innerHTML = '';
Or you can use jQuery's html method:
$('#output').html('');
And you can even use the first element of the jQuery's array, and use innerHTML again:
$('#output')[0].innerHTML = '';
It's up to you, but the first option will be faster, for sure.
I have code like
<a id='lnk1' onclick='do something' >test</a>
Later on code is added to the same anchor tag like
lnk = document.getElementById('lnk1')
lnk.onclick = function() { do something}
Now what is happening is that in the second piece of code the onclick function of the anchor tag is getting overwritten. What I want to happen instead is that the first onclick's code is run and after that the 2nd onclick's is run.
There is a very simple, standards-compliant way to do this:
lnk1.addEventListener('click', function() {
// do something
});
This doesn't work in IE before version 9, so you'll need to do this:
var handler = function() {
// do something
};
if ("addEventListener" in lnk1) { // standards-compliant browsers
lnk1.addEventListener('click', handler);
} else { // Internet Explorer < v9
lnk1.attachEvent('onclick', handler);
}
This will work, and both the original function specificed in the HTML attribute and in the code above will run. HOWEVER it would be far nicer to define all your event handlers in the same place: in the Javascript. Think hard about removing event handling logic from your HTML attributes.
You could try this:
var lnk = document.getElementById('lnk1'); // don't forget var!
var oldHandler = lnk.onclick;
lnk.onclick = function(ev) {
if (oldHandler) oldHandler(ev);
// do something ...
};
That code saves a reference to the old handler, and if it's not empty it calls it before doing whatever else the new handler wants to do.
You could put the call to the old handler after the new code, or mixed in, or whatever.
<a id='lnk1' onclick='do something' >test</a>
JavaScript
lnk1 = document.getElementById('lnk1')
lnk1.addEventListener('click',function() { do something}, false);
when setting onclick you are overwrite existing attribute, but assign click through event listener then it will be ok.
You have a mistake in your statement:
JAVASCRIPT
lnk = document.getElementById('lnk1')
lnk1.onclick = function() { do something} \\ replace lnk1 with lnk
lnk.onclick = function() { do something} \\ this will work
You have defined lnk as variable but your are calling lnk1 with onclick event. This is a wrong statement.
USE HTML
<a id='lnk1'>test</a>
See the Demo: http://jsfiddle.net/tm5cX/