How can I tell if a page is in print view? - javascript

I'm using a simple toggle of the CSS display attribute from none to block in a FAQ page. However, I want the page to show everything when printing. What happens now is if you go to the page and go to print mode, it'll open all the closed items since I added this code to my print.css stylesheet...
.faq
{
display:block;
}
However, if you open an item, close it again, and then go to print mode, that item will remain closed.
My JS code looks like this...
` var divNum = new Array("faq1", "faq2", "faq3", "faq4", "faq5", "faq6", "faq7", "faq8", "faq9", "faq10", "faq11", "faq12", "faq13");
function openClose(theID) {
for (var i = 0; i < divNum.length; i++) {
if (divNum[i] == theID) {
if (document.getElementById(divNum[i]).style.display == "block") { document.getElementById(divNum[i]).style.display = "none" }
else { document.getElementById(divNum[i]).style.display = "block" }
}
}
}`
and the HTML looks like this
<a class="faq" onClick="openClose('faq1')">Question?</a><br />
<p id="faq1" class="faq">Answer</p>
What can I do to make sure everything is opened when I go into print mode?

Instead of manipulating the display status of an element directly with your JS code, I would have classes defined in CSS then simply toggle the classes using JS.
If your classes are defined for #media screen only then you won't have to worry about what the current display status is of any of the FAQ entries.
EDIT: For example, in your CSS file:
#media screen .faq.open {
display: block;
}
#media screen .faq {
display: none;
}
Then your JS would look like:
function openClose(theID) {
for (var i = 0; i < divNum.length; i++) {
if (divNum[i] == theID) {
if (document.getElementById(divNum[i]).className.match(new RegExp('(\\s|^)open(\\s|$)'))) { document.getElementById(divNum[i]).className = ele.className.replace(new RegExp('(\\s|^)open(\\s|$)'), ' '); }
else { document.getElementById(divNum[i]).className += " open" }
}
}
}
Note, I haven't tested this so there may be some syntax errors. Also, most of my projects already include jQuery so the method I typically use is much cleaner code. I have not used jQuery here because you didn't use it in your code samples.

Related

How to make Jquery Darkmode load directly on the beginning?

So I have set up a theme switcher on my site, which stores preference in localStorage and everything works fine. Except that my code is loaded after document.ready, and checks for condition / status of theme. I also tried removing document.ready, because obviously that would remove any finishing of loading the site, but that doesn't work, and the function starts direct on load and obviously can't change any CSS, because there isn't any. For now, I am using a preloader, which is working fine, but it would be awesome if it could work without it. I have also placed the code at the beginning of the body tag, so it should execute really early. In the following, I will show you my code and a screenshot of my console, where you can see when the code gets executed. For your knowledge, I am on Shopify, if that does anything.
Any help for making my theme / code switch faster, is very much appreciated.
My Console:
console img...
My Code (I am sorry for the formatting, I made if farly quickly in the Shopify admin, so no "Prettier" and also no "IntelliSense" :) )
// Darkmode #legoon #raffaelbaer
let ALLDARKMODE = '#over-header-info-bar-legoon, #StickyBar, .full-width--return-link,' +
'.cart__subtotal, .transcy-money, .site-footer__newsletter-input, #product-title__cart-page-legoon, .cart-table,' +
'.responsive-table__row, label, .gridforsubcategories_item-grid, .linkitem_hassubcategoriesinner, .sticky--active, .sticky--open,' +
'.icon-hamburger, .icon-close, .drawer__inner, #einloggendrawerlink, .drawer__nav-link--top-level, .einloggensvgmobile, .drawer__search,' +
'.drawer__search-input, #legooninstagrammobile, #instagramiconheadermobile, .svgdarkmode, .drawer__nav-item, .drawerforsocialicons, #footerlegoonlink,' +
'.content-block, .icon-arrow-down, .product-card__name, .product-card__info, .country-selector, .site-header__search-input, .menu-sub-custom1, .menu-sub-custom2,' +
'.labelcustomnav1, .labelcustomnav2, .labelcustomnav3, .labelcustomnav4, .menu-sub-custom3, .menu-sub-custom4, .nav-bar, h1, h2, #footer-down_copyright, h3, h4, h5,' +
'h6, body, a, input, textarea, select, .link-item>a , #instagramiconheader, .site-footer, .site-footer__copyright, .darkmodeswitch, p, .site-footer__newsletter-label>p,' +
'button, #icon-wunschliste, .page-container, .labelcustomnav1>p, .labelcustomnav2>p, .labelcustomnav3>p, .labelcustomnav4>p, .site-header, .legoonlogotop, .iconuserhead, .icon-cart,' +
'.notification--success, .notification--promo, .notification__message>span, .wg-drop.country-selector, .hr-empfy_cart-page';
$(document).ready(function() {
$('.darkmodeswitch').click(function() {
/*main toggle dark mode*/
$(ALLDARKMODE).toggleClass("dark");
/*change darkmode image on mobile drawer and desktop accordingly to current mode | theme (light or dark)*/
if ($(".darkmodeswitch").hasClass("dark")) {
document.getElementById('svgdarkmode').src = "https://cdn.shopify.com/s/files/1/0571/7137/8349/files/sun.svg?v=1650307091";
document.getElementById('svgdarkmodemobile').src = "https://cdn.shopify.com/s/files/1/0571/7137/8349/files/sun.svg?v=1650307091";
localStorage.setItem("theme", "dark");
} else {
document.getElementById('svgdarkmode').src = "https://cdn.shopify.com/s/files/1/0571/7137/8349/files/moon.svg?v=1650306791";
document.getElementById('svgdarkmodemobile').src = "https://cdn.shopify.com/s/files/1/0571/7137/8349/files/moon.svg?v=1650306791";
localStorage.setItem("theme", "light");
};
});
// Am Anfang
if (localStorage.getItem("theme") === "dark") {
console.log("Darkmode status = true");
$(ALLDARKMODE).addClass("dark");
} else {
console.log("Darkmode status = false");
$(ALLDARKMODE).removeClass("dark");
}
$('.darkmodeswitch').click();
});
For a dark mode switch, simply toggle a CSS class on the HTML element (document.documentElement.classList.toggle('dark');), and use that as a contextual selector for changes on your elements. Use CSS custom properties for the changes.
Here's an example of the approach. Please note that localStorage is unavailable in StackOverflow snippets due to security reasons, so I left that part out (you've got that working already anyway).
function toggleDarkMode() {
document.documentElement.classList.toggle('dark');
}
:root {
--body-bg-color: #fff;
--btn-bg-color: #f0f0f0;
--btn-fg-color: #333;
}
:root.dark {
--body-bg-color: #666;
--btn-bg-color: #333;
--btn-fg-color: #f0f0f0;
}
body {
background-color: var(--body-bg-color);
}
button {
background-color: var(--btn-bg-color);
border: 1px solid #888;
color: var(--btn-fg-color);
}
<button type="button" id="toggle" onclick="toggleDarkMode()">Dark Mode Button</button>

Bootstrap Avoid navbar-toggle button to close opened sub menus on mobile

I have a code based on Bootstrap 3.3.7
There are different menu items and sub menus on my menu, I want to have all sub menu items to be open on mobile, Means no need to click on any menu items to display it's sub menus, So I wrote a JS code to open all sub menus on mobile:
function opensubmenus() {
if ($(window).width() < 768) {
$("#top-navbar-collapse li").addClass('open');
} else {
$("#top-navbar-collapse li").removeClass('open');
}
}
$(window).resize(opensubmenus);
opensubmenus();
But the problem is when I click on a navbar-toggle button, It closes all submenus, But I need to keep them open on mobile devices all the time
You can check my online sample on this site: https://dedidata.com
Here I have shown a screenshot: https://pasteboard.co/IfSMCIu.jpg
I don't like to disable navbar-toggle button completely, I need it to toggle the whole navbar, But I don't like it close the submenus, My JS code opens the submenus, But navbar-toggle closes those submenus
This snippet will be applied to all dropdowns, you can modify it to get what dropdowns you need.
I will explain what it does:
const targets = document.getElementsByClassName('dropdown-toggle');
for(let i = 0; i < targets.length; i++) {
targets[i].addEventListener('click', () => {
targets[i].hasAttribute('data-toggle') &&
targets[i].removeAttribute('data-toggle');
// Managing locally the open and close
targets[i].parentElement.classList.toggle('open');
});
}
First line:
const targets = document.getElementsByClassName('dropdown-toggle');
we get all the elements with class name dropdown-toggle (this is used in boostrap for dropdowns menus)
For each element, we attach a click listener to be able to manage "manually" dropdowns if the dropdown menu is clicked by the user.
This is managed by line: targets[i].parentElement.classList.toggle('open');
And the important one to avoid auto closing menus is to remove the attribute data-toggle.
targets[i].hasAttribute('data-toggle') &&
targets[i].removeAttribute('data-toggle');
If you only one apply a solution like this for mobiles you can use is.js to check when you are in mobile (android/ios)
UPDATE
This update will open the menus automatically:
const menuItems = document.getElementsByClassName('navbar-toggle');
for (let i = 0; i < menuItems.length; i++) {
menuItems[i].hasAttribute('data-toggle') && menuItems[i].addEventListener('click', () => {
const elements = document.getElementsByClassName('dropdown-toggle');
for (let i = 0; i < elements.length; i++) {
elements[i].hasAttribute('data-toggle') && elements[i].removeAttribute('data-toggle');
elements[i].parentElement.classList.add('open');
}
});
}
The following code expands the sub menus when a click occurs on navbar-toggle
and it change the aria-expanded to the correct value based on the open/close state of sub menu
function opensubmenus() {
if ($(window).width() < 768) {
$("#top-navbar-collapse li").addClass('open');
$("#top-navbar-collapse li a").attr('aria-expanded','true');
}else{
$("#top-navbar-collapse li").removeClass('open');
$("#top-navbar-collapse li a").attr('aria-expanded','false');
}
}
$('#top-menu .navbar-toggle').click(function(){
setTimeout(opensubmenus, 100);
});
$(window).resize(opensubmenus);
opensubmenus();
Thanks to #abelito for his hint
Straight code after tinkering for 10 minutes. I definitely don't recommend this as a final answer, but this will get you on the track towards one way of accomplishing it:
function delayedSubmenuOpen() { setTimeout(openAllSubmenus, 100); }
function openAllSubmenus() {
var eles = document.getElementsByClassName("dropdown-toggle");
for (i = 0; i < eles.length; i++) {
eles.item(i).parentElement.className += " open";
}
}
var navigationHamburger = document.getElementsByClassName("navbar-toggle").item(0);
navigationHamburger.addEventListener("click", delayedSubmenuOpen);
I would definitely replace all of these with cross-browser compliant jQuery calls, as I did it in straight javascript and only tested in my browser on Chrome.
I'd also look into just editing the CSS instead rather than relying on javascript to do this -- maybe on page load, make a renamed copy of the ".open" class and add it to all elements with the classname "menu-item-has-children" -- this way it can't be toggled off by the javascript. Sounds like you may have tried this but definitely worth looking into rather than depending on some hokey JS.
Add this to you css
#media only screen and (max-width: 768px) {
.megamenu .dropdown ul.dropdown-menu {
display: block;
position: static;
float: none;
width: auto;
margin-top: 0;
background-color: transparent;
border: 0;
-webkit-box-shadow: none;
box-shadow: none;
}
.navbar-fixed-top .navbar-collapse {
background-color: rgba(0, 0, 0, 0.7)!important;
}
}
And change the function to look like this
function opensubmenus() {
if (jQuery(window).width() < 768) {
jQuery("#top-navbar-collapse").addClass('in');
} else {
jQuery("#top-navbar-collapse").removeClass('in');
}
}
jQuery(window).resize(opensubmenus);
opensubmenus();

Printing string that was truncated by Angular filter

In my Angular app, I have a built a view with the specific purpose of showing data in a printer-friendly manner.
However, I have also added the option to show more/less text with an Angular filter that cuts strings to a pre-defined number of characters and lets the user see the rest by clicking a button, like so: {{elem | cut:!show[$index]:true:max}}.
Here is the complete filter:
angular.module('pear').filter('cut', function () {
return function (value, enabled, wordwise, max, tail) {
if (!value) return '';
max = parseInt(max, 10);
if (!max) return value;
if(!enabled) return value;
if (value.length <= max) return value;
value = value.toString().substr(0, max);
if (wordwise) {
var lastspace = value.lastIndexOf(' ');
if (lastspace != -1) {
value = value.substr(0, lastspace);
}
}
return value + (tail || '...');
};
});
And this is how it ends up looking:
Is there a way to disable this filter for the print view, or somehow force a css condition with #media print that will display the full text?
For reference, this is how I am currently using the filter in my template, in case a different markup approach is needed:
<div class="content"> {{elem | cut:!show[$index]:true:max}}
<span ng-show="!show[$index] && elem.length > max" class="show-more" ng-click="toggleShow($index)">more</span>
<span ng-show="show[$index]" class="show-more no-print" ng-click="toggleShow($index)">less</span>
</div>
Check working demo: JSFiddle.
Add the following code to your controller. The logic is: before your print, disable the filter. And re-enable it after printing. Use window.matchMedia to detect whether it is printing.
Note: be careful about the browser compatibility (IE>=10, Opera>=12.1, etc.)
The advantage of this solution is that: you can control the view not only through CSS. Actually you can design your view for printing specifically. Of course, after printing you need to restore the previous status.
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener(function (mql) {
$scope.$apply(function () {
var isPrinting = mql.matches;
$scope.enabled = !isPrinting;
});
});
}
I ended up using #media print in my sass file, and creating a couple of classes that will help me forcefully hide or show specific elements as I see fit, like so:
#media print {
.no-print, .no-print * {
display: none !important;
}
.force-print, .force-print * {
display: block !important;
}
html, body, .main-content {
margin: 0;
padding: 0;
width: 100%;
}
}
I've used them in my template by creating two <span> elements, one with the class .no-print, which includes the truncated string, and another one with the classes .force-print and hidden, which includes the element without any filter.
<div class="content">
<span class="no-print">{{elem | cut:!show[$index]:true:max}}</span>
<span class="hidden force-print">{{elem}}</span>
</div>
When printing, .force-print will override Bootstrap's hidden class, showing an element that is otherwise invisible to the user.

Disable/Enable CSS on webpage using Javascript

According to This page I was able to remove all the CSS preloaded and added on the webpage using that. I wanted to implement a button system where "onclick" = enable/disable webpage CSS even the ones pre-loaded by my web-host. I would like to eliminate the style tags to prevent lags for my website users. I prefer using the script that I have linked above unless there is another alternative that works better. Is it possible to enable CSS onclick the same button to disable? If not is it possible, can it be done with this quick? example with the preferred script below:
if (disable) {
style = "disable";
} else {
location.reload();
}
PREFERRED SCRIPT:
function removeStyles(el) {
el.removeAttribute('style');
if(el.childNodes.length > 0) {
for(var child in el.childNodes) {
/* filter element nodes only */
if(el.childNodes[child].nodeType == 1)
removeStyles(el.childNodes[child]);
}
}
}
removeStyles(document.body);
What about a different aproach?
Add initially a class to a body called 'styled' for example
<body class="styled">
use it as a main selector in your css definitions
<style>
.styled a { ... }
.styled h1 { .... }
</style>
then an example jquery script to toggle the class:
<script>
$(function() {
$('#myswitch').click(function() {
$('body').toggleClass('styled');
});
});
</script>
when class is present, the page will be styled, when absent there will be no styling.
Of coures there could be better aproach, but this is the first thing which pops up in my mind
To remove all style on an element, you could do
function removeStyles(el) {
el.style = {};
}
If you want to enable/disable the CSS on the page, then the goal is not to merely remove all the styles on the page, but you will need to save them somewhere also so they can be recalled when the user re-clicks the button. I would recommend having jQuery to help you with this, and it could be done the following way:
var style_nodes = $('link[rel="stylesheet"], style');
style_nodes.remove();
$('*').each(function(num, obj) {
var style_string = $(obj).attr("style");
if (style_string) {
$(obj).data("style-string", style_string);
$(obj).attr("style", "");
}
});
Now you've saved the stylesheets and style DOM nodes inside of style_nodes, and the actual style attribute inside of a jQuery data attribute for that specific DOM node. When you click to add the CSS back to the page, you can do the following:
$('head').append(style_nodes);
$('*').each(function(num, obj) {
if ($(obj).data("style-string"))
$(obj).attr("style", $(obj).data("style-string"));
});
Check out this JS Fiddle I put together to demonstrate it:
https://jsfiddle.net/5krLn3w1/
Uses JQuery, but I'm sure most frameworks should give you similar functionality.
HTML:
<h1>Hello World</h1>
Turn off CSS
Turn on CSS
JS:
$(document).ready(function() {
$('a#turn_off').click(function(evt) {
evt.preventDefault();
var css = $('head').find('style[type="text/css"]').add('link[rel="stylesheet"]');
$('head').data('css', css);
css.remove();
});
$('a#turn_on').click(function(evt) {
evt.preventDefault();
var css = $('head').data('css');
console.info(css);
if (css) {
$('head').append(css);
}
});
});
CSS:
body {
color: #00F;
}
h1 {
font-size: 50px;
}

Reveal DIV based on URL, rehide based on anchor change

I'm currently showing and hiding a DIV based on the URL, by setting the body class name:
if(location.href.match("#!/~/product/category")){document.body.className+="show";}
And the DIV changes accordingly:
#showcase {
display: none;
}
body.show #showcase {
display: block;
}
Works great. Up until the AJAX cart changes the URL to #!/~/cart
I would like to make the DIV disappear everywhere EXCEPT the product/category URL, but I'm stuck.
This should be done with an if-else as #HamzaKubba said.
If you'd still like to apply the relevant styles with your css you can do this:
// just because I have no idea what your body class usually is
// you might just hard-code something else you want
var defaultBodyClass = document.body.className
if (location.href.match('#!/~/product/category')){
document.body.className = defaultBodyClass + ' show';
} else {
document.body.className = defaultBodyClass;
}

Categories

Resources