Nested setInterval function on scroll not working - javascript

I am trying to set interval to a function that is only called when user scrolls below a certain height. My code return no errors and the function does not run either. However, I tried logging a random number at the end of the function and it doesn't so I think it has to do with my function. Take a look:
var firstString = ["This ", "is ", " me."];
var firstPara = document.querySelector("#firstPara");
var distanceSoFar = (document.body.scrollTop);
window.addEventListener("scroll", function() {
setInterval(slideIn, 450);
});
function slideIn() {
if (distanceSoFar > "130") {
for (var i = 0; i < firstString.length; i++) {
var stringOut = firstString.shift();
firstPara.innerHTML += stringOut;
console.log("5");
}
}
};
firstPara is just a paragraph in a div on the page. So the idea is to place some text in it on interval when a user scrolls into that view like so:
body {
height: 1000px;
}
div {
position: relative;
top: 700px;
}
div #firstPara {
border: 1px solid;
}

Part of your code is working. It handles the scroll event correctly and the slideIn function is called but the condition distanceSoFar > "130" is never met.
I'd suggest two changes to make your code work as you expect:
Use document.documentElement.scrollTop instead of document.body.scrollTop. document.body.scrollTop may return incorrect values (0) on some browsers. Look at this answer, for example.
Declare distanceSofar inside of the slideIn function. You declared the variable on the top of your code, so it stores a static value of the scrollTop property.
I'd avoid using setInterval inside a scroll event handler, you are setting a lot of intervals and not clearing them. I added some console.logs to show you how the slideIn function keeps being called even when the user is not scrolling.
A final tip: the scrollTop property is a number, so you can compare it to 130 instead of "130".
Here is the working fiddle.

I tried your code. I think it is working as you expected. I also added a clearInterval inorder to clear the timer after printing all text and also to avoid repeated calling.
<html>
<head>
<style>
body {
height: 1000px;
}
div {
position: relative;
top: 700px;
}
div #firstPara {
border: 1px solid;
}
</style>
<script>
function start(){
var s =0;
var interval = undefined;
function slideIn() {
console.log(distanceSoFar);
if (distanceSoFar > "130") {
while ( firstString.length > 0) {
var stringOut = firstString.shift();
firstPara.innerHTML += stringOut;
console.log(s++);
}
clearInterval(interval);
interval = undefined;
}
};
var firstString = ["This ", "is ", " me."];
var firstPara = document.querySelector("#firstPara");
var distanceSoFar = (document.body.scrollTop);
window.addEventListener("scroll", function() {
if(!interval)
interval = setInterval(slideIn, 450);
});
};
</script>
</head>
<body onload="start()">
<div id="firstPara"/>
</body>
<html>

Related

JS & HTML - place on html on which the JS script will appear

I'm quite new to JS.
I want to have my html page stay the same when JS text will be appearing in one exact place without starting from blank page.
I trigger JS function via button on HTML, function in HTML:
function match () {
setTimeout(function () {
player_hp -= monster_dmg
monster_hp -= player_dmg
if (player_hp<=0) {
document.write("\nPlayer dies!")
menu();
return;
}
if (monster_hp<=0) {
document.write("\nPlayer wins!")
menu();
return;
}
if (fight=1) {
document.write("\nPlayer hp:" + player_hp)
document.write("\nMonster hp:" + monster_hp)
document.write("\n");
match()
}
}, interval)
}
One easy way to handle this is to simply create a <div> or a <span> element that has an ID attribute like this:
<div id="status"> </div>
Now you can access this element by using the Javascript method
document.querySelector("#status") and then use the innerHTML function of that element to change the internal content. You can even place the document.querySelector function into a convenient function which I have named send_status()
Here's the whole thing
/* default values */
var player_hp = 300;
var monster_dmg = 30;
var monster_hp = 200;
var interval = 500;
var player_dmg = 50;
match();
/* heres a function that will replace your document.write() functions */
function send_status(message) {
document.querySelector("#status").innerHTML = message;
}
function match() {
setTimeout(function() {
player_hp -= monster_dmg
monster_hp -= player_dmg
if (player_hp <= 0) {
send_status("\nPlayer dies!") // replaced document.write with send_status
menu();
return;
}
if (monster_hp <= 0) {
send_status("\nPlayer wins!")
menu();
return;
}
if (fight = 1) {
send_status("\nPlayer hp:" + player_hp)
send_status("\nMonster hp:" + monster_hp)
send_status("\n");
match()
}
}, interval)
}
function menu() {}
#game {
width: 100%;
height: 100px;
border: 2px solid black;
}
#status {
width: 100%;
height: 50px;
background-color: grey;
}
<div id="game">Game Goes Here</div>
<!-- here is your status area -->
<div id="status"></div>
You should create a results div, in which will be shown the match result.
Just add <div id="match_results"></div> in your HTML code.
And replace all yours document.write() for
document.getElementById('match_results').innerHTML += "<br>Player wins!"
This command is appending content in the element with ID match_results.
You should use <br> instead of \n because it is the proper way to break line in HTML code.

How to know when a particular button is clicked in Javascript?

I'm making a function that displays a modal, and the modal has two buttons. I want this function to wait until one of the two buttons has been clicked, and return a value that corresponds to which button is clicked.
Here's a sample code that I came up with:
function myFunc()
{
var val=0;
buttonA = document.getElementById('buttonA');
buttonB = document.getElementById('buttonB');
buttonA.onclick = function(){
//do something
val = 1;
}
buttonB.onclick = function(){
//do something
val = 2;
}
while(val == 0);
return val;
}
The problem in this code is that the page becomes unresponsive because of the infinite loop, hence it isn't possible to change the value of val once initialised.
To be more precise, I want the main thread (on which myFunc is being implemented) to sleep until one of the other two threads (each of buttonA and buttonB) is clicked.
Is there some other work-around for this ? Please answer in Javascript only (no jQuery). Thanks.
Try something more like this:
function myFunc()
{
buttonA = document.getElementById('buttonA');
buttonB = document.getElementById('buttonB');
buttonA.onclick = function(){
//do something
differentFunc(1)
}
buttonB.onclick = function(){
//do something
differentFunc(2)
}
}
This is a different way to make the function more versatile (edited per your comment):
function myFunc(callback)
{
buttonA = document.getElementById('buttonA');
buttonB = document.getElementById('buttonB');
buttonA.onclick = function(){
//do something
callback(1)
}
buttonB.onclick = function(){
//do something
callback(2)
}
}
and call it like
myFunc(function(result) {
// do stuff with result
}
Javascript is naturally single-threaded. Any code that waits infinitely like that will cause a hangup and disallow input. There are ways to write async functions, namely using Promises like I did for a minute there, but it's generally easier to make your code work synchronously.
If I understand the OP's purpose is to create a modal with 2 choices like a confirm()? But for some reason confirm() isn't suitable? So a value on each button and it waits for user interaction? Unless I'm missing something fairly important, I have made a dynamically generated modal (no manual markup) that has 2 buttons. The purpose and result elude me so I left it with one event listener and a function with a simple ternary condition to which the alerts can be replaced by appropriate statements or expression at OP's discretion.
SNIPPET
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
.modal {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background:transparent;
}
.ui {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: table-cell;
border: 3px ridge grey;
border-radius: 6px;
}
button {
font-size: 24px;
}
</style>
</head>
<body>
<script>
var frag = document.createDocumentFragment();
var modal = document.createElement('div');
var ui = document.createElement('div');
var on = document.createElement('button');
var off = document.createElement('button');
modal.className = 'modal';
ui.className = 'ui';
on.id = 'on';
on.textContent = 'On';
off.id = 'off';
off.textContent = 'Off';
frag.appendChild(modal);
modal.appendChild(ui);
ui.appendChild(on);
ui.appendChild(off);
ui.addEventListener('click', status, false);
function status(e) {
var tgt = e.target.id;
tgt === 'on' ? alert('ON!') : alert('OFF!');
}
document.body.appendChild(frag);
</script>
</body>
</html>

Pop pre-pended div items in jQuery (on message receive)

I have a small function the uses a web socket to receive realtime updates. When a new response is received the function prepends a div in the html. I only want the updates to be shown in a window within the page, ie. only ~10 prepended divs should be showing at the most. Ideally I need to pop the oldest div before it overflows out of its parent div.
My question:
How do I pop divs before they overflow the parent? Considering I will receive a response nearly every second or so, what is the most efficient way of doing this?
#HTML
<div class="content">
<p>archienorman-thesis $ realtime_bitcoin</p>
<div id="messages"></div>
<!-- window content -->
</div>
#JS FUNCTION
var total = 0;
var btcs = new WebSocket('wss://ws.blockchain.info/inv');
btcs.onopen = function () {
btcs.send(JSON.stringify({"op": "unconfirmed_sub"}));
};
btcs.onmessage = function (onmsg) {
console.log(response);
var response = JSON.parse(onmsg.data);
var amount = response.x.out[0].value;
var calAmount = amount / 100000000;
var msgs = $('#messages .message');
var count = msgs.length;
if (count == 10) {
msgs.first().remove();
}
$('#messages').prepend("<p class='tx'> Amount: " + calAmount + "</p>");
}
Make the container div overflow: hidden, check if there is overflow using JS scrollHeight and clientHeight.
CSS
#messages {
overflow: hidden;
}
JS
Remove your if statement and add this after your prepend() line:
$('#messages').prepend("<p class='tx'> Amount: " + calAmount + "</p>");
$('#messages').css("overflow", "scroll");
if($('#messages')[0].scrollHeight > $('#messages').height())
msgs.last().remove();
$('#messages').css("overflow", "hidden");
The above quickly makes #messages have the overflow: scroll property in order for the scrollHeight property to work. If there is extra scroll, then it deletes the element.
See Demo.
NOTE
See my comment to your question. You should be removing last(), not first(). See the demo as an example -- try changing last() to first(), and it will not work.
I think something like this should work. This is test code that will basically remove the extra child elements when their combined width exceeds that of the container.
HTML
<div class="container">
<div>1.Test</div>
<div>2.Test</div>
<div>3.Test</div>
<div>4.Test</div>
<div>5.Test</div>
<div>6.Test</div>
<div>7.Test</div>
<div>8.Test</div>
<div>9.Test</div>
<div>10.Test</div>
<div>11.Test</div>
<div>12.Test</div>
<div>13.Test</div>
<div>14.Test</div>
<div>15.Test</div>
</div>
CSS
.container {
width:1000px;
}
.container div {
width: 100px;
display: inline-block;
}
Javascript
function a () {
var containerWidth = $('div.container').width();
var childWidth = $('div.container div').width();
var childCount = $('div.container div').length;
var removeCount = (childWidth * childCount) - containerWidth;
if(removeCount > 0) {
removeCount = Math.floor(removeCount/childWidth);
console.log(removeCount);
for(i = childCount; i > (childCount-removeCount); i--) {
$('div.container div:nth-child('+i+')').remove();
}
}
}
a();
Fiddle: https://jsfiddle.net/L3r2nk6z/5/

How can I detect changes in DIV element using pure JavaScript [duplicate]

I've the following sample html, there is a DIV which has 100% width. It contains some elements. While performing windows re-sizing, the inner elements may be re-positioned, and the dimension of the div may change. I'm asking if it is possible to hook the div's dimension change event? and How to do that? I currently bind the callback function to the jQuery resize event on the target DIV, however, no console log is outputted, see below:
<html>
<head>
<script type="text/javascript" language="javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" language="javascript">
$('#test_div').bind('resize', function(){
console.log('resized');
});
</script>
</head>
<body>
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" />
<input type="button" value="button 2" />
<input type="button" value="button 3" />
</div>
</body>
</html>
A newer standard for this is the Resize Observer api, with good browser support.
function outputsize() {
width.value = textbox.offsetWidth
height.value = textbox.offsetHeight
}
outputsize()
new ResizeObserver(outputsize).observe(textbox)
Width: <output id="width">0</output><br>
Height: <output id="height">0</output><br>
<textarea id="textbox">Resize me</textarea><br>
Resize Observer
Documentation: https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API
Spec: https://wicg.github.io/ResizeObserver
Current Support: http://caniuse.com/#feat=resizeobserver
Polyfills: https://github.com/pelotoncycle/resize-observer
https://github.com/que-etc/resize-observer-polyfill
https://github.com/juggle/resize-observer
There is a very efficient method to determine if a element's size has been changed.
http://marcj.github.io/css-element-queries/
This library has a class ResizeSensor which can be used for resize detection. It uses an event-based approach, so it's damn fast and doesn't waste CPU time.
Example:
new ResizeSensor(jQuery('#divId'), function(){
console.log('content dimension changed');
});
Please do not use the jQuery onresize plugin as it uses setTimeout() in combination with reading the DOM clientHeight/clientWidth properties in a loop to check for changes. This is incredible slow and inaccurate since it causes layout thrashing.
Disclosure: I am directly associated with this library.
Long term, you will be able to use the ResizeObserver.
new ResizeObserver(callback).observe(element);
Unfortunately it is not currently supported by default in many browsers.
In the mean time, you can use function like the following. Since, the majority of element size changes will come from the window resizing or from changing something in the DOM. You can listen to window resizing with the window's resize event and you can listen to DOM changes using MutationObserver.
Here's an example of a function that will call you back when the size of the provided element changes as a result of either of those events:
var onResize = function(element, callback) {
if (!onResize.watchedElementData) {
// First time we are called, create a list of watched elements
// and hook up the event listeners.
onResize.watchedElementData = [];
var checkForChanges = function() {
onResize.watchedElementData.forEach(function(data) {
if (data.element.offsetWidth !== data.offsetWidth ||
data.element.offsetHeight !== data.offsetHeight) {
data.offsetWidth = data.element.offsetWidth;
data.offsetHeight = data.element.offsetHeight;
data.callback();
}
});
};
// Listen to the window's size changes
window.addEventListener('resize', checkForChanges);
// Listen to changes on the elements in the page that affect layout
var observer = new MutationObserver(checkForChanges);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
// Save the element we are watching
onResize.watchedElementData.push({
element: element,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight,
callback: callback
});
};
I DO NOT recommend setTimeout() hack as it slows down the performance!
Instead, you can use DOM ResizeObserver method for listening to Div size change.
const myObserver = new ResizeObserver(entries => {
// this will get called whenever div dimension changes
entries.forEach(entry => {
console.log('width', entry.contentRect.width);
console.log('height', entry.contentRect.height);
});
});
const someEl = document.querySelector('.some-element');
// start listening to changes
myObserver.observe(someEl);
// later, stop listening to changes
myObserver.disconnect();
Old answer using MutationObserver:
For listening to HTML element attributes, subtree, and class changes:
JS:
var observer = new MutationObserver(function(mutations) {
console.log('size changed!');
});
var target = document.querySelector('.mydiv');
observer.observe(target, {
attributes: true,
childList: true,
subtree: true
});
HTML:
<div class='mydiv'>
</div>
Here's the fiddle.. Try to change the div size.
You can further wrap your method in the debounce method to improve efficiency. debounce will trigger your method every x milliseconds instead of triggering every millisecond the DIV is being resized.
ResizeSensor.js is part of a huge library, but I reduced its functionality to THIS:
function ResizeSensor(element, callback)
{
let zIndex = parseInt(getComputedStyle(element));
if(isNaN(zIndex)) { zIndex = 0; };
zIndex--;
let expand = document.createElement('div');
expand.style.position = "absolute";
expand.style.left = "0px";
expand.style.top = "0px";
expand.style.right = "0px";
expand.style.bottom = "0px";
expand.style.overflow = "hidden";
expand.style.zIndex = zIndex;
expand.style.visibility = "hidden";
let expandChild = document.createElement('div');
expandChild.style.position = "absolute";
expandChild.style.left = "0px";
expandChild.style.top = "0px";
expandChild.style.width = "10000000px";
expandChild.style.height = "10000000px";
expand.appendChild(expandChild);
let shrink = document.createElement('div');
shrink.style.position = "absolute";
shrink.style.left = "0px";
shrink.style.top = "0px";
shrink.style.right = "0px";
shrink.style.bottom = "0px";
shrink.style.overflow = "hidden";
shrink.style.zIndex = zIndex;
shrink.style.visibility = "hidden";
let shrinkChild = document.createElement('div');
shrinkChild.style.position = "absolute";
shrinkChild.style.left = "0px";
shrinkChild.style.top = "0px";
shrinkChild.style.width = "200%";
shrinkChild.style.height = "200%";
shrink.appendChild(shrinkChild);
element.appendChild(expand);
element.appendChild(shrink);
function setScroll()
{
expand.scrollLeft = 10000000;
expand.scrollTop = 10000000;
shrink.scrollLeft = 10000000;
shrink.scrollTop = 10000000;
};
setScroll();
let size = element.getBoundingClientRect();
let currentWidth = size.width;
let currentHeight = size.height;
let onScroll = function()
{
let size = element.getBoundingClientRect();
let newWidth = size.width;
let newHeight = size.height;
if(newWidth != currentWidth || newHeight != currentHeight)
{
currentWidth = newWidth;
currentHeight = newHeight;
callback();
}
setScroll();
};
expand.addEventListener('scroll', onScroll);
shrink.addEventListener('scroll', onScroll);
};
How to use it:
let container = document.querySelector(".container");
new ResizeSensor(container, function()
{
console.log("dimension changed:", container.clientWidth, container.clientHeight);
});
You have to bind the resize event on the window object, not on a generic html element.
You could then use this:
$(window).resize(function() {
...
});
and within the callback function you can check the new width of your div calling
$('.a-selector').width();
So, the answer to your question is no, you can't bind the resize event to a div.
The best solution would be to use the so-called Element Queries. However, they are not standard, no specification exists - and the only option is to use one of the polyfills/libraries available, if you want to go this way.
The idea behind element queries is to allow a certain container on the page to respond to the space that's provided to it. This will allow to write a component once and then drop it anywhere on the page, while it will adjust its contents to its current size. No matter what the Window size is. This is the first difference that we see between element queries and media queries. Everyone hopes that at some point a specification will be created that will standardize element queries (or something that achieves the same goal) and make them native, clean, simple and robust. Most people agree that Media queries are quite limited and don't help for modular design and true responsiveness.
There are a few polyfills/libraries that solve the problem in different ways (could be called workarounds instead of solutions though):
CSS Element Queries - https://github.com/marcj/css-element-queries
BoomQueries - https://github.com/BoomTownROI/boomqueries
eq.js - https://github.com/Snugug/eq.js
ElementQuery - https://github.com/tysonmatanich/elementQuery
And a few more, which I'm not going to list here, but you're free to search. I would not be able to say which of the currently available options is the best. You'll have to try a few and decide.
I have seen other solutions to similar problems proposed. Usually they use timers or the Window/viewport size under the hood, which is not a real solution. Furthermore, I think ideally this should be solved mainly in CSS, and not in javascript or html.
I found this library to work when MarcJ's solution didn't:
https://github.com/sdecima/javascript-detect-element-resize
It's very lightweight and detects even natural resizes via CSS or simply the HTML loading/rendering.
Code sample (taken from the link):
<script type="text/javascript" src="detect-element-resize.js"></script>
<script type="text/javascript">
var resizeElement = document.getElementById('resizeElement'),
resizeCallback = function() {
/* do something */
};
addResizeListener(resizeElement, resizeCallback);
removeResizeListener(resizeElement, resizeCallback);
</script>
Take a look at this http://benalman.com/code/projects/jquery-resize/examples/resize/
It has various examples. Try resizing your window and see how elements inside container elements adjusted.
Example with js fiddle to explain how to get it work.
Take a look at this fiddle http://jsfiddle.net/sgsqJ/4/
In that resize() event is bound to an elements having class "test" and also to the window object
and in resize callback of window object $('.test').resize() is called.
e.g.
$('#test_div').bind('resize', function(){
console.log('resized');
});
$(window).resize(function(){
$('#test_div').resize();
});
Only the window object generates a "resize" event. The only way I know of to do what you want to do is to run an interval timer that periodically checks the size.
You can use iframe or object using contentWindow or contentDocument on resize. Without setInterval or setTimeout
The steps:
Set your element position to relative
Add inside an transparent absolute hidden IFRAME
Listen to IFRAME.contentWindow - onresize event
An example of HTML:
<div style="height:50px;background-color:red;position:relative;border:1px solid red">
<iframe style=width:100%;height:100%;position:absolute;border:none;background-color:transparent allowtransparency=true>
</iframe>
This is my div
</div>
The Javascript:
$('div').width(100).height(100);
$('div').animate({width:200},2000);
$('object').attr({
type : 'text/html'
})
$('object').on('resize,onresize,load,onload',function(){
console.log('ooooooooonload')
})
$($('iframe')[0].contentWindow).on('resize',function(){
console.log('div changed')
})
Running Example
JsFiddle: https://jsfiddle.net/qq8p470d/
See more:
Clay - It's based on element-resize-event
element-resize-event
var div = document.getElementById('div');
div.addEventListener('resize', (event) => console.log(event.detail));
function checkResize (mutations) {
var el = mutations[0].target;
var w = el.clientWidth;
var h = el.clientHeight;
var isChange = mutations
.map((m) => m.oldValue + '')
.some((prev) => prev.indexOf('width: ' + w + 'px') == -1 || prev.indexOf('height: ' + h + 'px') == -1);
if (!isChange)
return;
var event = new CustomEvent('resize', {detail: {width: w, height: h}});
el.dispatchEvent(event);
}
var observer = new MutationObserver(checkResize);
observer.observe(div, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
#div {width: 100px; border: 1px solid #bbb; resize: both; overflow: hidden;}
<div id = "div">DIV</div>
Amazingly as old as this issue is, this is still a problem in most browsers.
As others have said, Chrome 64+ now ships with Resize Observes natively, however, the spec is still being fine tuned and Chrome is now currently (as of 2019-01-29) behind the latest edition of the specification.
I've seen a couple of good ResizeObserver polyfills out in the wild, however, some do not follow the specification that closely and others have some calculation issues.
I was in desperate need of this behaviour to create some responsive web components that could be used in any application. To make them work nicely they need to know their dimensions at all times, so ResizeObservers sounded ideal and I decided to create a polyfill that followed the spec as closely as possible.
Repo:
https://github.com/juggle/resize-observer
Demo:
https://codesandbox.io/s/myqzvpmmy9
Using Clay.js (https://github.com/zzarcon/clay) it's quite simple to detect changes on element size:
var el = new Clay('.element');
el.on('resize', function(size) {
console.log(size.height, size.width);
});
Here is a simplified version of the solution by #nkron, applicable to a single element (instead of an array of elements in #nkron's answer, complexity I did not need).
function onResizeElem(element, callback) {
// Save the element we are watching
onResizeElem.watchedElementData = {
element: element,
offsetWidth: element.offsetWidth,
offsetHeight: element.offsetHeight,
callback: callback
};
onResizeElem.checkForChanges = function() {
const data = onResizeElem.watchedElementData;
if (data.element.offsetWidth !== data.offsetWidth || data.element.offsetHeight !== data.offsetHeight) {
data.offsetWidth = data.element.offsetWidth;
data.offsetHeight = data.element.offsetHeight;
data.callback();
}
};
// Listen to the window resize event
window.addEventListener('resize', onResizeElem.checkForChanges);
// Listen to the element being checked for width and height changes
onResizeElem.observer = new MutationObserver(onResizeElem.checkForChanges);
onResizeElem.observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}
The event listener and observer can be removed by:
window.removeEventListener('resize', onResizeElem.checkForChanges);
onResizeElem.observer.disconnect();
This blog post helped me efficiently detect size changes to DOM elements.
http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/
How to use this code...
AppConfig.addResizeListener(document.getElementById('id'), function () {
//Your code to execute on resize.
});
Packaged code used by the example...
var AppConfig = AppConfig || {};
AppConfig.ResizeListener = (function () {
var attachEvent = document.attachEvent;
var isIE = navigator.userAgent.match(/Trident/);
var requestFrame = (function () {
var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
function (fn) { return window.setTimeout(fn, 20); };
return function (fn) { return raf(fn); };
})();
var cancelFrame = (function () {
var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
window.clearTimeout;
return function (id) { return cancel(id); };
})();
function resizeListener(e) {
var win = e.target || e.srcElement;
if (win.__resizeRAF__) cancelFrame(win.__resizeRAF__);
win.__resizeRAF__ = requestFrame(function () {
var trigger = win.__resizeTrigger__;
trigger.__resizeListeners__.forEach(function (fn) {
fn.call(trigger, e);
});
});
}
function objectLoad(e) {
this.contentDocument.defaultView.__resizeTrigger__ = this.__resizeElement__;
this.contentDocument.defaultView.addEventListener('resize', resizeListener);
}
AppConfig.addResizeListener = function (element, fn) {
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
if (attachEvent) {
element.__resizeTrigger__ = element;
element.attachEvent('onresize', resizeListener);
} else {
if (getComputedStyle(element).position === 'static') element.style.position = 'relative';
var obj = element.__resizeTrigger__ = document.createElement('object');
obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
obj.__resizeElement__ = element;
obj.onload = objectLoad;
obj.type = 'text/html';
if (isIE) element.appendChild(obj);
obj.data = 'about:blank';
if (!isIE) element.appendChild(obj);
}
}
element.__resizeListeners__.push(fn);
};
AppConfig.removeResizeListener = function (element, fn) {
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
if (!element.__resizeListeners__.length) {
if (attachEvent) element.detachEvent('onresize', resizeListener);
else {
element.__resizeTrigger__.contentDocument.defaultView.removeEventListener('resize', resizeListener);
element.__resizeTrigger__ = !element.removeChild(element.__resizeTrigger__);
}
}
}
})();
Note: AppConfig is a namespace/object I use for organizing reusable functions. Feel free to search and replace the name with anything you would like.
My jQuery plugin enables the "resize" event on all elements not just the window.
https://github.com/dustinpoissant/ResizeTriggering
$("#myElement") .resizeTriggering().on("resize", function(e){
// Code to handle resize
});
You can try the code in the following snippet, it covers your needs using plain javascript. (run the code snippet and click full page link to trigger the alert that the div is resized if you want to test it.).
Based on the fact that this is a setInterval of 100 milliseconds, i would dare to say that my PC did not find it too much CPU hungry. (0.1% of CPU was used as total for all opened tabs in Chrome at the time tested.). But then again this is for just one div, if you would like to do this for a large amount of elements then yes it could be very CPU hungry.
You could always use a click event to stop the div-resize sniffing anyway.
var width = 0;
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
}
if(document.getElementById("test_div").clientWidth!==width) {
alert('resized div');
width = document.getElementById("test_div").clientWidth;
}
}, 100);
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" />
<input type="button" value="button 2" />
<input type="button" value="button 3" />
</div>
You can check the fiddle also
UPDATE
var width = 0;
function myInterval() {
var interval = setInterval(function(){
if(width <= 0){
width = document.getElementById("test_div").clientWidth;
}
if(document.getElementById("test_div").clientWidth!==width) {
alert('resized');
width = document.getElementById("test_div").clientWidth;
}
}, 100);
return interval;
}
var interval = myInterval();
document.getElementById("clickMe").addEventListener( "click" , function() {
if(typeof interval!=="undefined") {
clearInterval(interval);
alert("stopped div-resize sniffing");
}
});
document.getElementById("clickMeToo").addEventListener( "click" , function() {
myInterval();
alert("started div-resize sniffing");
});
<div id="test_div" style="width: 100%; min-height: 30px; border: 1px dashed pink;">
<input type="button" value="button 1" id="clickMe" />
<input type="button" value="button 2" id="clickMeToo" />
<input type="button" value="button 3" />
</div>
Updated Fiddle
This is pretty much an exact copy of the top answer, but instead of a link, it's just the part of the code that matters, translated to be IMO more readable and easier to understand. A few other small changes include using cloneNode(), and not putting html into a js string. Small stuff, but you can copy and paste this as is and it will work.
The way it works is by making two invisible divs fill the element you're watching, and then putting a trigger in each, and setting a scroll position that will lead to triggering a scroll change if the size changes.
All real credit goes to Marc J, but if you're just looking for the relevant code, here it is:
window.El = {}
El.resizeSensorNode = undefined;
El.initResizeNode = function() {
var fillParent = "display: block; position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;";
var triggerStyle = "position: absolute; left: 0; top: 0; transition: 0s;";
var resizeSensor = El.resizeSensorNode = document.createElement("resizeSensor");
resizeSensor.style = fillParent;
var expandSensor = document.createElement("div");
expandSensor.style = fillParent;
resizeSensor.appendChild(expandSensor);
var trigger = document.createElement("div");
trigger.style = triggerStyle;
expandSensor.appendChild(trigger);
var shrinkSensor = expandSensor.cloneNode(true);
shrinkSensor.firstChild.style = triggerStyle + " width: 200%; height: 200%";
resizeSensor.appendChild(shrinkSensor);
}
El.onSizeChange = function(domNode, fn) {
if (!domNode) return;
if (domNode.resizeListeners) {
domNode.resizeListeners.push(fn);
return;
}
domNode.resizeListeners = [];
domNode.resizeListeners.push(fn);
if(El.resizeSensorNode == undefined)
El.initResizeNode();
domNode.resizeSensor = El.resizeSensorNode.cloneNode(true);
domNode.appendChild(domNode.resizeSensor);
var expand = domNode.resizeSensor.firstChild;
var expandTrigger = expand.firstChild;
var shrink = domNode.resizeSensor.childNodes[1];
var reset = function() {
expandTrigger.style.width = '100000px';
expandTrigger.style.height = '100000px';
expand.scrollLeft = 100000;
expand.scrollTop = 100000;
shrink.scrollLeft = 100000;
shrink.scrollTop = 100000;
};
reset();
var hasChanged, frameRequest, newWidth, newHeight;
var lastWidth = domNode.offsetWidth;
var lastHeight = domNode.offsetHeight;
var onResized = function() {
frameRequest = undefined;
if (!hasChanged) return;
lastWidth = newWidth;
lastHeight = newHeight;
var listeners = domNode.resizeListeners;
for(var i = 0; listeners && i < listeners.length; i++)
listeners[i]();
};
var onScroll = function() {
newWidth = domNode.offsetWidth;
newHeight = domNode.offsetHeight;
hasChanged = newWidth != lastWidth || newHeight != lastHeight;
if (hasChanged && !frameRequest) {
frameRequest = requestAnimationFrame(onResized);
}
reset();
};
expand.addEventListener("scroll", onScroll);
shrink.addEventListener("scroll", onScroll);
}
Pure Javascript solution, but works only if the element is resized with the css resize button:
store element size with offsetWidth and offsetHeight;
add an onclick event listener on this element;
when triggered, compare curent offsetWidth and offsetHeight with stored values, and if different, do what you want and update these values.
jQuery(document).ready( function($) {
function resizeMapDIVs() {
// check the parent value...
var size = $('#map').parent().width();
if( $size < 640 ) {
// ...and decrease...
} else {
// ..or increase as necessary
}
}
resizeMapDIVs();
$(window).resize(resizeMapDIVs);
});
using Bharat Patil answer simply return false inside the your bind callback to prevent maximum stack error see example below:
$('#test_div').bind('resize', function(){
console.log('resized');
return false;
});
This is a really old question, but I figured I'd post my solution to this.
I tried to use ResizeSensor since everyone seemed to have a pretty big crush on it. After implementing though, I realized that under the hood the Element Query requires the element in question to have position relative or absolute applied to it, which didn't work for my situation.
I ended up handling this with an Rxjs interval instead of a straight setTimeout or requestAnimationFrame like previous implementations.
What's nice about the observable flavor of an interval is that you get to modify the stream however any other observable can be handled. For me, a basic implementation was enough, but you could go crazy and do all sorts of merges, etc.
In the below example, I'm tracking the inner (green) div's width changes. It has a width set to 50%, but a max-width of 200px. Dragging the slider affects the wrapper (gray) div's width. You can see that the observable only fires when the inner div's width changes, which only happens if the outer div's width is smaller than 400px.
const { interval } = rxjs;
const { distinctUntilChanged, map, filter } = rxjs.operators;
const wrapper = document.getElementById('my-wrapper');
const input = document.getElementById('width-input');
function subscribeToResize() {
const timer = interval(100);
const myDiv = document.getElementById('my-div');
const widthElement = document.getElementById('width');
const isMax = document.getElementById('is-max');
/*
NOTE: This is the important bit here
*/
timer
.pipe(
map(() => myDiv ? Math.round(myDiv.getBoundingClientRect().width) : 0),
distinctUntilChanged(),
// adding a takeUntil(), here as well would allow cleanup when the component is destroyed
)
.subscribe((width) => {
widthElement.innerHTML = width;
isMax.innerHTML = width === 200 ? 'Max width' : '50% width';
});
}
function defineRange() {
input.min = 200;
input.max = window.innerWidth;
input.step = 10;
input.value = input.max - 50;
}
function bindInputToWrapper() {
input.addEventListener('input', (event) => {
wrapper.style.width = `${event.target.value}px`;
});
}
defineRange();
subscribeToResize();
bindInputToWrapper();
.inner {
width: 50%;
max-width: 200px;
}
/* Aesthetic styles only */
.inner {
background: #16a085;
}
.wrapper {
background: #ecf0f1;
color: white;
margin-top: 24px;
}
.content {
padding: 12px;
}
body {
font-family: sans-serif;
font-weight: bold;
}
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
<h1>Resize Browser width</h1>
<label for="width-input">Adjust the width of the wrapper element</label>
<div>
<input type="range" id="width-input">
</div>
<div id="my-wrapper" class="wrapper">
<div id="my-div" class="inner">
<div class="content">
Width: <span id="width"></span>px
<div id="is-max"></div>
</div>
</div>
</div>
expanding on this answer by #gman, here's a function that allows multiple per element callbacks, exploding the width and height into a quasi event object. see embedded demo that works live here on stack overflow ( you may need to resize the main browser drastically for it to trigger)
function elementResizeWatcher(element, callback) {
var
resolve=function(element) {
return (typeof element==='string'
? document[
['.','#'].indexOf(element.charAt(0)) < 0 ? "getElementById" : "querySelector"
] (element)
: element);
},
observer,
watched = [],
checkForElementChanges = function (data) {
var w=data.el.offsetWidth,h=data.el.offsetHeight;
if (
data.offsetWidth !== w ||
data.offsetHeight !== h
) {
data.offsetWidth = w;
data.offsetHeight = h;
data.cb({
target : data.el,
width : w,
height : h
});
}
},
checkForChanges=function(){
watched.forEach(checkForElementChanges);
},
started=false,
self = {
start: function () {
if (!started) {
// Listen to the window resize event
window.addEventListener("resize", checkForChanges);
// Listen to the element being checked for width and height changes
observer = new MutationObserver(checkForChanges);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
started=true;
}
},
stop : function ( ) {
if (started) {
window.removeEventListener('resize', checkForChanges);
observer.disconnect();
started = false;
}
},
addListener : function (element,callback) {
if (typeof callback!=='function')
return;
var el = resolve(element);
if (typeof el==='object') {
watched.push({
el : el,
offsetWidth : el.offsetWidth,
offsetHeight : el.offsetHeight,
cb : callback
});
}
},
removeListener : function (element,callback) {
var
el = resolve(element);
watched = watched.filter(function(data){
return !((data.el===el) && (data.cb===callback));
});
}
};
self.addListener(element,callback);
self.start();
return self;
}
var watcher = elementResizeWatcher("#resize_me_on_stack_overflow", function(e){
e.target.innerHTML="i am "+e.width+"px x "+e.height+"px";
});
watcher.addListener(".resize_metoo",function(e) {
e.target.innerHTML="and i am "+e.width+"px x "+e.height+"px";
});
var mainsize_info = document.getElementById("mainsize");
watcher.addListener(document.body,function(e) {
mainsize_info.innerHTML=e.width+"px x "+e.height+"px";
});
#resize_me_on_stack_overflow{
background-color:lime;
}
.resize_metoo {
background-color:yellow;
font-size:36pt;
width:50%;
}
<p> resize the main browser window! <span id="mainsize"><span> </p>
<p id="resize_me_on_stack_overflow">
hey, resize me.
</p>
<p class="resize_metoo">
resize me too.
</p>
Pure vanilla implementation.
var move = function(e) {
if ((e.w && e.w !== e.offsetWidth) || (e.h && e.h !== e.offsetHeight)) {
new Function(e.getAttribute('onresize')).call(e);
}
e.w = e.offsetWidth;
e.h = e.offsetHeight;
}
var resize = function(e) {
e.innerText = 'New dimensions: ' + e.w + ',' + e.h;
}
.resizable {
resize: both;
overflow: auto;
width: 200px;
border: 1px solid black;
padding: 20px;
}
<div class='resizable' onresize="resize(this)" onmousemove="move(this)">
Pure vanilla implementation
</div>
With disconnect to remove the event listener:
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "context", "output"]
connect() {
this.inputObserver = new ResizeObserver(() => { this.resizeInput() })
this.inputObserver.observe(this.inputTarget)
}
disconnect() {
this.inputObserver.disconnect(this.inputTarget)
}
resizeInput() {
const height = this.inputTarget.offsetHeight
this.contextTarget.style.height = `${height}px`
this.outputTarget.style.height = `${height}px`
}
}
Only Window.onResize exists in the specification, but you can always utilize IFrame to generate new Window object inside your DIV.
Please check this answer. There is a new little jquery plugin, that is portable and easy to use. You can always check the source code to see how it's done.
<!-- (1) include plugin script in a page -->
<script src="/src/jquery-element-onresize.js"></script>
// (2) use the detectResizing plugin to monitor changes to the element's size:
$monitoredElement.detectResizing({ onResize: monitoredElement_onResize });
// (3) write a function to react on changes:
function monitoredElement_onResize() {
// logic here...
}
i thought it couldn't be done but then i thought about it, you can manually resize a div via style="resize: both;" in order to do that you ave to click on it so added an onclick function to check element's height and width and it worked. With only 5 lines of pure javascript (sure it could be even shorter)
http://codepen.io/anon/pen/eNyyVN
<div id="box" style="
height:200px;
width:640px;
background-color:#FF0066;
resize: both;
overflow: auto;"
onclick="myFunction()">
<p id="sizeTXT" style="
font-size: 50px;">
WxH
</p>
</div>
<p>This my example demonstrates how to run a resize check on click for resizable div.</p>
<p>Try to resize the box.</p>
<script>
function myFunction() {
var boxheight = document.getElementById('box').offsetHeight;
var boxhwidth = document.getElementById('box').offsetWidth;
var txt = boxhwidth +"x"+boxheight;
document.getElementById("sizeTXT").innerHTML = txt;
}
</script>

Fade in boxes 1 after the other with jQuery

Im trying to make a 'blanket' of divs containing child divs 150px high and 150px wide.
I want each child div to fade in 1 after the other after after a millisecond or so, opacity changing from 0, to 1.
I cant seem to figure out how this works, or how id do it though?
http://jsfiddle.net/CCawh/
JS
$(function(){
var figure = [];
w = 1500;
h = 450;
for(i = 0, i < 30, i++){
$('div').append(figure[].clone()).fadeIn();
}
});
Here is a working solution.
The problems in your code
in for(i = 0, i < 30, i++), you should use ';', not ',' . Use developer tools in your browser to catch such typos
In your code $('div').append(figure[].clone()).fadeIn(); , The fadeIn applies to $('div') as append() returns the calling object itself. You must replace it with $('<figure></figure>').appendTo('div').fadeIn('slow'); and to fadeIn items one by one you could set a timeout with incrementing delays
Add display: none; style to the figure to keep it hidden initially
Here is the full code.
$(function(){
for(i = 0; i < 30; i++){
setTimeout(function(){$('<figure></figure>').appendTo('div').fadeIn('slow');}, i*200);
}
});
Here is a fiddle to see it working http://jsfiddle.net/CCawh/12/
Try using greensock TweenLite http://www.greensock.com/get-started-js/.
It has staggerTo/staggerFrom action that does exactly what you are asking. TweenLite in conjunction with jQuery makes animation very easy.
This would be a possible solution (DEMO).
Use an immediate function and call it again n times in the fadeIn callback.
$(function(){
var figure = $('figure');
var counter = 0;
(function nextFade() {
counter++;
figure.clone().appendTo('div').hide().fadeIn(500, function() {
if(counter < 30) nextFade();
});
})();
});
You can use the following implementation as an example. Using setTimeout() will do the trick.
I've updated your jsfiddle here: http://jsfiddle.net/CCawh/5/
HTML:
<div class="container">
<div class="box"></div>
</div>
CSS:
.box {
display: none;
float: left;
margin: 10px;
width: 150px;
height: 150px;
background-color: #000;
}
JS:
$(function() {
var box = $('.box');
var delay = 100;
for (i = 0; i < 30; i++) {
setTimeout(function() {
var new_box = box.clone();
$('.container').append(new_box);
new_box.fadeIn();
}, delay);
delay += 500; // Delay the next box by an extra 500ms
}
});
Note that in order for the element to actually fade in, it must be hidden in the first place, i.e. display: none; or .hide()
Here's perhaps a more robust solution without counters:
http://jsfiddle.net/CCawh/6/
for(var i = 0; i < 30; i++){
$('div').append($('<figure>figure</figure>'));
}
(function fade(figure, duration) {
if (figure)
figure.fadeIn(duration, function() { fade(figure.next(), duration); });
})($('figure').first(), 400);
By the way, clauses in for loops are separated using semicolons, not commas.

Categories