-= operator in jQuery's css method not working in IE8 - javascript

It looks like jQuery's handling of -= in css() isn't working in IE8 for some reason. The following code doesn't through any errors, but it doesn't work either. I've narrowed it down to the operator mentioned above.
var postCount = $(".postsWrapper .window .posts article").length;
var numberOfPages = Math.ceil(postCount / 3);
var currentPage = 1;
var transitioning = false;
$(".postsWrapper button").click(function() {
var direction = $(this).attr("data-direction");
var postWindowWidth = $(".postsWrapper .window").width();
if (direction == "next" && currentPage < numberOfPages && transitioning == false) {
transitioning = true;
// the below line does nothing in IE8
$(".postsWrapper .window .posts").css("marginLeft", "-=" + postWindowWidth);
setTimeout(function() {
transitioning = false;
}, 1000);
currentPage++;
} else if (direction == "previous" && currentPage > 1 && transitioning == false) {
transitioning = true;
// the below line does nothing in IE8
$(".postsWrapper .window .posts").css("marginLeft", "+=" + postWindowWidth);
setTimeout(function() {
transitioning = false;
}, 1000);
currentPage--;
}
});
See http://www.weblinxinc.com/beta/candy-controls/demo/site/index.htm

The problem is that your margin-left property is starting out with the value auto, and so jQuery can't increment/decrement it. Live Example (source below)
If you initially set it to a numeric value, it'll start working. Live example
This might qualify as a jQuery bug, you might check their list to see if it's there.
Here's the source to the live examples:
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<meta charset="utf-8">
<title>Example</title>
</head>
<body>
<div id="test">I'm the test div</div>
<script>
(function() {
"use strict";
// The following line is only in the second example, the one that works;
// without it, it doesn't work (in IE8) even though it does in Chrome
$("#test").css("marginLeft", 0);
var counter = 0;
var up = true;
display("Initial marginLeft: " + $("#test").css("marginLeft"));
setInterval(function() {
if (up) {
$("#test").css("marginLeft", "+=5");
} else {
$("#test").css("marginLeft", "-=5");
}
++counter;
if (counter > 10) {
counter =0;
up = !up;
}
}, 200);
function display(msg) {
$("<p>").html(String(msg)).appendTo(document.body);
}
})();
</script>
</body>
</html>

Related

Wrote two JavaScript functions but one of them seems to be crashing my website

So I wrote a simple program that should change the background of a given website after 3 seconds.
Now this is my JavaScript code:
//this function changes the backgrounds after 3 seconds and increments n
function changebackgroundTimed(startvariable)
{
var n = startvariable;
var loop = 1;
while (loop == 1)
{
setTimeout(function(){changebackground(n)}, 3000)
n++;
if (n == 5)
{
n=1;
}
}
}
//this function changes the background depending on the given number
function changebackground(number)
{
if (number == 1)
{
$("body").css("background","no-repeat center/120%url('../images/1.jpg')");
}
else if (number == 2)
{
$("body").css("background","no-repeat center/120%url('../images/2.jpg')");
}
else if (number == 3)
{
$("body").css("background","no-repeat center/120%url('../images/3.jpg')");
}
else if (number == 4)
{
$("body").css("background","no-repeat center/120%url('../images/4.jpg')");
}
else {
$("body").css("background","no-repeat center/120%url('../images/1.jpg')");
}
}
in the html I just call it with: changebackgroundTimed(2);
Problem is: When I start the page it just loads for a long while and then eventually crashes while showing nothing. It has to do something with these two functions. Does anybody of you notices a mistake I may be missing?
Looks like you are not updating your "loop" variable, which is causing it go in an infinite loop.
Instead of using the while loop, use setInterval() method. That should do the work for you.
Keep the variable n outside the function, and refer it using outsiders this keyword.
function abc(){
var self = this;
self.n = 1;
setInterval(function () {
if(self.n ===1){
self.n = self.n + 1;
// your code
}else if(){
// and so on
}
changebackground(self.n);
},3000);
}
my 2 cents...
CSS:
:root {
--bg-images :"../images/1.jpg|../images/2.jpg|../images/3.jpg|../images/4.jpg|../images/5.jpg";
--img-bg :url(../images/1.jpg);
}
body {
background-image: var(--img-bg);
background-position: center/120%;
background-repeat: no-repeat;
}
Javascript:
const Root = document.documentElement
, gRoot = getComputedStyle(Root)
, imgList = gRoot.getPropertyValue('--bg-images').match(/"(.*?)"/)[1].split('|')
, NbImgs = imgList.length
, regExp = /\(([^)]+)\)/ // to get img url between parentheses
;
let currImg = imgList.findIndex(img=>img=== (regExp.exec(gRoot.getPropertyValue('--img-bg'))[1]))
;
setInterval(() => {
currImg = ++currImg % NbImgs;
Root.style.setProperty('--img-bg', `url(${imgList[currImg]})`)
}, 3000)
It appears that you never exit your while loop which makes the page crash as it runs forever. At some part in your code you have to change the value of the loop variable.
You are creating an infinite loop and never breaking out of it so it will run as fast as possible and lock up the UI.
Why not use setInterval like so:
const backgroundChanger = setInterval(changeBackground, 3000)
let background = 1
function changeBackground() {
if (background >= 5) background = 1
// set background however you like
document.body.style.background = 'url(../images/' + background++ + '.jpg) center/120% no-repeat'
}
setTimeout is a non-blocking call. This means that your code wont wait for 3000ms and keep on running in an infinte loop while calling changebackground(n);.
To read more about setTimeout go here setTimeout from MDN
Use the following code:
function changebackgroundTimed(startvariable)
{
var n = startvariable;
setInterval(() => {
changebackground(n);
n = (n % 5) + 1;
}, 3000);
}
//this function changes the background depending on the given number
function changebackground(number)
{
console.log(number)
}
changebackgroundTimed(2)
var i = 1;
setInterval(() => {
if(i > 5){
i = 0;
}else{
changeBackground(i)
}
i++;
}, 250);
function changeBackground(i){
switch(i){
case 1 :
$("body").css("color", "red")
break;
case 2 :
$("body").css("color", "blue")
break;
case 3 :
$("body").css("color", "green")
break;
case 4 :
$("body").css("color", "black")
break;
case 5 :
$("body").css("color", "orange")
break;
default:
$("body").css("color", "white")
}
}
A variation on a theme perhaps but without the hardcoded limitation of the original functions
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>Background image cycling</title>
<style>
body{
width:100%;
height:100vh;
padding:0;
margin:0;
}
</style>
<script>
/* configuration */
const PAUSE=3;
const SLIDES=['//www.stevensegallery.com/800/600','//www.placecage.com/800/600','//placebear.com/640/360','//picsum.photos/800/600'];
(function( delay ){
document.addEventListener('DOMContentLoaded',()=>{
var images=[/* default images */
'//placekitten.com/900/800',
'//placekitten.com/1000/800',
'//placekitten.com/1024/768',
'//placekitten.com/1200/800',
'//placekitten.com/1366/768'
];
if( arguments[1] )images=images.concat( arguments[1] )
var i=0;
(function(){
setTimeout( arguments.callee, 1000 * delay );
i++;
if( i > images.length - 1 )i=0;
document.body.style.background='center / contain no-repeat url('+images[ i ]+')';
})();
});
})( PAUSE, SLIDES );
</script>
</head>
<body>
<!-- content -->
</body>
</html>

Creating a simple "smooth scroll" (with javascript vanilla)

I have been trying to make a simple "smoothscroll" function using location.href that triggers on the mousewheel. The main problem is that the EventListener(wheel..) gets a bunch of inputs over the span of ca. 0,9 seconds which keeps triggering the function. "I only want the function to run once".
In the code below I have tried to remove the eventlistener as soon as the function runs, which actually kinda work, the problem is that I want it to be added again, hence the timed function at the bottom. This also kinda work but I dont want to wait a full second to be able to scroll and if I set it to anything lover the function will run multiple times.
I've also tried doing it with conditions "the commented out true or false variables" which works perfectly aslong as you are only scrolling up and down but you cant scroll twice or down twice.
window.addEventListener('wheel', scrolltest, true);
function scrolltest(event) {
window.removeEventListener('wheel', scrolltest, true);
i = event.deltaY;
console.log(i);
if (webstate == 0) {
if (i < 0 && !upexecuted) {
// upexecuted = true;
location.href = "#forside";
// downexecuted = false;
} else if (i > 0 && !downexecuted) {
// downexecuted = true;
location.href = "#underside";
// upexecuted = false;
}
}
setTimeout(function(){ window.addEventListener('wheel', scrolltest, true); }, 1000);
}
I had hoped there was a way to stop the wheel from constantly produce inputs over atleast 0.9 seconds.
"note: don't know if it can help in some way but when the browser is not clicked (the active window) the wheel will registre only one value a nice 100 for down and -100 for up"
What you're trying to do is called "debouncing" or "throttling". (Those aren't exactly the same thing, but you can look up the difference in case it's going to matter to you.) Functions for this are built into libraries like lodash, but if using a library like that is too non-vanilla for what you have in mind, you can always define your own debounce function: https://www.geeksforgeeks.org/debouncing-in-javascript/
You might also want to look into requestanimationframe.
a different approach
okey after fiddeling with this for just about 2 days i got fustrated and started over. no matter what i did the browsers integrated "glide-scroll" was messing up the event trigger. anyway i decided to animate the scrolling myself and honestly it works better than i had imagined: here is my code if anyone want to do this:
var body = document.getElementsByTagName("BODY")[0];
var p1 = document.getElementById('page1');
var p2 = document.getElementById('page2');
var p3 = document.getElementById('page3');
var p4 = document.getElementById('page4');
var p5 = document.getElementById('page5');
var whatpage = 1;
var snap = 50;
var i = 0;
// this part is really just to read what "page" you are on if you update the site. if you add more pages you should remember to add it here too.
window.onload = setcurrentpage;
function setcurrentpage() {
if (window.pageYOffset == p1.offsetTop) {
whatpage = 1;
} else if (window.pageYOffset == p2.offsetTop) {
whatpage = 2;
} else if (window.pageYOffset == p3.offsetTop) {
whatpage = 3;
} else if (window.pageYOffset == p4.offsetTop) {
whatpage = 4;
} else if (window.pageYOffset == p5.offsetTop) {
whatpage = 5;
}
}
// this code is designet to automaticly work with any "id" you have aslong as you give it a variable called p"number" fx p10 as seen above.
function smoothscroll() {
var whatpagenext = whatpage+1;
var whatpageprev = whatpage-1;
var currentpage = window['p'+whatpage];
var nextpage = window['p'+whatpagenext];
var prevpage = window['p'+whatpageprev];
console.log(currentpage);
if (window.pageYOffset > currentpage.offsetTop + snap && window.pageYOffset < nextpage.offsetTop - snap){
body.style.overflowY = "hidden";
i++
window.scrollTo(0, window.pageYOffset+i);
if (window.pageYOffset <= nextpage.offsetTop + snap && window.pageYOffset >= nextpage.offsetTop - snap) {
i=0;
window.scrollTo(0, nextpage.offsetTop);
whatpage += 1;
body.style.overflowY = "initial";
}
} else if (window.pageYOffset < currentpage.offsetTop - snap && window.pageYOffset > prevpage.offsetTop + snap){
body.style.overflowY = "hidden";
i--
window.scrollTo(0, window.pageYOffset+i);
if (window.pageYOffset >= prevpage.offsetTop - snap && window.pageYOffset <= prevpage.offsetTop + snap) {
i=0;
window.scrollTo(0, prevpage.offsetTop);
whatpage -= 1;
body.style.overflowY = "initial";
}
}
}
to remove the scrollbar completely just add this to your stylesheet:
::-webkit-scrollbar {
width: 0px;
background: transparent;
}

how to make hide/show text javascript smoother?

I am using this script to hide and show text however, I want to make the transition smoother but I am not sure how to. Here's a demo of it: http://jsfiddle.net/LnE5U/.
Please help me change it to make it smoother.
hide/show text
<div id="showOrHideDiv" style="display: none">hidden text</div>
<script language="javascript">
function showOrHide()
{
var div = document.getElementById("showOrHideDiv");
if (div.style.display == "block")
{
div.style.display = "none";
}
else
{
div.style.display = "block";
}
}
</script>
Here is an example using jQuery's fadeToggle (a shortcut for a more complicated animate)
// assuming jQuery
$(function () { // on document ready
var div = $('#showOrHideDiv'); // cache <div>
$('#action').click(function () { // on click on the `<a>`
div.fadeToggle(1000); // toggle div visibility over 1 second
});
});
HTML
<a id="action" href="#">hide/show text</a>
<div id="showOrHideDiv" style="display: none;">hidden text</div>
DEMO
An example of a pure JavaScript fader. It looks complicated because I wrote it to support changing direction and duration mid-fade. I'm sure there are still improvements that could be made to it, though.
function generateFader(elem) {
var t = null, goal, current = 0, inProgress = 0;
if (!elem || elem.nodeType !== 1) throw new TypeError('Expecting input of Element');
function visible(e) {
var s = window.getComputedStyle(e);
return +!(s.display === 'none' || s.opacity === '0');
}
function fader(duration) {
var step, aStep, fn, thisID = ++current, vis = visible(elem);
window.clearTimeout(t);
if (inProgress) goal = 1 - goal; // reverse direction if there is one running
else goal = 1 - vis; // else decide direction
if (goal) { // make sure visibility settings correct if hidden
if (!vis) elem.style.opacity = '0';
elem.style.display = 'block';
}
step = goal - +window.getComputedStyle(elem).opacity;
step = 20 * step / duration; // calculate how much to change by every 20ms
if (step >= 0) { // prevent rounding issues
if (step < 0.0001) step = 0.0001;
} else if (step > -0.0001) step = -0.0001;
aStep = Math.abs(step); // cache
fn = function () {
// console.log(step, goal, thisID, current); // debug here
var o = +window.getComputedStyle(elem).opacity;
if (thisID !== current) return;
if (Math.abs(goal - o) < aStep) { // finished
elem.style.opacity = goal;
if (!goal) elem.style.display = 'none';
inProgress = 0;
return;
}
elem.style.opacity = (o + step).toFixed(5);
t = window.setTimeout(fn, 20);
}
inProgress = 1; // mark started
fn(); // start
}
return fader;
}
And using it
window.addEventListener( // this section matches the code above
'load',
function () {
var fader = generateFader(document.getElementById('showOrHideDiv'));
document.getElementById('action').addEventListener(
'click',
function () {
fader(1000);
}
);
}
);
DEMO of this
This is quite simple. I have just made a demo and i used setInterval
Here's how it works
var fadeout = function( element ) { // 1
element.style.opacity = 1; // 2
window.setInterval(function() { // 3
if(element.style.opacity > 0) { // 4
element.style.opacity = parseFloat(element.style.opacity - 0.01).toFixed(2); // 5
} else {
element.style.display = 'none'; // 6
}
}, 50);
};
JSFiddle Demo Link
Steps
Create a function that accepts a DOM element
Set the opacity of the element to 1
Create a function that loops every 50ms
If the opacity is greater than 0 -> continue
Take away 0.01 from the opacity
if it's less than 0 the animation is complete and hide it completely
Note this is a really simple example and will need a bit of work
You can use somthing like this
$('.showOrHideDiv').toggle(function() {
$('showOrHideDiv').fadeIn('slow', function() {
//fadeIn or fadeOut, slow or fast, all the stuffs you want to trigger, "a function to execute every odd time the element is clicked," says the [jquery doc][1]
});
}, function() {
//here comes "additional handlers to cycle through after clicks," says the [jquery doc][1]
});
I used OPACITY to make it show/hide. See this Example, Full code (without jQuery):
Click here
<div id="MyMesage" style="display:none; background-color:pink; margin:0 0 0 100px;width:200px;">
blablabla
</div>
<script>
function ShowDiv(name){
//duration of transition (1000 miliseconds equals 1 second)
var duration = 1000;
// how many times should it should be changed in delay duration
var AmountOfActions=100;
var diiv= document.getElementById(name);
diiv.style.opacity = '0'; diiv.style.display = 'block'; var counte=0;
setInterval(function(){counte ++;
if ( counte<AmountOfActions) { diiv.style.opacity = counte/AmountOfActions;}
},
duration / AmountOfActions);
}
</script>
I followed iConnor solution and works fine but it had a small issue setInterval will not stop after the element be hidden I added stop interval to make it better performance
var fadeout = function( element ) { // 1
element.style.opacity = 1; // 2
let hidden_process = window.setInterval(function() { // 3
if(element.style.opacity > 0) { // 4
element.style.opacity = parseFloat(element.style.opacity - 0.01).toFixed(2); // 5
} else {
element.style.display = 'none'; // 6
console.log('1');
clearInterval(hidden_process);
}
}, 50);
};

jQuery.appear to fire only when the entire element in is viewport

I'm currently using jQuery.appear to change the class of elements as they come into the viewport. The plugin works great, except that it fires right as the top of the element comes into view. I am wanting to adapt it so it only fires when the entire element is inside the viewport, or near to being.
CODE:
$('someselector').appear();
$('someselector').on('appear', function() {
$(this).removeClass('on').addClass('off');
$(this).siblings().removeClass('off').addClass('on');
});
jQuery Waypoints plugin could be useful also. It triggers an action, when the element became to be visible on the screen.
$('.entry').waypoint(function() {
alert('The element is appeared on the screen.');
});
There are some examples on the site of the plugin.
I have changed some of the code so you can check if an element is fully visible. Removed the code that will trigger an event since it's harder to clean up using destroy (not implemented yet). I will try and make it according to the documentation: http://docs.jquery.com/Plugins/Authoring
Here is the html page:
<!DOCTYPE html>
<html>
<head>
<script src="jquery.js"></script>
<script src="appear.js"></script>
<script>
$(document).ready(function(){
//TODO: not sure if there is such a thing as selector
// namespaces but could try that to add both appear and
// fully-appear to same selector elements
$('#fully').appear(function(){
console.log("full view");
},{fullView:true});
$('#partly').appear(function(){
console.log("partly visible");
});
$(window).scrollTop(1400).scrollLeft(1000);
});
</script>
</head>
<body >
<div style="width:3000px;height: 3000px"></div>
<div id="fully" style="width:50px;height:75px;
position: absolute;left:1500px;top:1500px;
background: black">
</div>
<div id="partly" style="width:50px;height:75px;
position: absolute;left:1450px;top:1350px;
background: yellow">
</div>
</body>
</html>
And the changed appear.js
/*
* jQuery appear plugin
*
* Copyright (c) 2012 Andrey Sidorov
* licensed under MIT license.
* Edit by HRM 2013 02 01
* https://github.com/morr/jquery.appear/
*
* Version: 0.2.1
*/
(function($) {
var selectors = [];
var $window = $(window);
var $document = $(document);
function process(p) {
p.checkLock = false;
var $appeared = p.elements.filter(function() {
return $(this).is(p.filterName);
});
if($appeared.length==0){
return;
}
p.callback($appeared);
}
// "appeared" custom filter
$.expr[':']['appeared'] = function(element) {
var $element = $(element);
if (!$element.is(':visible')) {
return false;
}
var window_left = $window.scrollLeft();
var window_top = $window.scrollTop();
var offset = $element.offset();
var left = offset.left;
var top = offset.top;
if (top + $element.height() >= window_top &&
top - ($element.data('appear-top-offset') || 0)
<= window_top + $window.height() &&
left + $element.width() >= window_left &&
left - ($element.data('appear-left-offset') || 0)
<= window_left + $window.width()) {
return true;
} else {
return false;
}
}
// "in-full-view" custom filter
$.expr[':']['fully-appeared'] = function(element) {
var $element = $(element);
if (!$element.is(':visible')) {
return false;
}
wLeft=$window.scrollLeft();
wTop=$window.scrollTop();
var offset = $element.offset();
var left = offset.left- ($element.data
('appear-left-offset') || 0);
var right = (offset.left+$element.width()) -
($element.data('appear-left-offset') || 0);
var top = offset.top - ($element.data
('appear-top-offset') || 0);
var bottom = offset.top+$element.height();
var window_left = wLeft;
var window_top = wTop;
var window_right = wLeft+ $window.width();
var window_bottom = wTop+$window.height();
if (window_bottom>=bottom&&
window_top<=top&&
window_left<=left&&
window_right>=right ) {
return true;
} else {
return false;
}
}
function compare(o1,o2){
//simple compare, assumes all properties of o1 and o2 are
// simple types make sure that o1 is not undefined
// comparing goes much further but requires writing another
// extension
if(typeof o2=="undefined"){
return false;
}
var i;
for(i in o1){
if(typeof o2[i]=="undefined"){
return false;
}
}
for(i in o1){
if(o1[i]!=o2[i]){
return false;
}
}
return true;
}
function checkExist(selector){
return !(typeof selectors[selector]=="undefined");
}
$.fn.extend({
// watching for element's appearance in browser viewport
appear: function(callback, options) {
if(typeof callback != "function"){
throw("Have to provide a callback: "
+"$('selector').appear(function()....");
}
var defaults = {
interval: 250
}
var index=this.selector;
if(index==""){
throw("Can't use an empty selector with this function.");
}
$.extend(defaults, options || {});
var exist=checkExist(index);
if(!exist){
selectors[index]=defaults;
}
var checkBind=compare(defaults,
selectors[index]);
selectors[index]=defaults;
var p={
checkLock:false,
filterName:(defaults.fullView)?":fully-appeared":":appeared",
callback:callback,
elements:this
}
if ((!checkBind)||(!exist)) {
$(window).off("scroll."+index,on_check)
.on("resize."+index,on_check);
var on_check = function() {
if (p.checkLock) {
return;
}
p.checkLock = true;
setTimeout(function(){
process(p);
}, defaults.interval);
};
$(window).on("scroll."+index,on_check)
.on("resize."+index,on_check);
}
if (options && options.force_process) {
setTimeout(process, defaults.interval);
}
return $(this.selector);
}
});
$.extend({
// force elements's appearance check
force_appear: function() {
if (check_binded) {
process();
return true;
};
return false;
}
});
})(jQuery);

Javascript "For" Loop not working?

I have a Javascript file that I am using to try to animate a dropdown menu. I have the "Toggle" function in that file set to run when I click on a certain div. Here's the script I'm using:
var up = true;
function Toggle(x)
{
if (up)
{
for (var i = x.offsetTop; i <= 0; i++)
{
x.style.top = i;
if (i == 0)
{
up = false;
}
}
}
else if (up == false)
{
for (var i = x.offsetTop; i >= -50; i--)
{
x.style.top = i;
if (i == -50)
{
up = true;
}
}
}
}
In the HTML div I want to animate, I have the "onclick" property set to "onclick=Toggle(this)". The first for loop works as it should (it sets the div's top offset to 0). However, the second for loop doesn't set the offsetTop. I know that the for loop is activating because I've tested it and it gives me every integer between 0 and -50. Why isn't it setting the offset position?
1) You must specify a unit to the top ie: x.style.top = i +"px"
2) Your function won't animate instead of you use a setInterval or a setTimeout
Like you asked, an example. I wouldn't do it like this for one of my project, but i kept your function to make you more familiar with the code.
I Used setTimeout instead of setInterval because setInterval must be cleared when not needed and setTimeout is just launched one time :
var Toggle = (function() { // use scope to define up/down
var up = true;
return function(element) {
var top = parseInt(element.style.top, 10); // element.offsetTop ?
if ( !top ) {
top = 0;
}
if (up) {
if (element.offsetTop < 0) { // if we are not at 0 and want to get up
element.style.top = (top+1) + "px";
setTimeout(function() { Toggle(element); }, 10); // recall the function in 10 ms
} else { // we change up value
up = !up;
}
}
else {
if (element.offsetTop > -50) {
element.style.top = (top-1) + "px";
setTimeout(function() { Toggle(element); }, 10); // recall the function in 10 ms
} else {
up=!up;
}
}
}
})();
You'd have to use x.style.top = i + 'px' as top and similar css properties must define the type (px, em, %, etc.) unless they are 0, as this is 0 in any case.
But your script would actually snap the div directly to -50px, as you do not wait between those iteration steps.
I'd recommend to use a library like jQuery to use it's animate() method.
function Toggle(obj) {
$(obj).animate({
top: parseInt($(obj).css('top')) === 0 ? '-50px' : '0px'
})
}

Categories

Resources