In my AngularJS application I am attempting to add a Facebook Native Web Ad to one of my views. I followed the steps outlined in the documentation to generate the necessary HTML snippet and added this to my view.
My application is using ui-router to resolve routes. When visiting the route/view containing this code snippet the FB ad is not displayed and neither of the callbacks are invoked (onAdLoaded or onAdError).
Facebook Generated HTML Snippet (added to view):
<div style="display:none; position: relative;">
<iframe style="display:none;"></iframe>
<script type="text/javascript">
var data = {
placementid: 'xxxxxxxxxxx_xxxxxxxx',
format: 'native',
testmode: true,
onAdLoaded: function (element) {
console.log('Audience Network ad loaded');
element.style.display = 'block';
},
onAdError: function (errorCode, errorMessage) {
console.log('Audience Network error (' + errorCode + ') ' + errorMessage);
}
};
(function (w, l, d, t) {
var a = t();
var b = d.currentScript || (function () {
var c = d.getElementsByTagName('script');
return c[c.length - 1];
})();
var e = b.parentElement;
e.dataset.placementid = data.placementid;
var f = function (v) {
try {
return v.document.referrer;
} catch (e) {
}
return '';
};
var g = function (h) {
var i = h.indexOf('/', h.indexOf('://') + 3);
if (i === -1) {
return h;
}
return h.substring(0, i);
};
var j = [l.href];
var k = false;
var m = false;
if (w !== w.parent) {
var n;
var o = w;
while (o !== n) {
var h;
try {
m = m || (o.$sf && o.$sf.ext);
h = o.location.href;
} catch (e) {
k = true;
}
j.push(h || f(n));
n = o;
o = o.parent;
}
}
var p = l.ancestorOrigins;
if (p) {
if (p.length > 0) {
data.domain = p[p.length - 1];
} else {
data.domain = g(j[j.length - 1]);
}
}
data.url = j[j.length - 1];
data.channel = g(j[0]);
data.width = screen.width;
data.height = screen.height;
data.pixelratio = w.devicePixelRatio;
data.placementindex = w.ADNW && w.ADNW.Ads ? w.ADNW.Ads.length : 0;
data.crossdomain = k;
data.safeframe = !!m;
var q = {};
q.iframe = e.firstElementChild;
var r = 'https://www.facebook.com/audiencenetwork/web/?sdk=5.3';
for (var s in data) {
q[s] = data[s];
if (typeof(data[s]) !== 'function') {
r += '&' + s + '=' + encodeURIComponent(data[s]);
}
}
q.iframe.src = r;
q.tagJsInitTime = a;
q.rootElement = e;
q.events = [];
w.addEventListener('message', function (u) {
if (u.source !== q.iframe.contentWindow) {
return;
}
u.data.receivedTimestamp = t();
if (this.sdkEventHandler) {
this.sdkEventHandler(u.data);
} else {
this.events.push(u.data);
}
}.bind(q), false);
q.tagJsIframeAppendedTime = t();
w.ADNW = w.ADNW || {};
w.ADNW.Ads = w.ADNW.Ads || [];
w.ADNW.Ads.push(q);
w.ADNW.init && w.ADNW.init(q);
})(window, location, document, Date.now || function () {
return +new Date;
});
</script>
<script type="text/javascript" src="https://connect.facebook.net/en_US/fbadnw.js" async></script>
<style>
.thirdPartyRoot {
background-color: white;
color: #444;
border: 1px solid #ccc;
border-left: 0;
border-right: 0;
font-family: sans-serif;
font-size: 14px;
line-height: 16px;
width: 320px;
text-align: left;
position: relative;
}
.thirdPartyMediaClass {
width: 320px;
height: 168px;
margin: 12px 0;
}
.thirdPartySubtitleClass {
font-size: 18px;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 16px;
-webkit-box-orient: vertical;
}
.thirdPartyTitleClass {
padding-right: 12px;
line-height: 18px;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 36px;
-webkit-box-orient: vertical;
}
.thirdPartyCallToActionClass {
background-color: #416BC4;
color: white;
border-radius: 4px;
padding: 6px 20px;
float: right;
text-align: center;
text-transform: uppercase;
font-size: 11px;
}
.fbDefaultNativeAdWrapper {
font-size: 12px;
line-height: 14px;
margin: 12px 0;
height: 36px;
vertical-align: top;
}
</style>
<div class="thirdPartyRoot">
<a class="fbAdLink">
<div class="fbAdMedia thirdPartyMediaClass"></div>
<div class="fbAdSubtitle thirdPartySubtitleClass"></div>
<div class="fbDefaultNativeAdWrapper">
<div class="fbAdCallToAction thirdPartyCallToActionClass"></div>
<div class="fbAdTitle thirdPartyTitleClass"></div>
</div>
</a>
</div>
</div>
I noticed that the Facebook Audience Network JS is loaded asynchronously and suspected that I might have a race condition causing the issue.
<script type="text/javascript" src="https://connect.facebook.net/en_US/fbadnw.js" async></script>
To test this, I've moved the FB code snippet out of my view and into my SPA index.html. The ad appears as expected. What callback does the fbadnw.js script call once the script is loaded? Is the closure within the FB generated code being invoked before fbadnw.js is loaded perhaps?
index.html (works)
<!DOCTYPE html>
<html ng-app="kcl-app">
<head>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title ng-bind="pageTitle"></title>
</head>
<body>
<!-- ui-router view -->
<div ui-view></div>
<!-- FB Begin -->
<div class="fb-native">
<div style="display:none; position: relative;">
<iframe style="display:none;"></iframe>
<script type="text/javascript">
var data = {
placementid: 'xxxxxxxxxxx_xxxxxxxx',
format: 'native',
testmode: true,
onAdLoaded: function (element) {
console.log('Audience Network ad loaded');
element.style.display = 'block';
},
onAdError: function (errorCode, errorMessage) {
console.log('Audience Network error (' + errorCode + ') ' + errorMessage);
}
};
(function (w, l, d, t) {
var a = t();
var b = d.currentScript || (function () {
var c = d.getElementsByTagName('script');
return c[c.length - 1];
})();
var e = b.parentElement;
e.dataset.placementid = data.placementid;
var f = function (v) {
try {
return v.document.referrer;
} catch (e) {
}
return '';
};
var g = function (h) {
var i = h.indexOf('/', h.indexOf('://') + 3);
if (i === -1) {
return h;
}
return h.substring(0, i);
};
var j = [l.href];
var k = false;
var m = false;
if (w !== w.parent) {
var n;
var o = w;
while (o !== n) {
var h;
try {
m = m || (o.$sf && o.$sf.ext);
h = o.location.href;
} catch (e) {
k = true;
}
j.push(h || f(n));
n = o;
o = o.parent;
}
}
var p = l.ancestorOrigins;
if (p) {
if (p.length > 0) {
data.domain = p[p.length - 1];
} else {
data.domain = g(j[j.length - 1]);
}
}
data.url = j[j.length - 1];
data.channel = g(j[0]);
data.width = screen.width;
data.height = screen.height;
data.pixelratio = w.devicePixelRatio;
data.placementindex = w.ADNW && w.ADNW.Ads ? w.ADNW.Ads.length : 0;
data.crossdomain = k;
data.safeframe = !!m;
var q = {};
q.iframe = e.firstElementChild;
var r = 'https://www.facebook.com/audiencenetwork/web/?sdk=5.3';
for (var s in data) {
q[s] = data[s];
if (typeof(data[s]) !== 'function') {
r += '&' + s + '=' + encodeURIComponent(data[s]);
}
}
q.iframe.src = r;
q.tagJsInitTime = a;
q.rootElement = e;
q.events = [];
w.addEventListener('message', function (u) {
if (u.source !== q.iframe.contentWindow) {
return;
}
u.data.receivedTimestamp = t();
if (this.sdkEventHandler) {
this.sdkEventHandler(u.data);
} else {
this.events.push(u.data);
}
}.bind(q), false);
q.tagJsIframeAppendedTime = t();
w.ADNW = w.ADNW || {};
w.ADNW.Ads = w.ADNW.Ads || [];
w.ADNW.Ads.push(q);
w.ADNW.init && w.ADNW.init(q);
})(window, location, document, Date.now || function () {
return +new Date;
});
</script>
<script type="text/javascript" src="https://connect.facebook.net/en_US/fbadnw.js" async></script>
<style>
.thirdPartyRoot {
background-color: white;
color: #444;
border: 1px solid #ccc;
border-left: 0;
border-right: 0;
font-family: sans-serif;
font-size: 14px;
line-height: 16px;
width: 320px;
text-align: left;
position: relative;
}
.thirdPartyMediaClass {
width: 320px;
height: 168px;
margin: 12px 0;
}
.thirdPartySubtitleClass {
font-size: 18px;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 16px;
-webkit-box-orient: vertical;
}
.thirdPartyTitleClass {
padding-right: 12px;
line-height: 18px;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 36px;
-webkit-box-orient: vertical;
}
.thirdPartyCallToActionClass {
background-color: #416BC4;
color: white;
border-radius: 4px;
padding: 6px 20px;
float: right;
text-align: center;
text-transform: uppercase;
font-size: 11px;
}
.fbDefaultNativeAdWrapper {
font-size: 12px;
line-height: 14px;
margin: 12px 0;
height: 36px;
vertical-align: top;
}
</style>
<div class="thirdPartyRoot">
<a class="fbAdLink">
<div class="fbAdMedia thirdPartyMediaClass"></div>
<div class="fbAdSubtitle thirdPartySubtitleClass"></div>
<div class="fbDefaultNativeAdWrapper">
<div class="fbAdCallToAction thirdPartyCallToActionClass"></div>
<div class="fbAdTitle thirdPartyTitleClass"></div>
</div>
</a>
</div>
</div>
</div>
<!-- FB End -->
</body>
</html>
I was able to resolve this issue by editing the boilerplate code provided by FB. In a nutshell, the provided closure (minified) attempts to:
Locate the iframe element where the Ad will be rendered and set it's
src and other attributes.
Attach an event handler to listen for post messages "message".
Initialize the Ad with FB Audience Network (ADNW.init())
My problem was with the assumptions this code makes in Step 1.
var b = d.currentScript || (function() {
var c = d.getElementsByTagName('script');
return c[c.length - 1];
})();
var e = b.parentElement;
The above code is attempting to locate this DIV.
<div style="display:none; position: relative;">
It does this by first trying to locate the parent of the last script element on the page. This is brittle and, in my case, did not work. The last script element added in my document was not the one this code expected.
I modified this code to explicitly select the correct element by ID.
Added an ID to containing DIV:
<div id="fb-ad-container" style="display:none; position: relative;">
Simplify the DOM parsing code (step 1) to select this DIV by ID:
var e = d.getElementById("fb-ad-container");
By selecting the correct element by ID I was able to alleviate the need to locate the current script element. The rest of the script ran as expected.
Related
I'm developing a sorting visualization website with my friend (https://tann1.github.io/SortingViz/). We both are fairly new to web development. We're currently facing this issue where if you were to zoom in and press play the canvas window shrinks every render or so. Similarly, if you zoom out it'll expand every render or so as well. You can test this bug out through the GitHub link I attached earlier. There's is definitely something that we're missing and we can't seem to identify it. Some help would be very appreciated.
// JS script goes here
let form = document.querySelector('#nBarsInput'); //# = reference to ID
let sortType = document.querySelector('#algoSelect');
let canvas = document.querySelector('.zdog-canvas');
let ctx = canvas.getContext('2d');
let nTextBox = document.getElementById('nTextBox');
let nSubmitButton = document.getElementById('nSubmit');
let replayButton = document.getElementById("replayButton");
let nVal = []; //stores the array that needs to be sorted
let nValUnsorted = [];
let sortSpeed = document.getElementById('sortSpeed');
let unsorted = '#fc90a9'//'#c9751a'
let sorted = '#c94260'
let sorting = '#83BCFF'
//let swapColor = 'red'
nSubmitButton.addEventListener('click', () => { // generate graph when submit button is clicked
generateNums(nTextBox.value);
startSort();
});
function enableInput() {
nSubmitButton.disabled = false;
replayButton.disabled = false;
nTextBox.disabled = false;
sortSpeed.disabled = false;
}
function disableInput() {
nSubmitButton.disabled = true;
replayButton.disabled = true;
nTextBox.disabled = true;
sortSpeed.disabled = true;
}
replayButton.addEventListener('click', () => {
nVal = [...nValUnsorted.map(arr => [...arr])];
//console.log(nValUnsorted);
startSort();
});
async function startSort() {
createCanvas(nTextBox.value);
disableInput();
switch (sortType.value) {
case 'Selection Sort': {
await selectionSort();
break;
}
case 'Insertion Sort': {
await insertionSort();
break;
}
default: {
alert("Invalid sort type");
break;
}
}
enableInput();
}
function generateNums(size) { // generates array of size ntextBox.value, populated with random numbers
nVal = []; // reset array
nValUnsorted = [];
for (let i = 0; i < size; ++i){
let randVal = Math.floor((Math.random() * 0.55 * canvas.height) + (0.40 * canvas.height));
nVal.push([randVal, unsorted]);
nValUnsorted.push([randVal, unsorted]);
}
//copy();
}
// do drawing stuff based on given array and N value
function createCanvas(size) {
let barWidth = (canvas.width/size);
let illo = new Zdog.Illustration({ //main parent object of
element: ".zdog-canvas",
centered: false
});
let rect = new Zdog.Rect({ // create first bar
addTo: illo,
width: barWidth * 0.95,
height: nVal[0][0],
color: nVal[0][1],
fill: true,
stroke: 4,
translate: {y: canvas.height - nVal[0][0] / 2, x : barWidth/2}});
illo.updateRenderGraph();
for(let i = 1; i < size; ++i){ // create rest of the bars by copying the first bar
rect.copy({
height: nVal[i][0],
color: nVal[i][1],
translate: {y: canvas.height - nVal[i][0]/ 2, x : (i * barWidth) + barWidth/2}
});
//nArr.push(rect);
illo.updateRenderGraph();
/* text setup*/
}
for (let i = 0; i < size; ++i) {
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.font = (0.50 * barWidth) + 'px monospace';
ctx.fillText(nVal[i][0], (i * barWidth) + (barWidth/2), 490);
}
}
async function selectionSort() {
let curr_pos = 0; // find the position with the lowest current value
let temp = 0; // need it for the swap
for (let i = 0; i < nTextBox.value - 1; ++i) {
curr_pos = i;
nVal[i][1] = sorting;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
for (let j = curr_pos + 1; j < nTextBox.value; ++j) { // finds the lowest relative value in the array
if (nVal[j][0] < nVal[curr_pos][0])
curr_pos = j;
}
//swap
temp = nVal[i];
nVal[i] = nVal[curr_pos];
nVal[curr_pos] = temp;
nVal[curr_pos][1] = sorting;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
nVal[i][1] = sorted;
if (i != curr_pos) {
nVal[curr_pos][1] = unsorted;
}
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
}
nVal[nTextBox.value - 1][1] = sorted;
createCanvas(nTextBox.value);
//console.log(nVal);
return new Promise((resolve, reject) => {resolve('');})
}
async function insertionSort() {
let val, j;
nVal[0][1] = sorted;
for (let i = 1; i < nTextBox.value; i++) {
val = nVal[i]; // current value to sort
val[1] = sorting; //nVal[i][1] = sorting;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
j = i; // only loop through "sorted" portion
while (j > 0 && val[0] < nVal[j-1][0]) { // keep going through sorted portion until correct position is found
nVal[j] = nVal[j-1]; // shift other values up
nVal[j][1] = sorted;
nVal[j-1] = val;
j--;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
}
nVal[j][1] = sorted;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
}
return new Promise((resolve) => {resolve('');})
}
function pause(ms) { // pause execution for a given duration in milliseconds
return new Promise(resolve => {
setTimeout(() => { resolve('') }, ms * 1000);
})
}
/* CSS goes here */
:root {
--pageColor: #FDB;
--buttonColor: #c94260;
--LabelColor: rgba(58, 44, 48, 0.959);
--textColor: #c94260;
--disabledColor: #fc90a9;
}
body {
background-color: var(--pageColor);
font-family: 'Patrick Hand', cursive;
font-size: 25px;
}
h1 {
font-family: 'Source Code Pro', monospace;
font-size: 60px;
color: white;
text-shadow: 2px 5px var(--textColor);
}
#nBarsInput {
display: flex;
flex-direction: column;
align-items: center;
border: 5px solid var(--buttonColor);
border-radius: 15px;
padding: 30px;
width: 1100px;
}
#textInput {
width: 980px;
display: flex;
justify-content: space-between;
}
.button {
background-color: var(--buttonColor);
border-radius: 3px;
border: none;
color: white;
padding: 5px 25px;
margin: 20px 5px;
font-family: 'Inter', sans-serif;
box-shadow: 0 5px #999;
}
.button:hover {
background-color: #83BCFF;
}
.button:active {
background-color: #83BCFF;
box-shadow: none;
transform: translateY(5px);
}
.button:disabled {
background-color: var(--disabledColor);
}
.inputTextBox {
padding: 5px 15px;
border: none;
border-radius: 25px;
}
.inputTextBox:focus {
outline: none;
}
.inputTextBox:disabled
{
}
label {
color: var(---LabelColor);
margin: 5px 15px;
}
main {
display: flex;
flex-direction: column;
align-items: center;
}
.zdog-canvas {
background: var(--pageColor); /*#843b62;*/
flex-basis: 100%;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="./style.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght#600&family=Patrick+Hand&family=Source+Code+Pro&display=swap" rel="stylesheet">
</head>
<body>
<main>
<h1>SORTING ALGORITHM VISUALIZER</h1>
<div id="nBarsInput">
<div id="textInput">
<div>
<label>Enter a sorting speed (in seconds)</label>
<input type="text" name="sortSpeed" id='sortSpeed' value="0.25" class='inputTextBox'>
</div>
<div>
<label>Enter a Value for N</label>
<input type="text" name="nBars" id='nTextBox' value="5" class='inputTextBox' required>
</div>
</div>
<div id="nBarsButton">
<select id='algoSelect' class='button'>
<option value='Selection Sort'>Selection Sort</option>
<option value='Insertion Sort'>Insertion Sort</option>
</select>
<input type="submit" value="Go" id='nSubmit' class='button'>
<input type="submit" value="Replay" id='replayButton' class='button'>
</div>
</div>
<canvas class='zdog-canvas' width='1000' height='500'></canvas>
</main>
<script src="./script.js" type="module"></script>
<script src="https://unpkg.com/zdog#1/dist/zdog.dist.min.js"></script>
</body>
</html>
I want to have a scrolling marquee that never ends, and with that, I mean that there will never be a blank space in the marquee.
So when, for example, all text has been in the screen (viewport) and the latest words are marquee'ing, the marquee will repeat without first ending the marquee (meaning: all text has gone away into the left side [marquee: right -> left]). With repeat I mean that the text will just start over right behind where the marquee is
So when I have the marquee text " Hello poeple of the earth •", and that is here:
_ = blank
! = Char of first run of marquee
^ = char of second run of marquee
& = char of third run of marquee
________!!!!!!!!!!!!!!!!!!!!!!!!!!!!****************************^^^^^^^^^^^^^^^^^^^^^^^^^^^^&&&&&&&&
Ofcourse I need it to be smooth. Something like this answer, but without the blank spaces. The use of libraries doesn't matter.
Can anyone help me?
You can use marque plugin to achieve this
$('.marquee').marquee({
//speed in milliseconds of the marquee
duration: 5000,
//gap in pixels between the tickers
gap: 0,
//time in milliseconds before the marquee will start animating
delayBeforeStart: 0,
//'left' or 'right'
direction: 'left',
//true or false - should the marquee be duplicated to show an effect of continues flow
duplicated: false
});
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script type='text/javascript' src='//cdn.jsdelivr.net/jquery.marquee/1.3.1/jquery.marquee.min.js'></script>
<body>
<div class="marquee">stuff to say 1|</div>
<div class="marquee">stuff to say 2|</div>
<div class="marquee">stuff to say 3|</div>
<div class="marquee">stuff to say 4|</div>
<div class="marquee">stuff to say 5</div>
</body>
I think this is what you want :)))
function start() {
new mq('latest-news');
mqRotate(mqr);
}
window.onload = start;
function objWidth(obj) {
if (obj.offsetWidth) return obj.offsetWidth;
if (obj.clip) return obj.clip.width;
return 0;
}
var mqr = [];
function mq(id) {
this.mqo = document.getElementById(id);
var wid = objWidth(this.mqo.getElementsByTagName("span")[0]) + 5;
var fulwid = objWidth(this.mqo);
var txt = this.mqo.getElementsByTagName("span")[0].innerHTML;
this.mqo.innerHTML = "";
var heit = this.mqo.style.height;
this.mqo.onmouseout = function () {
mqRotate(mqr);
};
this.mqo.onmouseover = function () {
clearTimeout(mqr[0].TO);
};
this.mqo.ary = [];
var maxw = Math.ceil(fulwid / wid) + 1;
for (var i = 0; i < maxw; i++) {
this.mqo.ary[i] = document.createElement("div");
this.mqo.ary[i].innerHTML = txt;
this.mqo.ary[i].style.position = "absolute";
this.mqo.ary[i].style.left = wid * i + "px";
this.mqo.ary[i].style.width = wid + "px";
this.mqo.ary[i].style.height = heit;
this.mqo.appendChild(this.mqo.ary[i]);
}
mqr.push(this.mqo);
}
function mqRotate(mqr) {
if (!mqr) return;
for (var j = mqr.length - 1; j > -1; j--) {
maxa = mqr[j].ary.length;
for (var i = 0; i < maxa; i++) {
var x = mqr[j].ary[i].style;
x.left = parseInt(x.left, 10) - 1 + "px";
}
var y = mqr[j].ary[0].style;
if (parseInt(y.left, 10) + parseInt(y.width, 10) < 0) {
var z = mqr[j].ary.shift();
z.style.left = parseInt(z.style.left) + parseInt(z.style.width) * maxa + "px";
mqr[j].ary.push(z);
}
}
mqr[0].TO = setTimeout("mqRotate(mqr)", 20);
}
.marquee {
position: relative;
overflow: hidden;
text-align: center;
margin: 0 auto;
width: 100%;
height: 30px;
display: flex;
align-items: center;
white-space: nowrap;
}
#latest-news {
line-height: 32px;
a {
color: #555555;
font-size: 13px;
font-weight: 300;
&:hover {
color: #000000;
}
}
span {
font-size: 18px;
position: relative;
top: 4px;
color: #999999;
}
}
<div id="latest-news" class="marquee">
<span style="white-space:nowrap;">
<span> •</span>
one Lorem ipsum dolor sit amet
<span> •</span>
two In publishing and graphic design
<span> •</span>
three Lorem ipsum is a placeholder text commonly
</span>
</div>
Thank You sire.....I get it....What I need....
function start() {
new mq('latest-news');
mqRotate(mqr);
}
window.onload = start;
function objWidth(obj) {
if (obj.offsetWidth) return obj.offsetWidth;
if (obj.clip) return obj.clip.width;
return 0;
}
var mqr = [];
function mq(id) {
this.mqo = document.getElementById(id);
var wid = objWidth(this.mqo.getElementsByTagName("span")[0]) + 5;
var fulwid = objWidth(this.mqo);
var txt = this.mqo.getElementsByTagName("span")[0].innerHTML;
this.mqo.innerHTML = "";
var heit = this.mqo.style.height;
this.mqo.onmouseout = function () {
mqRotate(mqr);
};
this.mqo.onmouseover = function () {
clearTimeout(mqr[0].TO);
};
this.mqo.ary = [];
var maxw = Math.ceil(fulwid / wid) + 1;
for (var i = 0; i < maxw; i++) {
this.mqo.ary[i] = document.createElement("div");
this.mqo.ary[i].innerHTML = txt;
this.mqo.ary[i].style.position = "absolute";
this.mqo.ary[i].style.left = wid * i + "px";
this.mqo.ary[i].style.width = wid + "px";
this.mqo.ary[i].style.height = heit;
this.mqo.appendChild(this.mqo.ary[i]);
}
mqr.push(this.mqo);
}
function mqRotate(mqr) {
if (!mqr) return;
for (var j = mqr.length - 1; j > -1; j--) {
maxa = mqr[j].ary.length;
for (var i = 0; i < maxa; i++) {
var x = mqr[j].ary[i].style;
x.left = parseInt(x.left, 10) - 1 + "px";
}
var y = mqr[j].ary[0].style;
if (parseInt(y.left, 10) + parseInt(y.width, 10) < 0) {
var z = mqr[j].ary.shift();
z.style.left = parseInt(z.style.left) + parseInt(z.style.width) * maxa + "px";
mqr[j].ary.push(z);
}
}
mqr[0].TO = setTimeout("mqRotate(mqr)", 20);
}
.marquee {
position: relative;
overflow: hidden;
text-align: center;
margin: 0 auto;
width: 100%;
height: 30px;
display: flex;
align-items: center;
white-space: nowrap;
}
#latest-news {
line-height: 32px;
a {
color: #555555;
font-size: 13px;
font-weight: 300;
&:hover {
color: #000000;
}
}
span {
font-size: 18px;
position: relative;
top: 4px;
color: #999999;
}
}
<div id="latest-news" class="marquee">
<span style="white-space:nowrap;">
<span> •</span>
one Lorem ipsum dolor sit amet
<span> •</span>
two In publishing and graphic design
<span> •</span>
three Lorem ipsum is a placeholder text commonly
</span>
</div>
I am learning JS and have created a carousel with a caption underneath.
How do I get the Prev/Next buttons to affect the caption as well as the image? I've tried combining the if statements in several ways but have failed miserably.
Relevant HTML:
<span id="prev" class="arrow">❮</span>
<div class="karussell" id="karussell">
<img class="karu" name="esislaid">
</div>
<span id="next" class="arrow">❯</span>
<div class="caption">
<h3 id="esikiri"></h3>
</div>
JS:
var p = 0;
var s = 0;
var esileht = [];
var aeg = 5000;
var kiri = [];
//Image List
esileht[0] = 'img/tooted/raamat/graafvanalinn2016.jpg';
esileht[1] = 'img/tooted/kaart/kaart_taskus_esipool.jpg';
esileht[2] = 'img/tooted/kaart/graafkaart_esikylg.jpg';
//Captions
kiri[0] = 'Raamat "Tallinn. Graafiline vanalinn"';
kiri[1] = 'Tallinna vanalinna graafiline kaart (suur formaat)';
kiri[2] = 'Tallinna vanalinna graafiline kaart (väike formaat)';
// Left and Right arrows
//Eelmine
function eelmine(){
if (p === 0){
p = esileht.length;
}
p = p - 1;
return esileht[p];
}
//Jargmine
function jargmine(){
p = p + 1;
p = p % esileht.length;
return esileht[p];
}
document.getElementById('prev').addEventListener('click', function (e){
document.querySelector('#karussell img').src = eelmine();
}
);
document.getElementById('next').addEventListener('click', function (e) {
document.querySelector('#karussell img').src = jargmine();
}
);
//Change Image
function changePilt (){
document.esislaid.src = esileht[p];
if(p < esileht.length -1){
p++;
} else {
p = 0;
}
setTimeout("changePilt()", aeg);
}
//Change Caption
function changeKiri(){
document.getElementById('esikiri').innerHTML = kiri[s];
if(s < kiri.length - 1){
s++;
}
else {
s = 0;
}
setTimeout('changeKiri()', aeg);
}
document.body.onload = function(){
changePilt();
changeKiri();
};
CSS, just in case:
.karussell {
position: relative;
width: 100%;
max-height: 600px;
overflow: hidden;
}
.arrow {
cursor: pointer;
position: absolute;
top: 40%;
width: auto;
color: #00A7E0;
padding: 16px;
font-weight: bold;
font-size: 18px;
border-radius: 3px;
transition: 0.6s ease;
}
#next {
right: 0;
}
#prev {
left: 0;
}
.arrow:hover {
background-color: rgba(0,0,0,0.8);
}
.caption {
text-align: center;
color: #00A7E0;
padding: 2px 16px;
}
.karu {
max-width: 75%;
animation-name: fade;
animation-duration: 2s;
}
#keyframes fade {
from {opacity: 0.4}
to {opacity: 1}
}
#media (max-width:767px){.karu{max-width: 95%;}}
I made a fiddle to try to illustrate (had to paste the js into the html tab to gt it to work for some reason): Fiddle
Really you just need to use the .innerHTML() feature and do exactly what you already have. Either create a eelmine2() function (or something like that) and call it again, grabbing the content from kiri[] or instead just return the p and use it in two places:
document.getElementById('prev').addEventListener('click', function (e){
document.querySelector('#karussell img').src = eelmine();
document.querySelector('#esikiri').innerHTML = eelmine2();
});
function eelmine2(){
if (p === 0){
p = kiri.length;
}
p = p - 1;
return kiri[p];
}
or
document.getElementById('prev').addEventListener('click', function (e){
var change = eelmine();
document.querySelector('#karussell img').src = esileht[change];
document.querySelector('#esikiri').innerHTML = kiri[change];
});
function eelmine(){
if (p === 0){
p = kiri.length;
}
p = p - 1;
return p;
}
This assumes your code is using the same global vars inside public functions that you have set up in your Fiddle. You should fix that to have variables passed into the functions before going live with all of this, but I'm not addressing that any further here.
There is a function in js which displays messages to the table (messages are stored in json). In Google Chrome, it works, but Safari, Opera or Microsoft Edge - no!
There is a mistake in code which is associated with the call to setTimeout (callback, 5000)(nothing is sent to the callback).So, For (var i = 0; i <respond.length; i ++) will not work since respond === undefined.
But why is it so?
callback(
[{
"time": "1500303264",
"user": "qwe",
"message": "we",
"id": 1
},
{
"time": "1500303987",
"user": "Max",
"message": "q",
"id": 2
}
]);
function smile(mess) {
var smile = ":)";
var graficSmile = "<img src = './image/Smile.png' alt='Smile' align='middle'>";
var string_with_replaced_smile = mess.replace(smile, graficSmile);
var sad = ":("
var graficSad = "<img src = './image/Sad.png' alt='Smile' align='middle'>";
var string_with_replaced_smile_and_sad = string_with_replaced_smile.replace(sad, graficSad);
return string_with_replaced_smile_and_sad;
}
$.getJSON('data/messages.json', callback);
var exists = [];
function callback(respond) {
var timeNow = Date.now();
for (var i = 0; i < respond.length; i++) {
var data = respond[i];
if (exists.indexOf(data.id) != -1) continue;
var timeInMessage = data.time * 1000;
var diff_time = (timeNow - timeInMessage);
if (diff_time <= 3600000) {
var rowClone = $('.mess_hide').clone().removeClass('mess_hide');
var newDate = new Date(timeInMessage);
var dateArray = [newDate.getHours(), newDate.getMinutes(), newDate.getSeconds()]
var res = dateArray.map(function(x) {
return x < 10 ? "0" + x : x;
}).join(":");
$('#messages').append(rowClone);
$('.time', rowClone).html(res);
$('.name', rowClone).html(data.user);
$('.message', rowClone).html(smile(data.message));
$('.scroller').scrollTop($('#messages').height());
exists.push(data.id);
}
}
setTimeout(function(){callback(respond)}, 5000);
}
.scroller {
width: 490px;
height: 255px;
max-height: 255px;
overflow-y: auto;
overflow-x: hidden;
}
table#messages {
min-height: 260px;
width: 100%;
background: #fffecd;
border: none;
}
table#messages::-webkit-scrollbar {
width: 1em;
}
table#messages::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}
table#messages::-webkit-scrollbar-thumb {
background-color: darkgrey;
outline: 1px solid slategrey;
}
tr {
height: 20%;
display: block;
}
td.time,
td.name {
width: 70px;
max-width: 75px;
text-align: center;
}
td.name {
font-weight: bold;
}
form#text_submit {
display: inline-flex;
align-items: flex-start;
}
input#text {
width: 370px;
height: 30px;
margin-top: 20px;
background: #fffecd;
font-family: 'Montserrat';
font-size: 16px;
border: none;
align-self: flex-start;
}
input#submit {
padding: 0;
margin-left: 21px;
margin-top: 21px;
height: 30px;
width: 95px;
background: #635960;
border: none;
color: white;
font-family: 'Montserrat';
font-size: 16px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="scroller">
<table id="messages">
<tr class="mess_hide">
<td class="time"></td>
<td class="name"></td>
<td class="message"></td>
</tr>
</table>
</div>
<form method="POST" id="easyForm">
<input type="text" name="text" id="text">
<input type="submit" value="Send" id="submit">
</form>
</div>
Chrome
Opera
Since it is assumed that the var exists - array, but the value of the array ([]) is assigned to it only later, after the call $.getJSON(...). So, when callback is called for the first time value [] is not set for exists.We just need to move var exists above the first call of callback.
When callback is called by the timer, nothing is passed to it. But timer needs to reread the messages from the file and display them on the screen.So, instead setTimeout(function(){callback(respond)}, 5000); we need setTimeout(function(){$.getJSON('data/messages.json', callback);}, 5000);.
var exists = [];
$.getJSON('data/messages.json', callback);
function callback(respond) {
var timeNow = Date.now();
for (var i = 0; i < respond.length; i++) {
var data = respond[i];
if (exists.indexOf(data.id) != -1) continue;
var timeInMessage = data.time * 1000;
var diff_time = (timeNow - timeInMessage);
if (diff_time <= 3600000) {
var rowClone = $('.mess_hide').clone().removeClass('mess_hide');
var newDate = new Date(timeInMessage);
var dateArray = [newDate.getHours(), newDate.getMinutes(), newDate.getSeconds()]
var res = dateArray.map(function(x) {
return x < 10 ? "0" + x : x;
}).join(":");
$('#messages').append(rowClone);
$('.time', rowClone).html(res);
$('.name', rowClone).html(data.user);
$('.message', rowClone).html(smile(data.message));
$('.scroller').scrollTop($('#messages').height());
exists.push(data.id);
}
}
setTimeout(function() {
$.getJSON('data/messages.json', callback);
}, 5000);
}
Since callback requires an array to be passed as an argument, setTimeout must ensure that when it calls callback, it passes the array.
Change
setTimeout(callback, 5000);
to
setTimeout(function(){callback(respond)}, 5000);
which allows callback to be called with an argument as the body of an anonymous function that will be called by setTimeout.
Also, as a side note, if you used respond.forEach() instead of a counting for loop, the code would be much cleaner:
respond.forEach(function(data) {
if (exists.indexOf(data.id) != -1) continue;
var timeInMessage = data.time * 1000;
var diff_time = (timeNow - timeInMessage);
if (diff_time <= 3600000) {
var rowClone = $('.mess_hide').clone().removeClass('mess_hide');
var newDate = new Date(timeInMessage);
var dateArray = [newDate.getHours(), newDate.getMinutes(), newDate.getSeconds()]
var res = dateArray.map(function(x) {
return x < 10 ? "0" + x : x;
}).join(":");
$('#messages').append(rowClone);
$('.time', rowClone).html(res);
$('.name', rowClone).html(data.user);
$('.message', rowClone).html(smile(data.message));
$('.scroller').scrollTop($('#messages').height());
exists.push(data.id);
}
});
my code lists items from an rss feed onto an html page. although, the java script is a little finicky. it won't read some xml feeds, usually the feeds containing list items over 25. I just need another set of eyes to take a look at the code and tell me if i'm missing something obvious.
.js file-----------------------------------------------
//XML CODE
var http_request = false;
var dataFileName = new Array();
dataFileName[1] = "http://newsrss.bbc.co.uk/rss/newsonline_world_edition/americas/rss.xml";
//dataFileName[2] = "http://newsrss.bbc.co.uk/rss/newsonline_world_edition/uk_news/magazine/rss.xml";
//dataFileName[3] = "http://newsrss.bbc.co.uk/rss/newsonline_world_edition/business/rss.xml";
function getData(dataFileIndex) {
if (window.ActiveXObject) { //IE
http_request = new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) { //other
http_request = new XMLHttpRequest();
} else {
alert("your browser does not support AJAX");
}
http_request.open("GET",dataFileName[dataFileIndex],true);
http_request.setRequestHeader("Cache-Control", "no-cache");
http_request.setRequestHeader("Pragma", "no-cache");
http_request.onreadystatechange = function() {
if (http_request.readyState == 4) {
if (http_request.status == 200) {
if (http_request.responseText != null) {
processRSS(http_request.responseXML);
} else {
alert("Failed to receive RSS file from the server - file not found.");
return false;
}
}
}
}
http_request.send(null);
}
function processRSS(rssxml) {
RSS = new RSS2Channel(rssxml);
outputData(RSS);
}
function RSS2Channel(rssxml) {
this.items = new Array();
var itemElements = rssxml.getElementsByTagName("item");
for (var i=0; i<itemElements.length; i++) {
Item = new RSS2Item(itemElements[i]);
this.items.push(Item);
}
}
function RSS2Item(itemxml) {
this.title;
this.link;
this.description;
this.pubDate;
this.guid;
var properties = new Array("title", "link", "description", "pubDate", "guid");
var tmpElement = null;
for (var i=0; i<properties.length; i++) {
tmpElement = itemxml.getElementsByTagName(properties[i])[0];
if (tmpElement != null) {
eval("this."+properties[i]+"=tmpElement.childNodes[0].nodeValue");
}
}
}
function outputData(RSS) {
dataString = "";
for (var i=0; i<RSS.items.length; i++) {
dataString += "<div class='itemBlock'>";
newDate = new Date(RSS.items[i].pubDate);
dateString = (newDate.getMonth()+1) + "/" + newDate.getDate() + "/" + newDate.getFullYear();
dataString += "<div class='itemDate'>" + dateString + "</div>";
dataString += "<div class='itemTitle'><a href='" + RSS.items[i].link + "' target='afps_news'>" + RSS.items[i].title + "</a></div>";
//dataString += "<div class='itemDescription'>" + RSS.items[i].description + "</div>";
dataString += "</div>";
}
document.getElementById('outputBlock').innerHTML = dataString;
}
//SCROLL BAR CODE
var ie=document.all;
var nn6=document.getElementById&&!document.all;
var isdrag=false;
var x,y;
var dobj;
var scrollPercent;
var boxTop;
var maxHeight;
var toppoint;
function movemouse(e) {
if (isdrag) {
//dobj.style.left = nn6 ? tx + e.clientX - x : tx + event.clientX - x;
toppoint = (nn6) ? ty + e.clientY - y : ty + event.clientY - y;
boxTop = parseInt(document.getElementById('scrollBarBox').style.top) - scrollBarBoxOffset;
if (toppoint < boxTop) toppoint = boxTop;
boxHeight = parseInt(document.getElementById('scrollBarBox').style.height);
maxHeight = boxTop + boxHeight - parseInt(document.getElementById('scrollBar').style.height);
if (toppoint > maxHeight) toppoint = maxHeight;
dobj.style.top = toppoint + "px";
scrollPercent = toppoint / maxHeight;
document.getElementById('textWindow').style.top = parseInt(0 - (document.getElementById('textWindow').offsetHeight - parseInt(document.getElementById('scrollBarBox').style.height)) * scrollPercent );
return false;
}
}
function selectmouse(e) {
var fobj = nn6 ? e.target : event.srcElement;
var topelement = nn6 ? "HTML" : "BODY";
while (fobj.tagName != topelement && fobj.className != "dragme") {
fobj = nn6 ? fobj.parentNode : fobj.parentElement;
}
if (fobj.className == "dragme") {
isdrag = true;
dobj = fobj;
//tx = parseInt(dobj.style.left + 0);
ty = parseInt(dobj.style.top + 0);
//x = nn6 ? e.clientX : event.clientX;
y = nn6 ? e.clientY : event.clientY;
document.onmousemove = movemouse;
return false;
}
}
document.onmousedown = selectmouse;
document.onmouseup = new Function("isdrag=false;");
html file-------------------------------------------------------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE>TEST</TITLE>
<META http-equiv=Content-Type content="text/html; charset=windows-1252">
<SCRIPT src="script1.js"></SCRIPT>
<STYLE>BODY {
MARGIN: 0px; FONT: 8pt arial
}
#widgetBody {
BACKGROUND-Color:gray; WIDTH: 240px; POSITION: relative; HEIGHT: 299px
}
#textWindowBox {
LEFT: 63px; OVERFLOW: hidden; WIDTH: 152px; POSITION: absolute; TOP: 70px; HEIGHT: 221px
}
#textWindow {
PADDING-TOP: 7px; POSITION: relative
}
#scrollBarBox {
LEFT: 221px; WIDTH: 12px; POSITION: absolute; TOP: 74px; HEIGHT: 216px
}
#scrollBar {
BACKGROUND: url(images/widget_scroll-handle1.gif) no-repeat; WIDTH: 12px; POSITION: relative; HEIGHT: 40px
}
#defenseLinkLink {
LEFT: 4px; WIDTH: 20px; CURSOR: pointer; POSITION: absolute; TOP: 155px; HEIGHT: 140px; BACKGROUND-COLOR: transparent
}
#defenseLinkLink A {
DISPLAY: block; WIDTH: 20px; HEIGHT: 140px
}
.dragme {
POSITION: relative
}
.itemBlock {
PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 4px; MARGIN: 0px 0px 3px; PADDING-TOP: 0px; BORDER-BOTTOM: #adafb3 1px dotted
}
.itemDate {
FONT-SIZE: 0.9em; COLOR: #666; LINE-HEIGHT: 1.1em
}
.itemTitle {
FONT-WEIGHT: bold; LINE-HEIGHT: 1.1em
}
.itemTitle A {
COLOR: #254a7d; TEXT-DECORATION: none
}
.itemDescription {
}
</STYLE>
<SCRIPT>
var scrollBarBoxOffset = 74;
function init() {
document.getElementById('scrollBarBox').style.top = "74px";
document.getElementById('scrollBarBox').style.height = "216px";
document.getElementById('scrollBar').style.height = "40px";
}
</SCRIPT>
<META content="MSHTML 6.00.6001.18294" name=GENERATOR></HEAD>
<BODY onload=init()>
<DIV id=widgetBody>
<DIV id=textWindowBox>
<DIV id=textWindow>
<DIV id=outputBlock></DIV></DIV></DIV>
<DIV id=scrollBarBox>
<DIV class=dragme id=scrollBar></DIV></DIV>
<DIV style="CLEAR: both"></DIV></DIV>
<SCRIPT language=javaScript>getData(2)</SCRIPT>
</BODY></HTML>
Ok, got it working.
2 issues.
army.mil does not resolve! Please use "www.army.mil" instead.
IN RSS2Item replace this line:
if (tmpElement != null) {
with this:
if (tmpElement != null && tmpElement.childNodes[0]) {
Oh man, why are you using XMLHttpRequest directly? Use a library for that, and make your life easier :)
You could be running into cross-site scripting security problems. If the RSS feeds exist on a different domain than the page running the JavaScript, the browser will not let your JavaScript make the requests.
dataFileName[1] = "http://newsrss.bbc.co.uk/rss/newsonline_world_edition/americas/rss.xml";
Unless you are (a) a script running from the BBC, or (b) a browser extension, you cannot make an XMLHttpRequest to that server.
dataString += "<div class='itemTitle'><a href='" + RSS.items[i].link
HTML/script injection. If you insist on rolling innerHTML instead of using simple DOM methods you must do HTML escaping to turn <&" into <&".
eval("this."+properties[i]+"=tmpElement.childNodes[0].nodeValue");
Don't use eval, except in the very few unusual cases you need it. This isn't one of them; you can access a JavaScript property by name using:
this[properties[i]]= tmpElement.firstChild.data;
Also, and this is probably where the unreliability is coming in, you can't be sure there will be a single child Text node. If there is no content in that element, firstChild/childNodes[0] will not exist and you will get an exception. If there is complex content in the element (which normally there shouldn't be, but for RSS 0.9 there can be as unencoded HTML), firstChild.nodeValue won't contain the text content of the element. Instead you would have to walk over the Text node descendents gathering their nodeValue/data.