#media for Javascript text animation 2 - javascript

I have a Javascript text animation on my homepage and would like to insert an #media command using internal CSS to the homepage scripted text 'we do content'. I am trying to make this so when viewed on an iphone screen size the text changes to be displayed on three separate lines, rather than one. ie:
We
Do
Content
I'm at a bit of a loss what to values/ elements to call out for the #media command. I think maybe /br may be one of the three things needed but otherwise a bit stumped! It's worth noting this is a wordpress theme and "bk_big_text" is a theme specific text id. Any help greatly appreciated!
<head>
<style type="text/css">
#media only screen and (max-width: 480px) {
.changer (not sure if this is right?) {
what goes here?: /br !important;
}
}
</style>
</head>
<p style="text-align: left;">[bk_big_text size="120" ]
<script type="text/javascript">// <![CDATA[
var words = ["content","brand", "identity", "digital", "fresh","integrated", "interactive", "creative", "powerful","witty", "editorial"];
var i = 0;
var text = "We do content";
function _getChangedText() {
i = (i + 1) % words.length;
console.log(words[i]);
console.log(i);
return text.replace(/content/, words[i]);
}
function _changeText() {
var txt = _getChangedText();
console.log(txt);
document.getElementById("changer").innerHTML = txt;
}
setInterval("_changeText()", 2200);
// ]]></script>
<span id="changer" style="color: #ffffff;">We do content</span>[/bk_big_text]

Related

How can I force a matching window.matchMedia to execute on page load?

I noticed a difference between css media query definition and the javascript window.matchMedia media query definition:
The css rules are apllied initially to a loaded page.
The rules defined in javascript are not executed after the page load, but only after a new condition is entered.
An example:
I have two different pages with equivalent media query definitions, the first one defined in css and the second one defined in javascript:
the css version (defined in a style element):
#media (min-width: 401px) and (max-width: 600px) { body {background-color: red; } }
#media (min-width: 601px) and (max-width: 800px) { body {background-color: blue; } }
the javascript version (defined either globally or in a function called after body onload):
window.matchMedia("(min-width: 401px) and (max-width: 600px)")
.addListener(function(e) {
if (e.matches) {
document.body.style.background = "red";
}
});
window.matchMedia("(min-width: 601px) and (max-width: 800px)")
.addListener(function(e) {
if (e.matches) {
document.body.style.background = "blue";
}
});
When I load a page and the window is 700 px wide
the css version page is blue
the javascript version is white and changes its state only after a new condition is met, i.e. the window is sized below 601 px.
How can I force a matching window.matchMedia to execute on page load?
To fire a matchMedia on load, you could do like this instead (with a somewhat cleaner code base).
Stack snippet
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener("DOMContentLoaded", function(e) {
// medias (as an array to make it a little easier to manage)
var mqls = [
window.matchMedia("(max-width: 400px)"),
window.matchMedia("(min-width: 401px) and (max-width: 600px)"),
window.matchMedia("(min-width: 601px) and (max-width: 800px)"),
window.matchMedia("(min-width: 801px)")
]
// event listeners
for (var i=0; i<mqls.length; i++){
mqls[i].addListener(mqh)
}
// matches methods
function mqh(){
if (mqls[0].matches) {
console.log("CALLBACK (max-width: 400px)");
document.body.style.background = "green";
} else if (mqls[1].matches) {
console.log("CALLBACK (max-width: 600px)");
document.body.style.background = "red";
} else if (mqls[2].matches) {
console.log("CALLBACK (max-width: 800px)");
document.body.style.background = "blue";
} else if (mqls[3].matches) {
console.log("CALLBACK (min-width: 801px)");
document.body.style.background = "gray";
}
console.log("window.innerWidth: " + window.innerWidth);
}
// call once on load
mqh();
});
</script>
</head>
<body>
</body>
</html>
Org. src: http://www.javascriptkit.com/javatutors/matchmediamultiple.shtml
A callback function bound to window.matchMedia is not called on page load.
A solution to the problem would be:
to define a function in which the media queries are explicitly checked via if(window.matchMedia("...").matches){
to call that function on page load via <body onload
to call that function on resize via window.onresize
I was facing the same problem today, and I've used the following solution inspired by Nathan:
const gate = 800
function listener(
matches,
) {
document.getElementById('tag').innerHTML = matches ? 'wider' : 'narrower'
}
window.onload=() => {
const match = window.matchMedia(`(min-width: ${gate}px)`)
match.addListener(e => listener(e.matches))
listener(match.matches)
}
<h1>
This window is
<span id='tag'></span>
than 800px
</h1>
The core concept is to run your listener function once with MediaQueryList.matches passed as a parameter.
And if someone is trying to achieve this with a framework, do remember to register and trigger the listener during the component mount event.

How to manipulate the screen width via js?

I'll try to explain my use case here. In my site I have a break point for desktop view, and break point for tablet view (which is more compact). I'm trying to add a function to allow seeing the tablet view when browsing from desktop, cause some members prefer the compact design in their desktop as well.
For doing that, I figured I would need to trick the '#media(max-width:X)' query. I'm looking for a JS code that can manipulate the screen width value, so when the browser calculates max-width, it would be against a value that I specified.
One thing to note, this is suppose to work on desktop browsers, so the meta viewport can't be used here.
One solution is to apply a specific class (e.g: .tablet) to the body.
<body class="tablet"></body>
In your CSS:
#media screen and (/* your query */) {
.tablet .my-class {
/* tablet specific stuff */
}
}
You could then remove the .tablet class and replace it with .desktop via JavaScript
var body = document.body;
var switchToDesktop = function() {
body.className = body.className.replace('tablet', 'desktop');
}
var switchToTablet = function() {
body.className = body.className.replace('desktop', 'tablet');
}
var toggleView = function() {
(body.className.indexOf("tablet") > -1) ?
switchToDesktop() :
switchToTablet();
}
If you are using SASS or LESS, you can nest the tablet-specific styles.
#media screen and (/* your query */) {
.tablet {
h1 {
/* tablet specific h1 */
}
.my-div {
color: red;
}
/* etc... */
}
}

Get CSS style sheet property using Javascript (and media query)

Generally, when I'm not using JQuery, I use the following utility function to get properties from a stylesheet using Javascript. This works nicely to get values from CSS style sheets:
<!DOCTYPE html>
<html>
<head>
<style>
.foo {
color: red;
}
</style>
<script type = "text/javascript">
function getcss(selector, property)
{
var len = document.styleSheets.length;
for (var idx = 0; idx < len; ++idx)
{
var sheet = document.styleSheets && document.styleSheets[idx];
if (sheet)
{
var r = sheet.rules ? sheet.rules : sheet.cssRules;
if (r)
{
var i = r.length;
while (i--)
{
if (r[i].selectorText && r[i].selectorText.toLowerCase() === selector.toLowerCase())
{
return (r[i].style[property]);
}
}
}
}
}
return null;
}
function exec()
{
alert(getcss(".foo", "color"));
}
</script>
</head>
<body onload = "exec()">
</body>
</html>
The problem is that it doesn't take into account media queries. If I replace the <style> section of the above code with:
.foo {
color: red;
}
#media screen and (max-width: 600px) {
.foo {
color: green;
}
}
...it still outputs red if I shrink the window to smaller than 600px.
Is this correct WC3 behavior? Or is this a browser bug? (I'm testing with Firefox 12 on Ubuntu).
Is there anyway to correct this, so that Javascript "sees" the correct style sheet as required by the media query?
Your code is simply looking through the rules of a stylesheet, and only the first list of rules at that. The rules are a object representation of the stylesheet -- they don't change just because you've resized your browser.
In your code you're never going to see the green rul because you're only iterating over the items in the first CSSRuleList. The trick here is you need to recursively loop over any additional rules that are CSSMediaRule and contain their own CSSRuleList. For you, the sheet.rules contains both a single CSSSyleRule (which is your .foo { color:red; }) and a CSSMediaRule (which is your media query). That second rule then its own CSSRuleList, which you can traverse to find your green color.
In this case, here's where your data lies:
// Assuming sheet 0 is your stylesheet above
var sheet = document.styleSheets[0];
// First rule is ".foo { color: red; }"
console.log(sheet.cssRules[0].cssText);
// Second Rule is your "#media" and its first rule is ".foo { color: green; }"
console.log(sheet.cssRules[1].cssRules[0].cssText);

How to find the height of an element that is not displayed

I'm writing a webpage that has a table with rows that slide open and closed. Initially, some rows are closed (display: none), and I want them to slide open. Setting the height and using overflow: hidden doesn't work on table rows, so I'm changing the height of a div inside the table.
This works. The only problem is that I need to know the height of the div before I slide it open, which seems to be impossible. One solution I can think of is to load the page with the rows show, then iterate through them, storing their heights and hiding them. I don't like this solution because the page would jump around when loading.
Here's a simple, runnable example of my problem.
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
table, td {border: 1px solid black;}
#lower_row {display: none;}
#lower_div {overflow: hidden;}
</style>
<script type="text/javascript">
function toggleLower() {
lowerRow = document.getElementById("lower_row");
lowerDiv = document.getElementById("lower_div");
if (getStyle(lowerRow, "display") == "none") {
lowerRow.style.display = "table-row";
}
else {
lowerRow.style.display = "none";
}
showHeight();
}
function showHeight() {
lowerDiv = document.getElementById("lower_div");
document.getElementById("info").innerHTML = getStyle(lowerDiv, "height");
}
// Return a style atribute of an element.
// J/S Pro Techniques p136
function getStyle(elem, name) {
if (elem.style[name]) {
return elem.style[name];
}
else if (elem.currentStyle) {
return elem.currentStyle[name];
}
else if (document.defaultView && document.defaultView.getComputedStyle) {
name = name.replace(/([A-Z])/g, "-$1");
name = name.toLowerCase();
s = document.defaultView.getComputedStyle(elem, "");
return s && s.getPropertyValue(name);
}
else {
return null;
}
}
</script>
</head>
<body onload="showHeight()">
<p>The height the lower row is currently <span id="info"></span></p>
<table>
<tr id="upper_row" onclick="toggleLower()"><td><p>Click me to toggle the next row.</p></td></tr>
<tr id="lower_row"><td><div id="lower_div"><p>Peekaboo!</p></div></td></tr>
</table>
</body>
</html>
Edit 1:
One proposed solution is to move the div off the page. I can't get that to work, and I think it would have the wrong height because its height depends on the width of the table.
I'm working on the solution of using visibility:hidden, but it has problems. It still takes up a small amount of space, and the reported height is wrong. Here's an example of that solution:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
table {width: 250px;}
table, td {border: 1px solid black;}
#lower_row {position: absolute; visibility: hidden}
#lower_div {overflow: hidden;}
</style>
<script type="text/javascript">
function toggleLower() {
lowerRow = document.getElementById("lower_row");
lowerDiv = document.getElementById("lower_div");
if (getStyle(lowerRow, "visibility") == "hidden") {
lowerRow.style.visibility = "visible";
lowerRow.style.position = "static";
}
else {
lowerRow.style.visibility = "hidden";
lowerRow.style.position = "absolute";
}
showHeight();
}
function showHeight() {
lowerDiv = document.getElementById("lower_div");
document.getElementById("info").innerHTML = getStyle(lowerDiv, "height");
}
// Return a style atribute of an element.
// J/S Pro Techniques p136
function getStyle(elem, name) {
if (elem.style[name]) {
return elem.style[name];
}
else if (elem.currentStyle) {
return elem.currentStyle[name];
}
else if (document.defaultView && document.defaultView.getComputedStyle) {
name = name.replace(/([A-Z])/g, "-$1");
name = name.toLowerCase();
s = document.defaultView.getComputedStyle(elem, "");
return s && s.getPropertyValue(name);
}
else {
return null;
}
}
</script>
</head>
<body onload="showHeight()">
<p>The height the lower row is currently <span id="info"></span></p>
<table>
<tr id="upper_row" onclick="toggleLower()"><td><p>Click me to toggle the next row.</p></td></tr>
<tr id="lower_row"><td><div id="lower_div"><p>This is some long text. This is some long text. This is some long text. This is some long text.</p></div></td></tr>
</table>
</body>
</html>
Edit 2: The Solution
Paul's answer is the solution to my question: how to find the height of an element that is not displayed. However, it wouldn't work for my problem. On my site, the height of the div depends on its width, which depends on the td's width, which depends on the state of the other rows and the width of table, which depends on the width of the page. This means that, even if I pre-compute the height, the value would be wrong as soon as someone expands another row or changes the window size. Also, copying the table and keeping all of these constraints would be near-impossible.
However, I have found a solution. When the user clicks to expand a row, my site would do the following steps in order:
Set the div.style.height to 1px.
Set the row.style.display to table-row.
Store the value of div.scrollHeight.
Run the scroll animation, stopping at div.scrollHeight.
After the animation, set div.style.height to auto.
div.scrollHeight gives the height of the div's contents, including its overflow. It doesn't work when the div is not displayed, but that's not a problem for my application. Here's a sample of the code in action. (Again, I don't include the code for the scroll animation because it would be too long.)
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
table, td {border: 1px solid black;}
#lower_row {display: none;}
#lower_div {overflow: hidden;}
</style>
<script type="text/javascript">
function toggleLower() {
var lowerRow = document.getElementById("lower_row");
var lowerDiv = document.getElementById("lower_div");
if (getStyle(lowerRow, "display") == "none") {
lowerDiv.style.height = "0px";
lowerRow.style.display = "table-row";
showHeight();
lowerDiv.style.height = "auto";
}
else {
lowerDiv.style.height = "0px";
showHeight();
lowerRow.style.display = "none";
}
}
function showHeight() {
var lowerDiv = document.getElementById("lower_div");
document.getElementById("info").innerHTML = lowerDiv.scrollHeight;
}
// Return a style atribute of an element.
// J/S Pro Techniques p136
function getStyle(elem, name) {
if (elem.style[name]) {
return elem.style[name];
}
else if (elem.currentStyle) {
return elem.currentStyle[name];
}
else if (document.defaultView && document.defaultView.getComputedStyle) {
name = name.replace(/([A-Z])/g, "-$1");
name = name.toLowerCase();
s = document.defaultView.getComputedStyle(elem, "");
return s && s.getPropertyValue(name);
}
else {
return null;
}
}
</script>
</head>
<body>
<p>The height the lower row is currently <span id="info">...</span></p>
<table>
<tr id="upper_row" onclick="toggleLower()"><td><p>Click me to toggle the next row.</p></td></tr>
<tr id="lower_row"><td><div id="lower_div"><p>
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
This is some long text. This is some long text. This is some long text. This is some long text.
</p></div></td></tr>
</table>
</body>
</html>
You can copy the div with the content and put it to the body with absolute positioning top:-10000; left:-10000; so it will be outside of visible area, then you can calculate the height and remove clone from the DOM.
UPDATE
Alternatively, in case when you add elements in dynamic way, you can set it to display:block, position:absolute, and visibility:hidden - but you have to make sure that it will not change position of any element on the page. visibility:hidden - will not show the element, but calculate it's dimensions (in contrast to display: none )
UPDATE
In your particular case, your parent have an influence on child's dimensions, so you need to clone your element into "similar" parent which is outside of visible area. By saying "similar" I mean it should have the same dimensions, but in general - styles and everything what is related to it's size:
var wrapper = $('<div></div>').appendTo('body').css('display', 'block').css('position', 'absolute').css('top', -10000).css('left', -10000).css('width', $('table').css('width'));
var clone = $('#lower_div').clone().appendTo(wrapper);
document.getElementById("info").innerHTML = clone.height();
Here is working jsfiddle for you.
You can do this by showing the element, capturing the height, and then re-hiding the element all before the browser repaints the screen. So, the user will never notice anything happening. I do this for a packaged used to slide open & closed elements with dynamic heights:
https://github.com/alexmacarthur/slide-element/blob/master/src/index.ts
In super simple code:
<div id="element" style="display: none;">hello</div>
<script>
const element = document.getElementById('element');
element.style.display = "";
const elementHeight = element.clientHeight;
element.style.display = "none";
console.log(elementHeight); // whatever the rendered height is.
</script>

Printing just an iFrame

I'm working on a case resolution system, and am currently using a jquery colorbox to display a list of open tasks to the user. Users want to be able to print this list, and I guess you can do it from within the page itself by adding a JavaScript link that triggers window.print from within the iframe. However, I've also got to account for users possibly selecting print from the browser's menu. In that case, if the colorbox is open, I just want to print its contents and not the overlying page.
Is it possible to hide everything except for the iframed content using a print media CSS file? If so, how can this be achieved? Failing that, I'll need to resort to JavaScript, so would achieving the effect in JavaScript be possible?
// suppose that this is how your iframe look like <iframe id='print-iframe' name='print-frame-name'></iframe>
// this is how you do it using jquery:
$("#print-iframe").get(0).contentWindow.print();
// and this is how you do it using native javascript:
document.getElementById("print-iframe").contentWindow.print();
In case the pure CSS solution will fail (didn't work for me but maybe I just missed something) you can have combined solution of CSS and JavaScript. First have this:
<style type="text/css" media="print">
.hideonprint { display:none; }
</style>
Then such JavaScript will cause all content to be hidden when printing, except your frame:
<script type="text/javascript">
window.onbeforeprint = function WindowPrint(evt) {
for (var i = 0; i < document.body.childNodes.length; i++) {
var curNode = document.body.childNodes[i];
if (typeof curNode.className != "undefined") {
var curClassName = curNode.className || "";
if (curClassName.indexOf("hideonprint") < 0) {
var newClassName = "";
if (curClassName.length > 0)
newClassName += curClassName + " ";
newClassName += "hideonprint";
curNode.setAttribute("original_class", curClassName);
curNode.className = newClassName;
}
}
}
document.getElementById("myframe").className = document.getElementById("myframe").getAttribute("original_class");
}
</script>
This also assume the iframe is direct child of the body otherwise it won't work either.
I have found a method that works to print just the IFrame's content even if the client uses the browser's print menu item, but I couldn't tell you why that is. The trick is to set the focus to the IFrame before printing. The print stylesheet is needed too, although the javascript seems to be what is happening when the user prints from the menu. You need both parts for it to work. It prints the entire document, even if it is larger than the IFrame! I have successfully tested it in IE8, Firefox 5 and 6 and Safari 3.2.
I use this script as a handler for an onclick event for a button or "print me" link:
<script type="text/javascript" language=JavaScript>
function CheckIsIE()
{
if (navigator.appName.toUpperCase() == 'MICROSOFT INTERNET EXPLORER')
{ return true; }
else
{ return false; }
}
function PrintThisPage()
{
if (CheckIsIE() == true)
{
document.content.focus();
document.content.print();
}
else
{
window.frames['content'].focus();
window.frames['content'].print();
}
}
</script>
The IFrame in question is named and id'd content. My button is in a div called print_iframe The browser sniffing is essential!
Then I use a print only stylesheet linked in like this:
<link href="/styles/print.css" rel="stylesheet" type="text/css" media="print" />
#charset "utf-8";
/* CSS Document */
body { background:none; }
#left { display:none; }
#main img { display:none; }
#banner
{
display:none;
margin-top:0px;
padding:0px;
}
#main
{
margin-top:0px;
padding:0px;
}
#print_iframe
{
display:none;
}
This could work if the iframe is a direct child of body
<style type="text/css" media="print">
body *{display:none}
iframe{display:block}
</style>

Categories