I'm trying to prepend old messages to a chatbox when the user scrolls to the top. I'm using eventListeners as the code illustrates, but I'm running into an error where only the last chatbox is working properly. It seems that bodies[index].scrollTop and bodies[index].scrollHeight always returns 0 with the exception of bodies[lastIndex]. Why might this be? (logging bodies[index] correctly returns the div element)
document.querySelectorAll('.popup-messages').forEach((item, index) =>
{
item.addEventListener('scroll', async function()
{
if (chatReady[index] == true && bodies[index].scrollTop == 0)
{
chatReady[index] = false;
var previousHeight = bodies[index].scrollHeight;
await getMessages(item.id.replace(":body", ""));
var heightDiff = bodies[index].scrollHeight - previousHeight;
bodies[index].scrollTop += heightDiff;
}
})
})
Edit: If there's a different way to make multiple eventListeners dynamically, please share it with me as it would help a lot.
This problem was solved by replacing bodies[index] with item. Perhaps there was a bug that occurs when more than one variable is associated with an element.
Related
I am trying to link part id's to PDF pages. At the moment I am test linking them by:
const page = results[0].selection[0] % 100;
Each part ID does now link to a page. However, it only works when I click on a part -> click off the part -> then click on another part. If you try to switch between parts it doesnt show the pdf.
I think there is something wrong with the pdf#page part. As I can switch between parts if I change the code and dont link to a page.
I noticed that the same issue happens on the demo: https://forge-digital-twin.autodesk.io/
Any ideas on how to solve this?
Many thank,
Poppy
Here is my code:
function initDocument(facility) {
// Only enable when exactly one part is selected
const $alert = $('#maintenance-docs div.alert');
$alert.show();
$('#maintenance-docs embed').hide()
$('#maintenance-docs > .show-based-on-selection').hide();
NOP_VIEWER.addEventListener(Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, function(ev) {
const results = NOP_VIEWER.getAggregateSelection();
if (results.length === 1 && results[0].selection.length === 1) {
$alert.hide();
const page = results[0].selection[0] % 100;
console.log(page);
$('#maintenance-docs > .show-based-on-selection').show();
$('#maintenance-docs embed').attr('src', `/resources/Operation-manual-Sigg-Plant.pdf#page=${page}`).show();
//$('#maintenance-docs embed').show();
} else {
$alert.show();
$('#maintenance-docs > .show-based-on-selection').hide();
$('#maintenance-docs embed').attr('src', '');
//$('#maintenance-docs embed').hide();
}
});
}
Try with
Autodesk.Viewing.SELECTION_CHANGED_EVENT
Inside the event function keep same functionality.
On this page: https://www.airsyspro.com There is a script for a stick header that seems to have an infinite loop that is causing thousands of errors in the console. Any ideas on how to fix this issue?
You'll see in the console that it appears on lines 2216 and 2245. JS is not my expertise, thanks in advance.
if ($('.hpg-sticky-bar').length) {
$('.hpg-sticky-bar').addClass('original').clone().insertAfter('.hpg-sticky-bar').addClass('cloned').css('position','fixed').css('top','0').css('margin-top','0').css('z-index','500').removeClass('original').hide();
scrollIntervalID = setInterval(stickIt, 10);
function stickIt() {
var orgElementPos = $('.original').offset();
orgElementTop = orgElementPos.top;
if ($(window).scrollTop() >= (orgElementTop)) {
// scrolled past the original position; now only show the cloned, sticky element.
// Cloned element should always have same left position and width as original element.
orgElement = $('.original');
coordsOrgElement = orgElement.offset();
leftOrgElement = coordsOrgElement.left;
widthOrgElement = orgElement.css('width');
$('.cloned').css('left',leftOrgElement+'px').css('top',0).css('width',widthOrgElement).show();
$('.original').css('visibility','hidden');
} else {
// not scrolled past the menu; only show the original menu.
$('.cloned').hide();
$('.original').css('visibility','visible');
}
}
}
It can be as simple as the example here above
I think that the problem is this:
var orgElementPos = $('.original1').offset();
There is not a single element with class 'original'.
The var orgElementPos is null.
Also check this:
$('.msb-sticky-bar').addClass('original1')
the class msb-sticky-bar is misssing.
I am having issue fixing the header after scrolling, I tried a lot of stuff but can't get it to work. I checked this thread but it doesnt work for me: Angular 4 #HostListener Window scroll event strangely does not work in Firefox . This is my component structure:
Layout
Steps
Routes
Inside steps is my header which I want to fix, after scrolling for 50px. Inside Layout is some other content like a div with logo background (above the content of steps).
This is what I tried inside Steps.ts
#HostListener('window:scroll', [])
onWindowScroll() {
const number = window.scrollY;
if (number > 40) {
this.fixed = true;
} else if (this.fixed && number < 10) {
this.fixed = false;
}
}
but the problem is that scroll is not triggering at all. I found examples
where scroll logs the event, but for me it doesn't work (I tried with $event as well). Anyone has a solution?
Found a solution. On my layout component I put a function
(scroll)="onWindowScroll($event)"
and in layout component i used:
#HostListener('window:scroll', ['$event'])
onWindowScroll($event) {
const number = $event.target.scrollTop;
if (number > 40) {
this.fixed = true;
} else if (this.fixed && number < 10) {
this.fixed = false;
}
}
I removed Steps component since I didnt need it anymore, all the content is inside layout now.
In Angular 5+ it works a little differently:
const number = $event.target.scrollingElement.scrollTop || $event.target.documentElement.scrollTop;
Since some people come via Google to this question:
I'm quite a fan of moving logic like this into something re-useable. For Angular this would mean a directive. Therefore as I run into this issue myself I created a library from my code that at least has some tests and support across many browsers. So feel free to use this tested piece of code instead of polluting your components with more code.
https://w11k.github.io/angular-sticky-things/
With the code I see in the answer I did run into some issues. In another SO I found this solution. It is crucial to determine the offsetY of the header element correctly.
// Thanks to https://stanko.github.io/javascript-get-element-offset/
function getPosition(el) {
let top = 0;
let left = 0;
let element = el;
// Loop through the DOM tree
// and add it's parent's offset to get page offset
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return {
y: top,
x: left,
};
Currently I hide and show the content of a div like this:
var header = null;
var content = null;
var mainHolder = null;
var expandCollapseBtn = null;
var heightValue = 0;
header = document.getElementById("header");
content = document.getElementById("content");
mainHolder = document.getElementById("mainHolder");
expandCollapseBtn = header.getElementsByTagName('img')[0];
heightValue = mainHolder.offsetHeight;
header.addEventListener('click', handleClick, false);
mainHolder.addEventListener('webkitTransitionEnd',transitionEndHandler,false);
function handleClick() {
if(expandCollapseBtn.src.search('collapse') !=-1)
{
mainHolder.style.height = "26px";
content.style.display = "none";
}
else
{
mainHolder.style.height = heightValue + "px";
}
}
function transitionEndHandler() {
if(expandCollapseBtn.src.search('collapse') !=-1)
{
expandCollapseBtn.src = "expand1.png";
}
else{
expandCollapseBtn.src = "collapse1.png";
content.style.display = "block";
}
}
This is fine if the content is static, but I'm trying to populate my div dynamically like so.
This is called from an iphone application and populates the div with a string.
var method;
function myFunc(str)
{
method = str;
alert(method);
document.getElementById('method').innerHTML = method;
}
I store the string globally in the variable method. The problem I am having is now when I try expand the div I have just collapsed there is nothing there. Is there some way that I could use the information stored in var to repopulate the div before expanding it again? I've tried inserting it like I do in the function but it doesn't work.
Does anyone have any ideas?
to replicate:
Here is the jsfiddle. jsfiddle.net/6a9B3 If you type in text between
here it will work fine. I'm not sure
how I can call myfunc with a string only once in this jsfiddle, but if
you can work out how to do that you will see it loads ok the first
time, but when you collapse the section and attempt to re open it, it
wont work.
If the only way to fix this is using jquery I dont mind going down that route.
is it working in other browsers?
can you jsfiddle.net for present functionality because it is hard to understand context of problem in such code-shoot...
there are tonns of suggestions :) but I have strong feeling that
document.getElementById('method')
returns wrong element or this element not placed inside mainHolder
update: after review sample in jsfiddle
feeling about wrong element was correct :) change 'method' to 'info'
document.getElementById('method') -> document.getElementById('info')
I think you want to use document.getElementById('content') instead of document.getElementById('method') in myFunc.
I really see nothing wrong with this code. However, a guess you could explore is altering the line
content.style.display = "none";
It might be the case that whatever is displaying your html ( a webview or the browser itself) might be wiping the content of the elemtns, as the display is set to none
I have been looking around and I cannot seem to figure out how to do this, although it seems like it would be very simple.(mobile development)
What I am trying to do is display a message (kind of like an alert, but not an alert, more like a dialog) while a calculation is being made. Simply like a Loading please wait. I want the message to appear and stay there while the calculation is being done and then be removed. I just cannot seem to find a proper way of doing this.
The submit button is pressed and first checks to make sure all the forms are filled out then it should show the message, it does the calculation, then hides the message.
Here is the Calculation function.
function scpdResults(form) {
//call all of the "choice" functions here
//otherwise, when the page is refreshed, the pulldown might not match the variable
//this shouldn't be a problem, but this is the defensive way to code it
choiceVoltage(form);
choiceMotorRatingVal(form);
getMotorRatingType();
getProduct();
getConnection();
getDisconnect();
getDisclaimer();
getMotorType();
//restore these fields to their default values every time submit is clicked
//this puts the results table into a known state
//it is also used in error checking in the populateResults function
document.getElementById('results').innerHTML = "Results:";
document.getElementById('fuse_cb_sel').innerHTML = "Fuse/CB 1:";
document.getElementById('fuse_cb_sel_2').innerHTML = "Fuse/CB 2:";
document.getElementById('fuse_cb_result').innerHTML = "(result1)";
document.getElementById('fuse_cb_res_2').innerHTML = "(result2)";
document.getElementById('sccr_2').innerHTML = "<b>Fault Rating:</b>";
document.getElementById('sccr_result').innerHTML = "(result)";
document.getElementById('sccr_result_2').innerHTML = "(result)";
document.getElementById('contactor_result').innerHTML = "(result)";
document.getElementById('controller_result').innerHTML = "(result)";
//Make sure something has been selected for each variable
if (product === "Choose an Option." || product === "") {
alert("You must select a value for every field. Select a Value for Product");
**************BLAH************
} else {
//valid entries, so jump to results table
document.location.href = '#results_a';
******This is where the message should start being displayed***********
document.getElementById('motor_result').innerHTML = motorRatingVal + " " + motorRatingType;
document.getElementById('voltage_res_2').innerHTML = voltage + " V";
document.getElementById('product_res_2').innerHTML = product;
document.getElementById('connection_res_2').innerHTML = connection;
document.getElementById('disconnect_res_2').innerHTML = disconnect;
if (BLAH) {
}
else {
}
populateResults();
document.getElementById('CalculatedResults').style.display = "block";
} //end massive else statement that ensures all fields have values
*****Close out of the Loading message********
} //scpd results
Thank you all for your time, it is greatly appreciated
It is a good idea to separate your display code from the calculation code. It should roughly look like this
displayDialog();
makeCalculation();
closeDialog();
If you are having trouble with any of those steps, please add it to your question.
Computers are fast. Really fast. Most modern computers can do several billion instructions per second. Therefore, I'm fairly certain you can rely on a a setTimeout function to fire around 1000ms to be sufficient to show a loading message.
if (product === "Choose an Option." || product === "") {
/* ... */
} else {
/* ... */
var loader = document.getElementById('loader');
loader.style.display = 'block';
window.setTimeout(function() {
loader.style.display = 'none';
document.getElementById('CalculatedResults').style.display = "block";
}, 1000);
}
<div id="loader" style="display: none;">Please wait while we calculate.</div>
You need to give the UI main thread a chance to render your message before starting your calculation.
This is often done like this:
showMessage();
setTimeout(function() {
doCalculation();
cleanUp()
}, 0);
Using the timer allows the code to fall through into the event loop, update the UI, and then start up the calculation.
You're already using a section to pop up a "results" page -- why not pop up a "calculating" page?
Really, there are 4,000,000 different ways of tackling this problem, but why not try writing a "displayCalculatingMessage" function and a "removeCalculatingMessage" function, if you don't want to get all object-oriented on such a simple thing.
function displayCalculatingMessage () {
var submit_button = getSubmitButton();
submit_button.disabled = true;
// optionally get all inputs and disable those, as well
// now, you can either do something like pop up another hidden div,
// that has the loading message in it...
// or you could do something like:
var loading_span = document.createElement("span");
loading_span.id = "loading-message";
loading_span.innerText = "working...";
submit_button.parentElement.replaceChild(loading_span, submit_button);
}
function removeCalculatingMessage () {
var submit_button = getSubmitButton(),
loading_span = document.getElementById("loading-message");
submit_button.disabled = false;
loading_span.parentElement.replaceChild(submit_button, loading_span);
// and then reenable any other disabled elements, et cetera.
// then bring up your results div...
// ...or bring up your results div and do this after
}
There are a billion ways of accomplishing this, it all comes down to how you want it to appear to the user -- WHAT you want to have happen.