JavaScript Safari issue with setting data attribute - javascript

In order to deal with responsive images, I'm displaying images using the correct data attribute according to the current window width. So e.g. if my window width is larger than my settings.desktop var value, whatever is currently in the data-lrg attribute is displayed as an image.
The following JavaScript code works in all recent browsers, except in Safari where the data attribute of the images can't be set, or so it seems.
My JS-fu isn't exactly up to par, which means I'm not 100% sure what I'm doing. Does anybody know of any Safari quirks related to settings data attributes?
Markup:
<img class="data-img" data-xsml="width_460_height_345.jpg" data-sml="width_670_height_503.jpg" data-med="width_437_height_328.jpg" data-lrg="width_288_height_216.jpg" alt="alt tag" src="width_288_height_216.jpg">
My JavaScript:
$.fn.dataImg = function(options) {
var settings = $.extend({
mobile: 506,
tablet: 725,
desktop: 945
}, options );
var elements = $(this);
function getSrc(element) {
var screen = $(window).width();
if (screen >= settings.desktop) {
return element.data('lrg');
}
else if (screen < settings.desktop && screen >= settings.tablet) {
return element.data('med');
}
else if (screen < settings.tablet && screen >= settings.mobile) {
return element.data('sml');
}
else {
return element.data('xsml');
}
}
function breakpoints() {
elements.each(function () {
var e = $(this);
var src = getSrc(e);
if(src != undefined){
if (e.is('img')) {
e.attr('src', src);
} else {
e.css('background-image', 'url(' + src + ')');
}
}
});
}breakpoints();
if(settings.resize == true){
$(window).resize(function(){
breakpoints();
});
}
};

This turned out to be a CSS or markup error which was most likely caused by incorrect usage of the framework used. Somehow Safari handled things differently than other browsers. the My bad!

Related

Javascript Load More when Scroll To Div

When I click I on load more link it load more results I need to change it from when I click to link when I Scroll To AJAXloaded Div
My Html Code
<div id="AJAXloaded">
<div class="loadmore">
<ul class="pagination">
<li class="active">
<a id="load-more">مشاهدة المزيد ...
<img id="lodingGif" src="{{Request::root()}}/public/upload/logo/loading.gif" height="25" width="25" style="display: none;">
</a>
</li>
</ul>
</div>
</div>
My JS Code
$(document).on("click","#load-more",function() {
page=page+1;
loadMoreData(page);
});
This is a perfect use case for the IntersectionObserver API
From the docs:
The Intersection Observer API provides a way to asynchronously observe
changes in the intersection of a target element with an ancestor
element or with a top-level document's viewport. [...] Intersection
information is needed for many reasons, such as:
Implementing "infinite scrolling" web sites, where more and more content is loaded and rendered as you scroll, so that the user doesn't
have to flip through pages.
var options = {
root: document.querySelector('#AJAXloaded'),
rootMargin: '0px',
threshold: 0,
};
var observer = new IntersectionObserver(callback, options);
function callback() {
// element scrolled into viewport
}
Browser support is quite good (except for IE). If you need to support older browsers there are a couple of polyfills available. But since they use polling and don't work in every situation I would personally just leave the button for those browsers and remove it for browsers supporting IntersectionObserver.
var supportsIntersectionObserver = 'IntersectionObserver' in window;
if(supportsIntersectionObserver) { button.remove(); }
How to add infinite scrolling to Blogger blogs?
If you don’t care about the details and you only want to enable the feature on your blog, click on the button below and add the code to your blog. Infinite scrolling should just work on your blog, in most cases. If you have a custom template, though, you may need to tweak the code a little. (Scroll down to “Frequently asked question” section for details.
If clicking on this button does nothing, or it doesn’t work for some reason, you can add this code manually:
Add a HTML/JavaScript gadget to your blog.
Leave the gadget’s title empty.
Paste the following code as the gadget’s content:
(function($) {
var loadingGif = 'https://lh3.googleusercontent.com/-FiCzyOK4Mew/T4aAj2uVJKI/AAAAAAAAPaY/x23tjGIH7ls/s32/ajax-loader.gif';
var olderPostsLink = '';
var loadMoreDiv = null;
var postContainerSelector = 'div.blog-posts';
var loading = false;
var win = $(window);
var doc = $(document);
// Took from jQuery to avoid permission denied error in IE.
var rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
function loadDisqusScript(domain) {
$.getScript('http://' + domain + '.disqus.com/blogger_index.js');
}
function loadMore() {
if (loading) {
return;
}
loading = true;
if (!olderPostsLink) {
loadMoreDiv.hide();
return;
}
loadMoreDiv.find('a').hide();
loadMoreDiv.find('img').show();
$.ajax(olderPostsLink, {
'dataType': 'html'
}).done(function(html) {
var newDom = $('<div></div>').append(html.replace(rscript, ''));
var newLink = newDom.find('a.blog-pager-older-link');
var newPosts = newDom.find(postContainerSelector).children();
$(postContainerSelector).append(newPosts);
// Loaded more posts successfully. Register this pageview with
// Google Analytics.
if (window._gaq) {
window._gaq.push(['_trackPageview', olderPostsLink]);
}
// Render +1 buttons.
if (window.gapi && window.gapi.plusone && window.gapi.plusone.go) {
window.gapi.plusone.go();
}
// Render Disqus comments.
if (window.disqus_shortname) {
loadDisqusScript(window.disqus_shortname);
}
// Render Facebook buttons.
if (window.FB && window.FB.XFBML && window.FB.XFBML.parse) {
window.FB.XFBML.parse();
}
// Render Twitter widgets.
if (window.twttr && window.twttr.widgets && window.twttr.widgets.load) {
window.twttr.widgets.load();
}
if (newLink) {
olderPostsLink = newLink.attr('href');
} else {
olderPostsLink = '';
loadMoreDiv.hide();
}
loadMoreDiv.find('img').hide();
loadMoreDiv.find('a').show();
loading = false;
});
}
function getDocumentHeight() {
return Math.max(
win.height(),
doc.height(),
document.documentElement.clientHeight);
}
function handleScroll() {
var height = getDocumentHeight();
var pos = win.scrollTop() + win.height();
if (height - pos < 150) {
loadMore();
}
}
function init() {
if (_WidgetManager._GetAllData().blog.pageType == 'item') {
return;
}
olderPostsLink = $('a.blog-pager-older-link').attr('href');
if (!olderPostsLink) {
return;
}
var link = $('Load more posts');
link.click(loadMore);
var img = $('<img src="' + loadingGif + '" style="display: none;">');
win.scroll(handleScroll);
loadMoreDiv = $('<div style="text-align: center; font-size: 150%;"></div>');
loadMoreDiv.append(link);
loadMoreDiv.append(img);
loadMoreDiv.insertBefore($('#blog-pager'));
$('#blog-pager').hide();
}
$(document).ready(init);
})(jQuery);

Scroll bottom in JavaScript

I have a working bottom function in JavaScript to detect if the user scrolls at the bottom. However, a problem comes when the user has a strange resolution (like windows scale) or when you zoom. The function is not working anymore and can't detect the bottom.
Here is what I did :
const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
if (bottom) {
this.props.getNewValues();
}
Is there a way to avoid that? Even when you don't zoom, this is not working for people displaying the site on a TV or something like this (like a friend of mine did)
Thanks you
EDIT : I'm applying this on a precise element and I repeat that my solution is working except by unzooming. Unzooming provides float values that made the response not really accurate (it goes from 1 to 50px of difference based on the zoom made)
I use this function (can't take credit as someone else wrote it - sorry for no credit - it was ages ago). Maybe you can adapt this to your use case:
(function($) {
//CHECK SCROLLED INTO VIEW UTIL
function Utils() {
}
Utils.prototype = {
constructor: Utils,
isElementInView: function (element, fullyInView) {
var pageTop = $(window).scrollTop();
var pageBottom = pageTop + $(window).height();
var elementTop = $(element).offset().top;
var elementBottom = elementTop + $(element).height();
if (fullyInView === true) {
return ((pageTop < elementTop) && (pageBottom > elementBottom));
} else {
return ((elementTop <= pageBottom) && (elementBottom >= pageTop));
}
}
};
var Utils = new Utils();
//END CHECK SCROLLED INTO VIEW UTIL
//USING THE ELEMENT IN VIEW UTIL
//this function tells what to do do when the element is or isnt in view.
//var inView = Utils.isElementInView(el, false); Where FALSE means the element doesnt need to be completely in view / TRUE would mean the element needs to be completely in view
function IsEInView(el) {
var inView = Utils.isElementInView(el, false);
if(inView) {
//console.log('in view');
} else {
//console.log('not in view');
}
};
//Check to make sure the element you want to be sure is visible is present on the page
var variableOfYourElement = $('#variableOfYourElement');
//if it is on this page run the function that checks to see if it is partially or fully in view
if( variableOfYourElement.length ) {
//run function on page load
IsEInView(variableOfYourElement);
//run function if the element scrolls into view
$(window).scroll(function(){
IsEInView(variableOfYourElement);
});
}
//END USING THE ELEMENT IN VIEW UTIL
})(jQuery);

How to detect responsive breakpoints of Twitter Bootstrap 3 using JavaScript?

Currently, Twitter Bootstrap 3 have the following responsive breakpoints: 768px, 992px and 1200px, representing small, medium and large devices respectively.
How can I detect these breakpoints using JavaScript?
I would like to listen with JavaScript for all related events triggered when the screen change. And to be able to detect if the screen is for small, medium or large devices.
Is there something already done? What are your suggestions?
Edit: This library is now available through Bower and NPM. See github repo for details.
UPDATED ANSWER:
Live example: CodePen
Latest version: Github repository
Don't like Bootstrap? Check: Foundation demo and Custom framework demos
Have a problem? Open an issue
Disclaimer: I'm the author.
Here's a few things you can do using the latest version (Responsive Bootstrap Toolkit 2.5.0):
// Wrap everything in an IIFE
(function($, viewport){
// Executes only in XS breakpoint
if( viewport.is('xs') ) {
// ...
}
// Executes in SM, MD and LG breakpoints
if( viewport.is('>=sm') ) {
// ...
}
// Executes in XS and SM breakpoints
if( viewport.is('<md') ) {
// ...
}
// Execute only after document has fully loaded
$(document).ready(function() {
if( viewport.is('xs') ) {
// ...
}
});
// Execute code each time window size changes
$(window).resize(
viewport.changed(function() {
if( viewport.is('xs') ) {
// ...
}
})
);
})(jQuery, ResponsiveBootstrapToolkit);
As of version 2.3.0, you don't need the four <div> elements mentioned below.
ORIGINAL ANSWER:
I don't think you need any huge script or library for that. It's a fairly simple task.
Insert the following elements just before </body>:
<div class="device-xs visible-xs"></div>
<div class="device-sm visible-sm"></div>
<div class="device-md visible-md"></div>
<div class="device-lg visible-lg"></div>
These 4 divs allow you check for currently active breakpoint. For an easy JS detection, use the following function:
function isBreakpoint( alias ) {
return $('.device-' + alias).is(':visible');
}
Now to perform a certain action only on the smallest breakpoint you could use:
if( isBreakpoint('xs') ) {
$('.someClass').css('property', 'value');
}
Detecting changes after DOM ready is also fairly simple. All you need is a lightweight window resize listener like this one:
var waitForFinalEvent = function () {
var b = {};
return function (c, d, a) {
a || (a = "I am a banana!");
b[a] && clearTimeout(b[a]);
b[a] = setTimeout(c, d)
}
}();
var fullDateString = new Date();
Once you're equipped with it, you can start listening for changes and execute breakpoint-specific functions like so:
$(window).resize(function () {
waitForFinalEvent(function(){
if( isBreakpoint('xs') ) {
$('.someClass').css('property', 'value');
}
}, 300, fullDateString.getTime())
});
If you don't have specific needs you can just do this:
if ($(window).width() < 768) {
// do something for small screens
}
else if ($(window).width() >= 768 && $(window).width() <= 992) {
// do something for medium screens
}
else if ($(window).width() > 992 && $(window).width() <= 1200) {
// do something for big screens
}
else {
// do something for huge screens
}
Edit: I don't see why you should use another js library when you can do this just with jQuery already included in your Bootstrap project.
Here my own simple solution:
jQuery:
function getBootstrapBreakpoint(){
var w = $(document).innerWidth();
return (w < 768) ? 'xs' : ((w < 992) ? 'sm' : ((w < 1200) ? 'md' : 'lg'));
}
VanillaJS:
function getBootstrapBreakpoint(){
var w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
return (w < 768) ? 'xs' : ((w < 992) ? 'sm' : ((w < 1200) ? 'md' : 'lg'));
}
Detect responsive breakpoint of Twitter Bootstrap 4.1.x using JavaScript
The Bootstrap v.4.0.0 (and the latest version Bootstrap 4.1.x) introduced the updated grid options, so the old concept on detection may not directly be applied (see the migration instructions):
Added a new sm grid tier below 768px for more granular control. We now have xs, sm, md, lg, and xl;
xs grid classes have been modified to not require the infix.
I written the small utility function that respects an updated grid class names and a new grid tier:
/**
* Detect the current active responsive breakpoint in Bootstrap
* #returns {string}
* #author farside {#link https://stackoverflow.com/users/4354249/farside}
*/
function getResponsiveBreakpoint() {
var envs = {xs:"d-none", sm:"d-sm-none", md:"d-md-none", lg:"d-lg-none", xl:"d-xl-none"};
var env = "";
var $el = $("<div>");
$el.appendTo($("body"));
for (var i = Object.keys(envs).length - 1; i >= 0; i--) {
env = Object.keys(envs)[i];
$el.addClass(envs[env]);
if ($el.is(":hidden")) {
break; // env detected
}
}
$el.remove();
return env;
};
Detect responsive breakpoint of Bootstrap v4-beta using JavaScript
The Bootstrap v4-alpha and Bootstrap v4-beta had different approach on grid breakpoints, so here's the legacy way of achieving the same:
/**
* Detect and return the current active responsive breakpoint in Bootstrap
* #returns {string}
* #author farside {#link https://stackoverflow.com/users/4354249/farside}
*/
function getResponsiveBreakpoint() {
var envs = ["xs", "sm", "md", "lg"];
var env = "";
var $el = $("<div>");
$el.appendTo($("body"));
for (var i = envs.length - 1; i >= 0; i--) {
env = envs[i];
$el.addClass("d-" + env + "-none");;
if ($el.is(":hidden")) {
break; // env detected
}
}
$el.remove();
return env;
}
I think it would be useful, as it's easy to integrate to any project. It uses native responsive display classes of the Bootstrap itself.
Have you taken a look at Response.js? It's designed for this kind of thing. Combine Response.band and Response.resize.
http://responsejs.com/
Response.resize(function() {
if ( Response.band(1200) )
{
// 1200+
}
else if ( Response.band(992) )
{
// 992+
}
else if ( Response.band(768) )
{
// 768+
}
else
{
// 0->768
}
});
You could use the window size and hard code the breakpoints. Using Angular:
angular
.module('components.responsiveDetection', [])
.factory('ResponsiveDetection', function ($window) {
return {
getBreakpoint: function () {
var w = $window.innerWidth;
if (w < 768) {
return 'xs';
} else if (w < 992) {
return 'sm';
} else if (w < 1200) {
return 'md';
} else {
return 'lg';
}
}
};
});
Using this approach with Response.js is better. Response.resize triggers on every window resize where crossover will only be triggered if breakpoint is changed
Response.create({
prop : "width",
breakpoints : [1200, 992, 768, 480, 320, 0]
});
Response.crossover('width', function() {
if (Response.band(1200)) {
// 1200+
} else if (Response.band(992)) {
// 992+
} else if (Response.band(768)) {
// 768+
} else if (Response.band(480)) {
//480+
} else {
// 0->320
}
});
Response.ready(function() {
$(window).trigger('resize');
});
There should be no problem with some manual implementation like the one mentioned by #oozic.
Here are a couple of libs you could take a look at:
Response.js - jQuery plugin - make use of html data attributes and also has a js api.
enquire.js - enquire.js is a lightweight, pure JavaScript library for responding to CSS media queries
SimpleStateManager - s a javascript state manager for responsive websites. It is built to be light weight, has no dependencies.
Note that these libs are designed to work independently of bootstrap, foundation, etc. You can configure your own breakpoints and have fun.
You may want to add this to your bootstrap project to check active breakpoint visually
<script type='text/javascript'>
$(document).ready(function () {
var mode;
$('<div class="mode-informer label-info" style="z-index:1000;position: fixed;bottom:10px;left:10px">%mode%</div>').appendTo('body');
var checkMode = function () {
if ($(window).width() < 768) {
return 'xs';
}
else if ($(window).width() >= 768 && $(window).width() < 992) {
return 'sm';
}
else if ($(window).width() >= 992 && $(window).width() < 1200) {
return 'md';
}
else {
return 'lg';
}
};
var compareMode = function () {
if (mode !== checkMode()) {
mode = checkMode();
$('.mode-informer').text(mode).animate({
bottom: '100'
}, 100, function () {
$('.mode-informer').animate({bottom: 10}, 100)
});
}
};
$(window).on('resize', function () {
compareMode()
});
compareMode();
});
</script>
Here is the BOOTPLY
Building on Maciej Gurban's answer (which is fantastic... if you like this, please just up vote his answer). If you're building a service to query you can return the currently active service with the setup below. This could replace other breakpoint detection libraries entirely (like enquire.js if you put in some events). Note that I've added a container with an ID to the DOM elements to speed up DOM traversal.
HTML
<div id="detect-breakpoints">
<div class="breakpoint device-xs visible-xs"></div>
<div class="breakpoint device-sm visible-sm"></div>
<div class="breakpoint device-md visible-md"></div>
<div class="breakpoint device-lg visible-lg"></div>
</div>
COFFEESCRIPT (AngularJS, but this is easily convertible)
# this simple service allows us to query for the currently active breakpoint of our responsive app
myModule = angular.module('module').factory 'BreakpointService', ($log) ->
# alias could be: xs, sm, md, lg or any over breakpoint grid prefix from Bootstrap 3
isBreakpoint: (alias) ->
return $('#detect-breakpoints .device-' + alias).is(':visible')
# returns xs, sm, md, or lg
getBreakpoint: ->
currentBreakpoint = undefined
$visibleElement = $('#detect-breakpoints .breakpoint:visible')
breakpointStringsArray = [['device-xs', 'xs'], ['device-sm', 'sm'], ['device-md', 'md'], ['device-lg', 'lg']]
# note: _. is the lodash library
_.each breakpointStringsArray, (breakpoint) ->
if $visibleElement.hasClass(breakpoint[0])
currentBreakpoint = breakpoint[1]
return currentBreakpoint
JAVASCRIPT (AngularJS)
var myModule;
myModule = angular.module('modules').factory('BreakpointService', function($log) {
return {
isBreakpoint: function(alias) {
return $('#detect-breakpoints .device-' + alias).is(':visible');
},
getBreakpoint: function() {
var $visibleElement, breakpointStringsArray, currentBreakpoint;
currentBreakpoint = void 0;
$visibleElement = $('#detect-breakpoints .breakpoint:visible');
breakpointStringsArray = [['device-xs', 'xs'], ['device-sm', 'sm'], ['device-md', 'md'], ['device-lg', 'lg']];
_.each(breakpointStringsArray, function(breakpoint) {
if ($visibleElement.hasClass(breakpoint[0])) {
currentBreakpoint = breakpoint[1];
}
});
return currentBreakpoint;
}
};
});
Instead of using $(document).width(), you should get set a CSS rule that gives you this information.
I just wrote an article to get it accurately. See it here : http://www.xurei-design.be/2013/10/how-to-accurately-detect-responsive-breakpoints/
Why not just use jQuery to detect the current css width of the bootstrap container class?
ie..
if( parseInt($('#container').css('width')) > 1200 ){
// do something for desktop screens
}
You could also use $(window).resize() to prevent your layout from "soiling the bed" if someone resizes the browser window.
Instead of inserting the below many times into each page...
<div class="device-xs visible-xs"></div>
<div class="device-sm visible-sm"></div>
<div class="device-md visible-md"></div>
<div class="device-lg visible-lg"></div>
Just use JavaScript to dynamically insert it into every page (note that I have updated it to work with Bootstrap 3 with .visible-*-block:
// Make it easy to detect screen sizes
var bootstrapSizes = ["xs", "sm", "md", "lg"];
for (var i = 0; i < bootstrapSizes.length; i++) {
$("<div />", {
class: 'device-' + bootstrapSizes[i] + ' visible-' + bootstrapSizes[i] + '-block'
}).appendTo("body");
}
I don't have enough reputation points to comment but for those who are having problems with getting "unrecognized" when they try using Maciej Gurban's ResponsiveToolKit, I was also getting that error until I noticed that Maciej actually references the toolkit from the bottom of the page in his CodePen
I tried doing that and suddenly it worked ! So, use the ResponsiveToolkit but put your links in the bottom of the page:
I don't know why it makes a difference but it does.
Here is another way to detect the current viewport without putting the viewport size numbers in your javascript.
See css and javascript snippets here: https://gist.github.com/steveh80/288a9a8bd4c3de16d799
After adding that snippets to your css and javascript files you can detect the current viewport like this:
viewport.is('xs') // returns boolean
If you want to detect a viewport range use it like this
viewport.isEqualOrGreaterThan('sm') // returns true for sm, md and lg
Bootstrap's CSS for the .container class looks like that:
.container {
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
#media (min-width: 768px) {
.container {
width: 750px;
}
}
#media (min-width: 992px) {
.container {
width: 970px;
}
}
#media (min-width: 1200px) {
.container {
width: 1170px;
}
}
So this means we can safely rely on jQuery('.container').css('width') to detect breakpoints without the drawbacks of relying on jQuery(window).width().
We can write a function like this:
function detectBreakpoint() {
// Let's ensure we have at least 1 container in our pages.
if (jQuery('.container').length == 0) {
jQuery('body').append('<div class="container"></div>');
}
var cssWidth = jQuery('.container').css('width');
if (cssWidth === '1170px') return 'lg';
else if (cssWidth === '970px') return 'md';
else if (cssWidth === '750px') return 'sm';
return 'xs';
}
And then test it like
jQuery(document).ready(function() {
jQuery(window).resize(function() {
jQuery('p').html('current breakpoint is: ' + detectBreakpoint());
});
detectBreakpoint();
});
Use CSS :before and content property to print the breakpoint state in the <span id="breakpoint-js"> so the JavaScript just have to read this data to turn it as a variable to use within your function.
(run the snippet to see the example)
NOTE: I added a few line of CSS to use the <span> as a red flag in the upper corner of my browser. Just make sure to switch it back to display:none; before pushing your stuff public.
// initialize it with jquery when DOM is ready
$(document).on('ready', function() {
getBootstrapBreakpoint();
});
// get bootstrap grid breakpoints
var theBreakpoint = 'xs'; // bootstrap336 default = mobile first
function getBootstrapBreakpoint(){
theBreakpoint = window.getComputedStyle(document.querySelector('#breakpoint-js'),':before').getPropertyValue('content').replace(/['"]+/g, '');
console.log('bootstrap grid breakpoint = ' + theBreakpoint);
}
#breakpoint-js {
/* display: none; //comment this while developping. Switch back to display:NONE before commit */
/* optional red flag layout */
position: fixed;
z-index: 999;
top: 0;
left: 0;
color: white;
padding: 5px 10px;
background-color: red;
opacity: .7;
/* end of optional red flag layout */
}
#breakpoint-js:before {
content: 'xs'; /* default = mobile first */
}
#media screen and (min-width: 768px) {
#breakpoint-js:before {
content: 'sm';
}
}
#media screen and (min-width: 992px) {
#breakpoint-js:before {
content: 'md';
}
}
#media screen and (min-width: 1200px) {
#breakpoint-js:before {
content: 'lg';
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<div class="container">
<span id="breakpoint-js"></span>
<div class="page-header">
<h1>Bootstrap grid examples</h1>
<p class="lead">Basic grid layouts to get you familiar with building within the Bootstrap grid system.</p>
</div>
</div>
Bootstrap4 with jQuery, simplified solution
<div class="device-sm d-sm-none"></div>
<div class="device-md d-md-none"></div>
<div class="device-lg d-lg-none"></div>
<div class="device-xl d-xl-none"></div>
<script>
var size = $('.device-xl').is(':hidden') ? 'xl' : ($('.device-lg').is(':hidden') ? 'lg'
: ($('.device-md').is(':hidden') ? 'md': ($('.device-sm').is(':hidden') ? 'sm' : 'xs')));
alert(size);
</script>
I've made a native jQuery method for Twitter Bootstrap screen size detection. Here is:
// Screen size ID will be stored in this variable (global var for JS)
var CurrentBootstrapScreenSize = 'unknown';
$(document).ready(function () {
// <div> objects for all screen sizes required for screen size detection.
// These <div> is hidden for users eyes.
var currentScreenSizeDetectorObjects = $('<div>').css({
'position':'absolute',
'top':'-200px'
}).addClass('current-screen-size').append([
$('<div>').addClass('device-xs visible-xs').html(' '),
$('<div>').addClass('device-sm visible-sm').html(' '),
$('<div>').addClass('device-md visible-md').html(' '),
$('<div>').addClass('device-lg visible-lg').html(' ')
]);
// Attach <div> objects to <body>
$('body').prepend(currentScreenSizeDetectorObjects);
// Core method for detector
function currentScreenSizeDetectorMethod() {
$(currentScreenSizeDetectorObjects).find('div').each(function() {
var className = $(this).attr('class');
if($(this).is(':visible')) {
if(String(className).match(/device-xs/)) CurrentBootstrapScreenSize = 'xs';
else if(String(className).match(/device-sm/)) CurrentBootstrapScreenSize = 'sm';
else if(String(className).match(/device-md/)) CurrentBootstrapScreenSize = 'md';
else if(String(className).match(/device-lg/)) CurrentBootstrapScreenSize = 'lg';
else CurrentBootstrapScreenSize = 'unknown';
};
})
console.log('Current Bootstrap screen size is: '+CurrentBootstrapScreenSize);
$('.CurrentBootstrapScreenSize').first().html('Bootstrap current screen size: <b>' + CurrentBootstrapScreenSize + '</b>' );
}
// Bind screen size and orientation change
$(window).bind("resize orientationchange", function() {
// Execute screen detection
currentScreenSizeDetectorMethod();
});
// Execute screen detection on page initialize
currentScreenSizeDetectorMethod();
});
JSFillde: https://jsfiddle.net/pstepniewski/7dz6ubus/
JSFillde as fullscreen example: https://jsfiddle.net/pstepniewski/7dz6ubus/embedded/result/
For anyone interested in this, i wrote a breakpoint detection based on CSS breakpoints using TypeScript and Observables. it is not very hard to make ES6 out of it, if you remove the types. In my example i use Sass, but it is also easy to remove this.
Here is my JSFiddle: https://jsfiddle.net/StefanJelner/dorj184g/
HTML:
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.min.js"></script>
<div id="result"></div>
SCSS:
body::before {
content: 'xs';
display: none;
#media screen and (min-width: 480px) {
content: 's';
}
#media screen and (min-width: 768px) {
content: 'm';
}
#media screen and (min-width: 1024px) {
content: 'l';
}
#media screen and (min-width: 1280px) {
content: 'xl';
}
}
TypeScript:
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
class BreakpointChangeService {
private breakpointChange$: BehaviorSubject<string>;
constructor(): BehaviorSubject<string> {
// initialize BehaviorSubject with the current content of the ::before pseudo element
this.breakpointChange$ = new Rx.BehaviorSubject(this.getBreakpoint());
// observe the window resize event, throttle it and combine it with the BehaviorSubject
Rx.Observable
.fromEvent(window, 'resize')
.throttleTime(0, Rx.Scheduler.animationFrame)
.withLatestFrom(this.breakpointChange$)
.subscribe(this.update.bind(this))
;
return this.breakpointChange$;
}
// method to get the content of the ::before pseudo element
private getBreakpoint(): string {
// see https://www.lullabot.com/articles/importing-css-breakpoints-into-javascript
return window.getComputedStyle(document.body, ':before').getPropertyValue('content').replace(/[\"\']/g, '');
}
private update(_, recent): void {
var current = this.getBreakpoint();
if(recent !== current) { this.breakpointChange$.next(current); }
}
}
// if the breakpoint changes, react on it
var $result = document.getElementById('result');
new BreakpointChangeService().subscribe(breakpoint => {
$result.innerHTML = Date.now()+': '+breakpoint;
});
I hope this helps somebody.
I was not really satisfied with the given answers, which seem overly complicated to use to me, so I wrote my own solution. However, for the time being this relies on underscore/lodash to work.
https://github.com/LeShrimp/GridSizeEvents
You can use it like this:
GridSizeEvents.addListener(function (newSize, oldSize) {
// Will output eg. "xs -> sm"
console.log(oldSize + ' -> ' + newSize);
});
This works out of the Box for Bootstrap 3, as the breakpoints are hard coded to 768px, 992px and 1200px. For other versions you could easily adapt the code.
Internally this uses matchMedia() and should thus guarantee to produce results that are in synch with Bootstrap.
Maybe it'll help some of you, but there is a plugin which help you to detect on which current Bootstrap v4 breakpoint you are see: https://www.npmjs.com/package/bs-breakpoints
Simple to use (can be used with or without jQuery):
$(document).ready(function() {
bsBreakpoints.init()
console.warn(bsBreakpoint.getCurrentBreakpoint())
$(window).on('new.bs.breakpoint', function (event) {
console.warn(event.breakpoint)
})
})
Here is my solution (Bootstrap 4):
<div class="alert alert-warning row">
<div class="col">
Bootstrap breakpoint is
</div>
<div class="col">
<div class="d-block d-sm-none">
XS
</div>
<div class="d-none d-sm-block d-md-none">
SM
</div>
<div class="d-none d-md-block d-lg-none">
MD
</div>
<div class="d-none d-lg-block d-xl-none">
MD
</div>
<div class="d-none d-xl-block">
MD
</div>
</div>
</div>
For anyone using knockout.js, I wanted some knockout.js observable properties that would tell me when the breakpoints are hit. I opted to use Modernizr's support for css-style media queries so the numbers matched the bootstrap definitions, and to get modernizr's compatibility benefits. My knockout view model is as follows:
var viewModel = function() {
// depends on jquery, Modernizr
var self = this;
self.widthXsOrLess = ko.observable();
self.widthSmOrLess = ko.observable();
self.widthMdOrLess = ko.observable();
var setWindowSizeVars = function() {
self.widthXsOrLess(!Modernizr.mq('(min-width: 768px)'));
self.widthSmOrLess(!Modernizr.mq('(min-width: 992px)'));
self.widthMdOrLess(!Modernizr.mq('(min-width: 1200px)'));
};
$(window).resize(setWindowSizeVars);
setWindowSizeVars();
};
Here is good way to detect it (maybe funny, but works) and You can use necessary element so code is clear:
Example:
css:
#media (max-width: 768px) {
#someElement
{
background: pink
}
}
and in document by jQuery:
if($('#someElement').css('background') == 'pink')
{
doWhatYouNeed();
}
of course css property is any.
Since bootstrap 4 will be out soon I thought I would share a function that supports it (xl is now a thing) and performs minimal jQuery to get the job done.
/**
* Get the Bootstrap device size
* #returns {string|boolean} xs|sm|md|lg|xl on success, otherwise false if Bootstrap is not working or installed
*/
function findBootstrapEnvironment() {
var environments = ['xs', 'sm', 'md', 'lg', 'xl'];
var $el = $('<span />');
$el.appendTo($('body'));
for (var i = environments.length - 1; i >= 0; i--) {
var env = environments[i];
$el.addClass('hidden-'+env);
if ($el.is(':hidden')) {
$el.remove();
return env;
}
}
$el.remove();
return false;
}
Bootstrap 4
setResponsiveDivs();
function setResponsiveDivs() {
var data = [
{id: 'visible-xs', class: 'd-block d-sm-none'},
{id: 'visible-sm', class: 'd-none d-sm-block d-md-none'},
{id: 'visible-md', class: 'd-none d-md-block d-lg-none'},
{id: 'visible-lg', class: 'd-none d-lg-block d-xl-none'},
{id: 'visible-xl', class: 'd-none d-xl-block'}
];
for (var i = 0; i < data.length; i++) {
var el = document.createElement("div");
el.setAttribute('id', data[i].id);
el.setAttribute('class', data[i].class);
document.getElementsByTagName('body')[0].appendChild(el);
}
}
function isVisible(type) {
return window.getComputedStyle(document.getElementById('visible-' + type), null).getPropertyValue('display') === 'block';
}
// then, at some point
window.onresize = function() {
console.log(isVisible('xs') === true ? 'xs' : '');
console.log(isVisible('sm') === true ? 'sm' : '');
console.log(isVisible('md') === true ? 'md' : '');
console.log(isVisible('lg') === true ? 'lg' : '');
console.log(isVisible('xl') === true ? 'xl' : '');
};
or minified
function setResponsiveDivs(){for(var e=[{id:"visible-xs","class":"d-block d-sm-none"},{id:"visible-sm","class":"d-none d-sm-block d-md-none"},{id:"visible-md","class":"d-none d-md-block d-lg-none"},{id:"visible-lg","class":"d-none d-lg-block d-xl-none"},{id:"visible-xl","class":"d-none d-xl-block"}],s=0;s<e.length;s++){var l=document.createElement("div");l.setAttribute("id",e[s].id),l.setAttribute("class",e[s]["class"]),document.getElementsByTagName("body")[0].appendChild(l)}}function isVisible(e){return"block"===window.getComputedStyle(document.getElementById("visible-"+e),null).getPropertyValue("display")}setResponsiveDivs();
If you use Knockout, then you could use the following custom binding to bind the current viewport breakpoint (xs, sm, md or lg) to an observable in your model. The binding...
wraps the 4 divs with visible-?? class in a div with id detect-viewport and adds it to the body if it doesn't exist already (so you could reuse this binding without duplicating these divs)
sets the current viewport breakpoint to the bound observable by querying which of the divs is visible
updates the current viewport breakpoint when the window is resized
ko.bindingHandlers['viewport'] = {
init: function(element, valueAccessor) {
if (!document.getElementById('detect-viewport')) {
let detectViewportWrapper = document.createElement('div');
detectViewportWrapper.id = 'detect-viewport';
["xs", "sm", "md", "lg"].forEach(function(breakpoint) {
let breakpointDiv = document.createElement('div');
breakpointDiv.className = 'visible-' + breakpoint;
detectViewportWrapper.appendChild(breakpointDiv);
});
document.body.appendChild(detectViewportWrapper);
}
let setCurrentBreakpoint = function() {
valueAccessor()($('#detect-viewport div:visible')[0].className.substring('visible-'.length));
}
$(window).resize(setCurrentBreakpoint);
setCurrentBreakpoint();
}
};
ko.applyBindings({
currentViewPort: ko.observable()
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<div data-bind="viewport: currentViewPort"></div>
<div>
Current viewport breakpoint: <strong data-bind="text: currentViewPort"></strong>
</div>
<div>
(Click the <em>full page</em> link of this snippet to test the binding with different window sizes)
</div>
It's been a while since the OP, but here's my solution for this using Bootstrap 3. In my use case, I was only targeting rows, but the same could be applied to the container, etc.
Just change .row to whatever you want.
jQuery(document).ready(function ($) {
var alterClass = function () {
var ww = document.body.clientWidth;
if (ww < 768) {
$('.row').addClass('is-xs').removeClass('is-sm').removeClass('is-lg').removeClass('is-md');
} else if (ww >= 768 && ww < 992) {
$('.row').addClass('is-sm').removeClass('is-xs').removeClass('is-lg').removeClass('is-md');
} else if (ww >= 992 && ww < 1200) {
$('.row').addClass('is-md').removeClass('is-xs').removeClass('is-lg').removeClass('is-sm');
} else if (ww >= 1200) {
$('.row').addClass('is-lg').removeClass('is-md').removeClass('is-sm').removeClass('is-xs');
};
};
// Make Changes when the window is resized
$(window).resize(function () {
alterClass();
});
// Fire when the page first loads
alterClass();
});
Here is an idea how to do it using CSS variables.
For Bootstrap 3 (or Bootstrap 4) using CSS:
:root {
--current-breakpoint: xs;
}
#media (min-width: 576px){
:root {
--current-breakpoint: sm;
}
}
#media (min-width: 768px){
:root {
--current-breakpoint: md;
}
}
#media (min-width: 992px){
:root {
--current-breakpoint: lg;
}
}
#media (min-width: 1200px){
:root {
--current-breakpoint: xl;
}
}
Bootstrap 4 and SCSS may look like this:
:root{
#include media-breakpoint-up(xs) {
--current-breakpoint:xs;
}
#include media-breakpoint-up(sm) {
--current-breakpoint:sm;
}
#include media-breakpoint-up(md) {
--current-breakpoint:md;
}
#include media-breakpoint-up(lg) {
--current-breakpoint:lg;
}
#include media-breakpoint-up(xl) {
--current-breakpoint:xl;
}
}
The JS code may be:
window.onresize=()=>{
console.log(getComputedStyle(document.documentElement).getPropertyValue('--current-breakpoint'));
}

Resizing an image using Javascript running in Opera Browser

I hope someone can help with this quirky issue I am having with the Opera Browser, I have version 11 Beta installed, but I suspect is a common problem in Opera.
The website and page in question is http://www.amigaos.net/index.html.
At the bottom of the body of the html I have the following code which resizes the 3 images you see on this webpage depending on width of the viewport at page load. In Safari and FireFox the code works fine, but in Opera the following lines which involve resizing the width and height of an image do not work:
document.getElementById('img1').width = '475';
document.getElementById('img1').height = '375';
Here is the code in full (sorry, about the layout, stackoverflow hasn't formatted carriage returns correctly)
<script type="text/javascript">
function GetWidth()
{
var x = 0;
if (typeof window.innerWidth != 'undefined')
{
x = window.innerWidth;
}
else if (document.documentElement && document.documentElement.clientHeight)
{
x = document.documentElement.clientWidth;
}
else if (document.body)
{
x = document.getElementsByTagName('body')[0].clientWidth;
}
return x;
}
width = GetWidth();
if (width>=1680)
{
document.getElementById('img1').width = '475';
document.getElementById('img1').height = '375';
document.getElementById('img2').width = '475';
document.getElementById('img2').height = '375';
document.getElementById('img3').width = '475';
document.getElementById('img3').height = '375';
}
else if ((width>800) && (width<=1280))
{
document.getElementById('img1').width = '300';
document.getElementById('img1').height = '235';
document.getElementById('img2').width = '300';
document.getElementById('img2').height = '235';
document.getElementById('img3').width = '300';
document.getElementById('img3').height = '235';
}
else if (width<=800)
{
document.getElementById('img1').width = '225';
document.getElementById('img1').height = '195';
document.getElementById('img2').width = '225';
document.getElementById('img2').height = '195';
document.getElementById('img3').width = '225';
document.getElementById('img3').height = '195';
}
</script>
instead of doing width and height attributes, I think you can just set width: 33% via CSS and have the scaling happen automatically, regardless of the browser window size. Better solution than trying to use javascript, IMHO.
Here's a simple tutorial: http://haslayout.net/css-tuts/CSS-Proportional-Image-Scale
you are making this way too complicated. I don't think your issue is browser-specific, you just need to recode your script.
First. I would recommmend using percentages.. Not sure how you will guess the visitors browser width in pixels.
Let's say that your three resizeable images are 20% width of your browser. So your css would be:
#img1, #img2, #img3 {
width: 20%;
}
now that your css says that your images are 20% of the total with, you're good to add some js. Keep in mind that the percentage will be that of its outer container.
<script type=text/javascript">
function resizeImages() {
document.getElementById('img1').style.height = (document.body.clientHeight - 100) * 0.2;
document.getElementById('img2').style.height = (document.body.clientHeight - 100) * 0.2;
document.getElementById('img3').style.height = (document.body.clientHeight - 100) * 0.2;
}
</script>
and most importantly.. call your function:
add this to your body tag:
<body onresize="resizeImages()">
boom.. you're done.

jQuery check if browser support position: fixed

How do I check if browser supports position:fixed using jQuery. I assume I have to use $.support I think, but how?
Thank you for your time.
The most reliable way would be to actually feature-test it. Browser sniffing is fragile and unreliable.
I have an example of such test in CFT http://kangax.github.com/cft/#IS_POSITION_FIXED_SUPPORTED. Note that the test should be run after document.body is loaded.
I find that mobile safari (specifically iOS 4.2 via the iOS Simulator on OSX) refuses to scroll anywhere unless you wait a few miliseconds. Hence the false positive.
I wrote a quick jquery plugin to work around it:
(function($) {
$.support.fixedPosition = function (callback) {
setTimeout(
function () {
var container = document.body;
if (document.createElement && container && container.appendChild && container.removeChild) {
var el = document.createElement('div');
if (!el.getBoundingClientRect) return null;
el.innerHTML = 'x';
el.style.cssText = 'position:fixed;top:100px;';
container.appendChild(el);
var originalHeight = container.style.height,
originalScrollTop = container.scrollTop;
container.style.height = '3000px';
container.scrollTop = 500;
var elementTop = el.getBoundingClientRect().top;
container.style.height = originalHeight;
var isSupported = !!(elementTop === 100);
container.removeChild(el);
container.scrollTop = originalScrollTop;
callback(isSupported);
}
else {
callback(null);
}
},
20
);
}
})(jQuery);
function fixedcheck () {
var fixedDiv = $('<div>').css('position', 'fixed').appendTo('body');
var pos1 = fixedDiv.offset().top;
$(window).scrollTop($(window).scrollTop() + 1);
var pos2 = fixedDiv.offset().top;
fixedDiv.remove();
return (pos1 != pos2)
}
/* Usage */
$(document).ready(function () {
if (!fixedcheck()) alert('Your browser does not support fixed position!')
});
You could check if position exists by making a code like this:
<html>
<script type="text/javascript">
test = function() {
if(!!document.getElementById("test").style.position) {
alert('true');
}
else{
alert('false');
}
}
</script>
<body>
<p id="test" onclick="test();" style="position:fixed;">Hi</p>
</body>
</html>
Since position exists in all main browser this will always return true. I imagine there isn't a way to check the possible values of position, so you'll have to check which browser and which version the user are viewing your page as Paolo Bergantino said.
position:fixed apparently works for all block elements in Mobile Safari (4.3.2) except body, so the CFT answer (http://kangax.github.com/cft/#IS_POSITION_FIXED_SUPPORTED) should have this in it:
var isSupported = (container.scrollTop === 500 && elementTop === 100);
The feature-test Position fixed support , mentioned above, returns a false-positive on Opera Mini (which does not support position: fixed).
I've created another check if position:fixed is really supported in browser. It creates fixed div and try to scroll and check if the position of div changed.
function isPositionFixedSupported(){
var el = jQuery("<div id='fixed_test' style='position:fixed;top:1px;width:1px;height:1px;'></div>");
el.appendTo("body");
var prevScrollTop = jQuery(document).scrollTop();
var expectedResult = 1+prevScrollTop;
var scrollChanged = false;
//simulate scrolling
if (prevScrollTop === 0) {
window.scrollTo(0, 1);
expectedResult = 2;
scrollChanged = true;
}
//check position of div
suppoorted = (el.offset().top === expectedResult);
if (scrollChanged) {
window.scrollTo(0, prevScrollTop);
}
el.remove();
return suppoorted;
}
This function was tested in Firefox 22, Chrome 28, IE 7-10, Android Browser 2.3.

Categories

Resources