I have a situation where different elements need to be re-sized by JavaScript when the window is re-sized.
Currently for each element I am simply attaching a new event like so:
window.addEventListener('resize',function(){ self.resize(MyEl);}, false );
I'm starting to think this isn't a smart idea, because say i have to resize 50 elements, i am currently attaching 50 events (gross exaggeration, but you can see how it isn't a smart design). Not to mention, when i remove the element, the event is still there!
So i am wondering what would be a better way to handle the resize event so it will then process different resize functions that i define but can equally remove said functions when they are no longer relevant.
What is considered a good approach for something like this so i then only need one event attached.
I would use an object to map an element to function. For example:
var toResize = {'#banner': self.bigResizer, '.ads': self.smallResizer};
Then you can easily dynamically add/remove elements and their resize function.
toResize['#footer'] = self.bigResizer;
delete toResize['#banner'];
Then in your event handler you'd do something like:
for (var sel in toResize) {
if (toResize[sel]) {
toResize[sel].call(self, document.querySelector(sel));
}
}
I think, adding separate events for each element is the correct approach compared to what I will explain below. The reason is that browser will run the event function when its time comes, so the browser won't freeze.
Also, I am sure you know it already, you can remove the event function if it was a single function instead of anonymous function. So, turning the system into that style would be helpful.
Another approach, but this might freeze your browser whenever the window is resized.
Define an array. Each item of array is an htmlElement.
var elementsToBeResized = [];
Define a single function that receives the resize event for elements.
function element_resize( elIndex ){
var htmlElement = elementsToBeResized[ elIndex ];
if( !document.body.contains( htmlElement ) ){
elementsToBeResized[ elIndex ] = null;
return;
}
// ... do your resizing things ...
}
When window is resized, call a function that loops over the array elementsToBeResized. And instead of adding new event, either replace a null item in elementsToBeResized, or append it.
But do not forget, because all events are called sequentially without a break, it might create freezing issue as I am telling third time.
You can simply resize all the elements in one event handler.
The list of elements would need to be maintained in global object of some form though.
Using jQuery, for example:
var $all = $('div')
$( window ).resize( function(){
$all.each(function () {
console.log($(this).attr('class'));
// individual element resize logic goes here e.g.
// $(this).myresizefunc();
})
});
jsfiddle here:https://jsfiddle.net/jsheridan390/aLdap6j8/
Related
And therefore adding a bit of computing load?
For those of you unfamiliar with the .one() jquery function it basically triggers an event just once. Such as if you wanted to add a div on the first time a page is scrolled.
To bring background to the matter, I came across this question:
How to alert when scroll page only first time using javascript?
I have been in projects where I had to add hundreds or thousands of events, so for me it’s always very important to optimize computing power, plus, I am a curious person so I just need to know.
One of the answers where the guy uses vanilla javascript is basically an endless loop where you switch a boolean on the first instance and basically have to continually enter the function to see if it has been already triggered.
var xxx;
$(window).scroll(function () {
if(!xxx)
{
xxx = true;
var div = $("#myDiv");
alert(div.height());
}
});
My idea is that jquery being already heavy on the page it probably just performs this same action under the hood, but I would like to be completely certain as for my future implementations.
No. jQuery's .one works similarly to, for example:
calling addEventListener, and then, in the callback, calling removeEventListener
calling addEventListener with { once: true } in the options object
in jQuery, like calling .on, and then, in the callback, calling .off
Once the listener runs once, it's de-attached; no further logic takes place when the event occurs in the future, because the listener is no longer connected at all.
So .one is very light on computing resources, even if you add lots and lots of .ones.
You can see the source code of one here:
if (one === 1) {
origFn = fn;
fn = function (event) {
// Can use an empty set, since event contains the info
jQuery().off(event); // <-------------------------------------------------
return origFn.apply(this, arguments);
};
// Use same guid so caller can remove using origFn
fn.guid = origFn.guid || (origFn.guid = jQuery.guid++);
}
return elem.each(function () {
jQuery.event.add(this, types, fn, data, selector);
});
where jQuery() returns a jQuery collection containing elements matching the current selector. When one is called, the callback is wrapped in another that calls .off as soon as the function is executed.
The sample code in the answer you linked to is quite inefficient, and should not be used, especially for scroll events, which fire very frequently.
I am currently writing a program in JS using jQuery, which is basically a checkers game.
I am using jQuery's .on() and .off() functions to create events for each of the pieces. What happens is that the program will loop through each of the pieces and will set a function to be called when the piece is clicked. This function will then show the player the available moves that the piece can make.
This is setup using a for-loop and this code:
$("#" + String(playerPositions[i])).on('click', function() {movePiece(validMoves, this)});
This passes the valid moves of that piece as well as the id of that piece to the movePiece function which then deals with highlighting the moves.
The problem lies in my "clean up" function, where I want to remove the onClick handler from all the pieces once a move is made. I use this code:
var elements = $('.' + classToClean);
//clean off the onclick
elements.off("click"); <-- this doesn't work
//clean off the classes
elements.removeClass(classToClean);
The strange thing is that a) the .removeClass function works perfectly, and b) the onClick attribute only is removed from the piece that I have just moved.
I have tried using attaching an empty function to the piece, but this did not work. I also cannot use $('.validPieces').on('click', function () ... ) because I need to pass variables unique to the piece with each piece's onclick.
Thanks in advance for any help, and I apologise about the wall of text but I wanted to make sure everything was clear.
Using .off('click') should remove all event handlers of that type. If that doesn't work it is likely the element(s) you are removing from don't match the ones they were attached to.
If that removes more than you want, you will need to include a reference to your handler in the .off() call. To preserve the different validMoves variable for each call you will need to use a closure:
function move(validMoves) {
return function() {
movePiece(validMoves, this);
}
}
// within your for loop
keepMoveFn[i] = move(validMoves);
$("#" + String(playerPositions[i])).on('click', keepMoveFn[i] );
// elsewhere in your code:
//clean off the onclicks
keepMoveFn.forEach(function(fn) {
el.off("click", fn );
}
Note that you will need to either keep a reference to the move function or have access to it when you call the .off() function. In the snippet above I assume you are keeping an array of functions that you can then later iterate to remove the click events.
Fiddle (Uses JQuery) - http://jsbin.com/ponikasa/1/edit
I know JQuery is Javascript, but for the sake of an argument how do you write the following in pure Javascript without the need for a js library like JQuery?
$(document).ready(function() {
$('.preview-site').on('click', function(){
window.open('javascript:document.write("'+ $('.workflow').val() +'")', 'Opened Page', 'width=660, height=440');
return false;
});
});
I tried this, but doesn't work.
Any help is greatly appreciated.
window.onload = function() {
var preview = document.getElementsByClassName("preview-site"),
code = document.getElementsByClassName("workflow")[0].value;
preview.onClick = function() {
window.open('javascript:document.write("'+ code = +'")', 'Opened Page', 'width=660, height=440');
return false;
}
}
Well to write in javascript you would do the following
document.addEventListener('DOMContentLoaded', function() {
var previewSite = this.querySelectorAll('.preview-site');
var handler = function() {
var workflow = document.querySelector('.workflow')
window.open('javascript: document.write(' + workflow.value + ')', 'Opened Page', 'width=660, height=440')
return false;
};
for( var i = 0; i < previewSite.length; i++) {
previewSite[i].addEventListener('click', handler);
}
});
The problem you had is getElementsByClassName returns a collection, so you cannot use value or onclick on the collection.
I use querySelectorAll because it's easier and has almost better support that getElementsByClassName
I don't usually answer questions like this, but I am highly supportive of anyone that uses jQuery that want's to actually learn javascript it's self
also, in your question, you have onClick, for the event handler you want onclick
For one minor performance improvement you could move workflow out of handler, that way it won't fetch it on every click, only do this if you don't intend to add dynamic .workflow
Yeah, and also. (as pointed out in comments) window.onload is not the same as document ready, window.onload will wait for images & media to be fully loaded, so use DOMContentLoaded
One of the things jQuery selectors do is try to abstract the "array" when calling functions and assigning handlers. Consider something like this:
$('.preview-site').on('click', function(){
// code
});
This code doesn't just assign the click handler. On a lower level than that presented by the jQuery interface, this iterates the array of .preview-site elements and assigns the click handlers to each element. Sometimes it's one element, sometimes it's many. (Sometimes it's none.) jQuery makes the interface the same regardless of the count.
Without it, you need to handle that difference explicitly. These values are arrays:
var preview = document.getElementsByClassName("preview-site"),
code = document.getElementsByClassName("workflow");
Even if each one only finds a single element by that class name, the result from document.getElementsByClassName() is an array. So even if the array has only one element, it's still an array. And you can't assign a handler to an array, you need to assign it to each element in the array. Potentially something like this:
for (var i = 0; i < preview.length; i++) {
preview[i].addEventListener('click', function() {
window.open('javascript:document.write("'+ code[i].value[0] +'")', 'Opened Page', 'width=660, height=440');
return false;
}
}
Naturally, you'd probably want to put in some checks to ensure that the two arrays are the same length before assuming that for each preview element there exists a code element. But the principle is the same. You just need to account for the enumeration of the array manually.
Like if I wanted to select both document and window elements.
$(window, document).doStuff();
Doesn't work. Probably because according to the docs, 2nd argument is some "context" thing...
Basically I'm just looking for an alternative too
$(window).doStuff();
$(document).doStuff();
var one = $("#1");
var two = $("#2");
var three = $("#3");
var four = $("#4");
$([one, two, three, four]).each(function() {
// your function here
});
The document ready event executes already when the HTML-Document is loaded and the DOM is ready, even if all the graphics haven’t loaded yet.
If you want to hook up your events for certain elements before the window loads, then $(document).ready is the right place.
$(window).add(document).doStuff();
I have a question, which I can't seem to decide on my own so I'll ask here. The question is simple: whether to use inline JavaScript events or adding them afterwards? The theory in the background isn't that simple though:
I have a JS object that returns HTML. Whenever you create this object, the returned HTML will be used for another object's HTML. Therefore, adding events is not straight-forward. See:
secret.object = function() {
this.init = function() {
var html = '<div>and lots of other HTML content</div>';
return html;
};
}
This is a sample object that is created within this code:
for ( var i = 0; i < countObjects; i++) {
var obj = arguments[0].content[i];
generatedContent += spawnSecret(); /* The spawnSecret() is a method that initializes the object, and calls its init() method that returns the HTML.
}
and then later on I create a new object whose property "content" will be set to "generatedContent". It needs to add the events within the secret object I have, nowhere else. And since my system is built like this, I see only two ways around this: use inline events or build HTML using method calling instead of returning.
Hopefully, this wasn't too hard to understand.
If you created the elements using document.createElement() (but didn't append them to the DOM) and kept a reference to them, then you could populate them with the text content and attach event handlers to them, without having to use inline events.
When you are ready to reveal your 'secret' you could then append them to the DOM, rather than dumping in a text string of HTML tags and content.
I cant see it making much of a difference - if you just render your events using onclick etc. JavaScript event handlers they will be evaluated as soon as you append your generated HTML to the document, rather than you having to call attachEvent() or whatever.