innerHTML and event delegation - javascript

I have a containing div that has multiple divs within which is updated every 25ms using innerHTML (for performance reasons). I have tried using event delegation to capture events but nothing I seem to do captures the click event. I think this may be due to the speed that the contents are getting updated. Any ideas would be very welcome.
My code:
$(document).ready(function () {
var canvas = $('#gCanvas')[0];
$(document.body).delegate('a', 'click', function (e) {
console.log(e.target);
});
var sprites = [];
for (var i = 0; i < 100; i++) {
sprites[i] = {x: i+10, y: i+10};
}
var doIt = function () {
var s = '';
for(var i = 0; i < sprites.length; i++) {
var spr = sprites[i];
spr.x++;
spr.y++;
s+= '<a id="s'+i+'" class="s" style="left:'+sprites[i].x+';top:'+sprites[i].y+'"></a>';
}
canvas.innerHTML = s;
};
//doIt();
setInterval(doIt, 50);
});

it easy if you use jquery. there is a .live function, but i am not sure if it quick enough. alternatively you could try to check jquery source to find the .live() code and find the solution
UPDATE
if you are trying to make a game then you can try to use svg for good browsers and vml for bad ones.

Related

Loop images on hover - setInterval issue

My code is running well...
but there is one thing I can't solve:
when I mouseover the image the loop starts well, but on subsequent mouseovers it starts changing faster and faster...
var Image = new Array("//placehold.it/400x180/?text=Welcome",
"//placehold.it/400x180/?text=To",
"//placehold.it/400x180/?text=My",
"//placehold.it/400x180/?text=Web+page",
"//placehold.it/400x180/?text=INPHP");
var Image_Number=0;
var Image_Length= Image.length;
function change_image(num){
Image_Number = Image_Number + num;
if(Image_Number > Image_Length)
Image_Number = 0;
if(Image_Number < Image_Length)
document.slideshow.src = Image[Image_Number];
return false;
Image_Number = Image_Length;
}
function auto () {
setInterval("change_image(1)", 1000);
}
<img src="//placehold.it/400x180/?text=Welcome" name="slideshow" onmouseover="auto()" />
On every mouseover you're reassigning a brand-new-intervalâ„¢ resulting in multiple functions running at the same time.
name on IMG tag is an obsolete HTML5 attribute - See also img tag # W3.org
"change_image(1)" ...strings inside setInterval or setTimeout tigger eval. Which is bad. The real function name should be used instead: setInterval(functionName, ms)
You're not managing well the argument num
You cannot have code after a return statement
Use the mouseenter event (instead of mouseover)
and many more errors....
Here's a remake:
var images = [
"//placehold.it/400x180/?text=Welcome",
"//placehold.it/400x180/?text=To",
"//placehold.it/400x180/?text=My",
"//placehold.it/400x180/?text=Web+page",
"//placehold.it/400x180/?text=INPHP"
];
var c = 0; // c as Counter ya?
var tot = images.length;
var angryCat = false;
// Preload! Make sure all images are in tha house
for(var i=0; i<tot; i++) {
var im = new Image();
im.src= images[i];
}
function changeImage() {
document.slideshow.src = images[++c%tot];
}
function auto() {
if(angryCat) return; // No more mouse enter
angryCat = true;
setInterval(changeImage, 1000);
}
<img src="//placehold.it/400x180/?text=Welcome" name="slideshow" onmouseenter="auto()" />
The increment and loop is achieved using ++c % tot
The angryCat boolean flag helps to know it the auto() already started (mouse was there!), in that case it will return; (exit) the function preventing the creation of additional overlapping intervals on subsequent mouseenter (which was your main issue).
Additionally, I'd suggest to keep your JS away from HTML, so instead of the JS attribute event
onmouseenter="auto()"
assign an ID to your image id="myimage" and use JS:
document.getElementById("myimage").addEventListener("mouseenter", auto, false);

Event target should be anchor but is image instead

I am working on a dialog script in Vanilla JS. I ran into a problem with the click event on the video image. Even tough the image is surrounded with an anchor tag it shows the image as the event.target on the "trigger-dialog-open" event.
Here is the HMTL:
<a class="trigger-dialog--open thumbnail" data-dialog-id="dialog-video" href="javascript:;">
<figure>
<img src="http://i.ytimg.com/vi/id/sddefault.jpg" alt="" />
</figure>
</a>
And this is the event in JS:
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < openTriggers.length; i++) {
openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
}.bind(this), false);
}
The event handler wants to know the dialog-id from the anchors data attribute. It can't be found because it thinks the image is the event.target, not the actual anchor. How can I correct this? Thanks!
Use event.currentTarget. The event.target is supposed to be the img element since that is what the user has clicked on. The click then bubbles up through the image's containers. event.currentTarget gives you the element that the click handler was actually bound to.
(Or if you didn't bind this to some other object you could use this within the click handler and it should also be the current target.)
I have a few questions is the var openTriggers supposed to be a part of a module hash? Because if it's global then you don't use a this, you only add a this, if it's referencing a variable that the function is also contained in. For example:
var aThing = {
openTriggers: document.getElementsByClassName('trigger-dialog--open'),
openModal: null,
openDialog: function(clickedThingAttr){
if(this.openModal !== null){
this.openModal.style.display = 'none';
}else{
this.openModal = document.getElementById(clickedThingAttr);
}
this.openModal = document.getElementById(clickedThingAttr);
this.openModal.style.display = 'block';
},
setEventListenersNStuff: function(){
for (var i = 0, n = this.openTriggers.length;i < n; i++) {
this.openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
});
};
}
};//end of aThing hash object
aThing.setEventListenersNStuff();
There are a few issues here:
1. why are you using .bind I think that is a jQuery thing, you want to pass a string to another function when an object is clicked, there no need for binding at all.
2. Also make sure that if you want to do something like open a modal, there is no need to call another method unless it's kinda complex.
3. What about other potential dialogs, it seems that when a .trigger-dialog--open is clicked you're just showing that one one modal with the embedded id, but what about others? Make sure all modals are closed before you open a new one, unless you want to have like 10 modals are open.
A thing to note: I added the line var i = 0, n = openTriggers.length;i < n; i++, now in this case it's silly optimization, and I heard for modern browsers this doesn't apply, but to explain why I added it, is because i < openTriggers.length would count and integrate the array N times. (This may be an outdated optmiziation).
If you meant global
Below I added a different set of code, just in case you meant that var openTriggers is global, kinda like you wrote above. Also I used querySelectorAll for this which is like jQuery's $('.thing') selector.
anyhoo, I also added
var openTriggers = document.querySelectorAll('.trigger-dialog--open');
var n = openTriggers.length;
function openDialog(ddId){
for (var i = 0;i < n; i++) {
openTriggers[i].style.display = 'none';
};
document.getElementById(ddId).style.display = 'block';
};
for (var i = 0;i < n; i++) {
openTriggers[i].addEventListener("click", function (event) {
openDialog(event.target.getAttribute('data-dialog-id'));
});
}
}
So for the question of hiding already open modals I would suggest you could either cache the open Dialog within a module, or you could toggle a class, which would be less efficient since it would require an extra DOM search. Additionally you could add a if this.openModal.id === clickedThingAttr to hide if open, that way you got a toggle feature.
Anyways I suggest you read up on this stuff, if you want to use plain JS but would like the features of jQuery: http://blog.romanliutikov.com/post/63383858003/how-to-forget-about-jquery-and-start-using-native
Thank you for your time.
You can use a closure
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < this.openTriggers.length; i++) {
(function(element) {
element.addEventListener("click", function (event) {
element.openDialog(event.target.getAttribute('data-dialog-id'));
}, false)
})(openTriggers[i]);
}

Change zIndex using javascript

It works on the premade divs but on the newly made one, it doesn't work. How do I fix that?
Here's the code on the event for changing the zIndex:
$(".widget").mousedown(function (event) {
var ws = document.getElementById("widget-workspace");
var list = ws.children, x=0;
for(x=0;x<ws.children.length;x++){
console.log(ws.children[x].id);
$("#"+ws.children[x].id).css("zIndex", 99);
}
$(this).css("zIndex", 100);
});
Now, here's the code for adding the div:
document.getElementById("widget-dialog-button").onclick = function () {
var ws = document.getElementById("widget-workspace");
var list = ws.children;
var x, w = document.getElementById("select-widget");
var widget = w.options[w.selectedIndex].value;
var c = document.getElementById("select-widget-color");
var color = c.options[c.selectedIndex].value;
var left = 0, top = 25, docWidth = ws.offsetWidth, check;
for(x=0; x < list.length; x++){
docWidth -= 325;
left += 325;
if(docWidth < 325){
check = false;
docWidth = ws.offsetWidth;
left = 0;
top += 210;
}else{
check = true;
}
}
x-=2;
var iDiv = document.createElement('div');
iDiv.id = 'widget_'+x;
iDiv.className = 'widget';
iDiv.style.backgroundColor = color;
iDiv.style.left = left;
iDiv.style.top = top;
ws.appendChild(iDiv);
$(function() {
$( ".widget" ).draggable();
});
};
If you guys need anything else, feel free to ask.
The answer is quite simple :
"It works on the premade divs but on the newly made one, it doesn't work. How do I fix that?"
It's normal :
$(".widget").mousedown(...);
// should be read as (except that a new var is not created)
var $currentlyExistingWidgets = $(".widget");
$currentlyExistingWidgets.mousedown(...);
To each element of class widget currently existing, you bind an event.
If you want to bind events to elements not existing... You have to reconider your way of thinking and then bind an event listener to a container always existing, with an event delegation mechanism and proper filtering.
For example the following code should catch the event for all .widget, created before or after :
// http://api.jquery.com/on/
$('body').on('mousedown', '.widget', function() { ... });
If you want to search and learn, the key concepts are event bubbling and event delegation.
The way you're attaching the mousedown listener means that only the element that exist at that point will be listened to. Use the on method:
// May want to use something other than body
$('body').on('mousedown', '.widget', function() {
console.log('go');
});
Docs

Elements By ClassName Failing

I am creating sliding menus in JavaScript, and the following is my init() function:
function init() {
var menus = new Array();
var allElems = document.getElementsByTagName("*");
for (var i = 0; i < allElems.length; i++) {
alert(allElems[i]);
if (allElems[i].className == "navG") {
alert(allElems[i]);
menus.push(allElems[i]);
}
}
/* assign the openMenu function to the onclick event for each
Menus item */
for (var i = 0; i < menus.length; i++) {
alert(menus[i]);
menus[i].onclick=openMenu;
}
document.getElementById("logo").onclick = closeMenu;
document.getElementById("linkList").onclick = closeMenu;
document.getElementById("main").onclick = closeMenu;
}
The problem seems to be in the first for loop. This is definitely the correct class name..just for reference, this is the type of HTML that I am referring to:
<div class="navG" id="gallery1" style="position: absolute; top: 180px; left: -150px; " >
Is there an obvious, or not so obvious reason, that this is not adding the elements to menus?
You got a bug here
document.getElementById("logo").onclick = closeMenu();
document.getElementById("linkList").onclick = closeMenu();
document.getElementById("main").onclick = closeMenu();
You calling closeMenu, not assigning it.
Needs to be
document.getElementById("logo").onclick = closeMenu;
document.getElementById("linkList").onclick = closeMenu;
document.getElementById("main").onclick = closeMenu;
In the page you linked to, in your family.js script, this line:
window.onLoad = init();
says to run the init function immediately and assign its return value to window.onLoad. Because it is running immediately the actual document hasn't been parsed yet so it doesn't find any of your elements. You need to say this:
window.onload = init;
which assigns a reference to the init function to window.onload so that that function will be run later after all of the elements have been parsed.
Also onload should have a lowercase l.
(There are some other problems in your code, e.g., you don't seem to have elements with the ids "linkList" or "main", but I think what I said above is the main problem with the part you are asking about.)
Whoops, you're missing quotes around that "navG" in the first loop.

Make all hyperlinks in the site do nothing

I want to make a script that disables hyperlinks and instead fires a function when one is clicked.
should work as
<a onclick="talk('http://google.com)'></a>
Is there a way to know when the wants to redirect and instead run "talk()" or display an alert window?
var anchors = document.getElementsByTagName('a');
for (var a = 0; a < anchors.length; a++){
anchors[a].href = "javascript:talk('" + anchors[a].href + "');";
}
Use some discretion though...
This solution uses (DOM Level 0) event handling instead of touching the href directly.
(function () {
var anchors = document.getElementsByTagName('a'),
i = anchors.length;
while (i--) {
anchors[i].onclick = function () {
talk(this.href);
return false;
};
}
}());
Edit: The benefit of this approach is it's much simpler to put the href back when you want to. Given an anchor tAnchor, you merely need to unset the onclick attribute:
tAnchor.onclick = null

Categories

Resources