How can I use scrollIntoView() with mounted hook - javascript

I have a Vue component and the template has some div elements, a few images and a User object passed over from Laravel 6.0 Everything seems to work just fine until I try to use scrollIntoView() with the mounted() function. From what I can tell the page is trying to scrollIntoView() before it is completely loaded. If I limit the user object to a small size (just 2 users) everything works fine. But if there is more data it does not scroll to view like it should. It does work if I use a set timeout function but it looks hackey and not professional.
I just want to scroll to a div (the header) after view is mounted/loaded. The div is the first div on the page and has no images (if that even matters). Can anyone help me to solve this? I have seen that the mounted function only applies to the virtual DOM? If so what can we use in Vue that is more like JQuery $(document).ready() but for the specific component and applies to the actual DOM?
mounted() {
var elmnt = document.getElementById("move-to-header");
elmnt.scrollIntoView(true);
}
}

I got it working by using the callback function of my axios request. I did however need to wrap it in a setTimeout function with no specified number of milliseconds but overall it is working without errors.
get_user(the_name) {
axios
.post("/get_users", { the_name: the_name})
.then(response => {
this.display_user(response.data);
setTimeout(() => {
var elmnt = document.getElementById("move-to-header");
elmnt.scrollIntoView();
})

Related

How to use window onload in a single page application?

I'm using node.js in order to build a single page application.
At the top of the homepage I used a 100vh css style to show some color in full screen, and then after page loads, I created a transition to load an image.
I used this code:
JS
$('body').addClass('is-loading');
$(window).on('load', function() {
$('body').removeClass('is-loading');
});
However, single page application doesn´t need to load again after it loads. So, my question is how can I achieve this transition using node.js? Is there an alternative?
Window onload would works fine in a static web page. In a single page application you can use setTimeout (as one possible solution) to removeClass.
For what I understand you want transition works any time in your app:
This code works fine (is already tested):
if ( ! $('body').is('.is-loading') ) {
$('body').addClass('is-loading');
}
setTimeout(function () {
$('body').removeClass('is-loading');
}, 1000)
I hope it works in your code. Would be great to see some example of your css code. For now, you can achieve transition with the example written above.
Change time parameter according to your needs.
You can use localStorage.
Using a native js approach
localStorage.setItem('myToggle', isLoaded);
and then you retrieve from the same local storage using getItem and then specifying the name of the created localStorage item.
var isItAlreadyLoaded = localStorage.getItem('myToggle');
So
window.onload = function(){
var isItAlreadyLoaded = localStorage.getItem('myToggle');
if(isItAlreadyLoaded == 'true'){
console.log('already loaded');
}
else{
//load image here
//.....
var isLoaded = 'true';
localStorage.setItem('myToggle', isLoaded);
console.log('saved!');
}
}
This works like magic. localStorage is some sort of browser mini-storage, each storage variable can hold upto 5~10mb depending on the browser. It works well in most modern browsers.
All I did was put a boolean variable to check if the image was already loaded, or just check for the existence of the localStorage.

Moving from page to page creates errors (duplicates?)

I'm trying to use the pushPage() function of the OnsenUI 2 (rc15 as of now). Alongside the OnsenUI, I'm using jQuery 3.
Here is my function that upon clicking certain elements should push a page:
$(".tile_handler").on("click", ".imalink", function () {
var link = $(this).data().href;
if(link != null){
document.querySelector("#myNavigator").pushPage(link, { animation: "slide-ios" });
}
})
When I push the page for the first time, it works fine. I use the iOS back button to return. Then I click it again and I get this error (and increasingly more as I repeat the process):
[index.js:450] Uncaught (in promise) pushPage is already running.
Here is another function that is supposed to load splitter pages:
$(".splitter_item").click(function () {
var address = $(this).data('address');
$('#content')[0].load(address).then(menu.close.bind($('#menu')[0]));
})
When I switch between two pages via Splitter it starts throwing this ( and more every time I switch between pages)
[undefined:1] Uncaught (in promise) Splitter side is locked.
What I assume is happening is that I load a page, leave it and when I access it again, it loads the page again. However that doesn't seem to be the behavior shown in OnsenUI examples such as this:
document.addEventListener('init', function(event) {
var page = event.target;
if (page.id === 'page1') {
page.querySelector('#push-button').onclick = function() {
document.querySelector('#myNavigator').pushPage('page2.html', {data: {title: 'Page 2'}});
};
} else if (page.id === 'page2') {
page.querySelector('ons-toolbar .center').innerHTML = page.data.title;
}
});
There is the popPage() function which should delete previously loaded page and would prevent this. But the references don't use it, so I assume I'm doing something wrong. But I don't know what.
UPDATE:
I managed to reproduce both problems in CodePen. Here is the Splitter error and here is the pushPage() one. It seems like the pushPage() one is a problem with my function as it adds a pushPage() request every time I click it, not sure why though.
Both errors only seem to happen in Ripple emulator (via VS2015). I don't seem to be able to reproduce them in Android Emulator (but the $(".tile_handler").on("click", ".imalink", function () { code fires incorreclty multiple times anyway). I'm testing this further.
Basically each time you push a page, that page fires an init event. However Onsen still keeps the initial page in the dom.
Navigator example (the same logic applies to the splitter one):
<ons-navigator>
<ons-page id="dashboard">
<div class="imalink" data-href="request_list.html"></div>
</ons-page>
</ons-navigator>
You have an init event for dashboard. Then you click the tile and go to another page.
Then request_list fires its own init event. However our initial page is still in the dom.
<ons-navigator>
<ons-page id="dashboard" style="display: none">
<div class="imalink" data-href="request_list.html"></div>
</ons-page>
<ons-page id="request_list">
...
</ons-page>
</ons-navigator>
You have something like this. However the following is called a second time:
$(".tile_handler").on("click", ".imalink", function () {
...
})
Which adds the listeners once more. $el.on("click") like an alias of addEventListener, meaning you are adding more and more listeners.
So whenever you navigate you keep adding them because the initial page was never removed from the dom.
Alternative solutions:
Use only the current page (e.target)
$('selector') // instead of this
$(e.target).find('selector') // use this
That way you limit to only finding elements within the page which you just created.
Enable the handlers from the start.
Since you are using jQuery there is actually a simpler way to do the things without relying on the init event.
Just do this outside of any init handler:
$(document).on("click", ".tile_handler .imalink", function () { ... })
This actually means that the handler is attached to the document itself and the handler will be called only when the target is .tile_handler .imalink - so it works with any future imalinks which you create.
This may not be the most efficient approach, but is definitely one of the simplest.
Both errors which you mention are actually a safe measure from you pushing or doing some action twice by mistake if you double click a button/link for example.
They appear when you try to make an action while an animation is running. As you can see in the examples generally there is no problem to push a page twice or more, as long as you start the second push after the first one is finished.
Here are demos with your exact code for both the splitter and navigator.
So the error does not come from the code which you provided, its someplace elsewhere.
The only thing I can come up with is that the behaviour which you mentioned would be seen if for some reason the popPage method failed to finish correctly. Maybe if you provide us with your own codepen where the issue can be reproduced we could debug it further.
An alternative, though highly NOT recommended would be to force the state before you do your action. This however would not be solving the problem, rather only masking it. And of course as with all hacks - it may break in future versions.
myNavigator._isRunning = false;
UPDATE:
Here are the two updated pens which you gave in the comments:
https://codepen.io/IliaSky/pen/YWOOkW?editors=1010
https://codepen.io/IliaSky/pen/QEVVGm?editors=1010
Basically you were adding the handlers on init event, which is fired whenever a page is added. so with your logic you are adding more and more handlers with each page. Just make sure you add them only once and you will be fine.
Add things like:
if (e.target.id == 'pagename') ...
or simply
$(document).on("init", '#dashboard_page', function(){ ... }
Take a look at .one() from jQuery - the event handler will only execute once per element in order to prevent the error: Uncaught (in promise) pushPage is already running
$(".tile_handler").one("click", ".imalink", function () {
var link = $(this).data().href;
if(link != null){
document.querySelector("#myNavigator").pushPage(link, { animation: "slide-ios" });
}})

How to create a React component for a bootstrap alert?

I'm trying to create a React component where I can call a function like showMessage(message, bgColor) and an alert box will pop up that closes automatically after 5 seconds.
I've created many React components in my application but this one is causing me trouble because of having to call alert(), it not always being present, and having a required timer functionality. I just can't think of a "React"ive way of designing the component.
Here is the current jQuery code that I'm using which works but I'm trying to get away from:
$("#alertBox").css('background-color', color);
$("#alertBox").html(message);
$("#alertBox").alert();
$("#alertBox").fadeIn(500, "linear").fadeOut(5000, "linear", function() {
$("#alertBox").alert('close');
});
The problem is React is fundamentally declarative and this code just seems so imperative. In certain event handlers in my codebase I want to be able to just call a function to momentarily display this alert box. If it wasn't for the call to the alert function and fading in/out it wouldn't be so bad (could just do conditional redner). This is the last place in my code I'm still using jQuery which I'm trying to completely neuter from my application.
I'd also rather not use react-bootstrap and react-motion because I just found out about them a couple days ago and this is the last React component I need for my application and rather not rewrite everything now to use those libraries.
How about something like;
<showMessage message={message} bgcolor={bgcolor} hide={hideMessage}/>
then in showMessage render;
render() {
if (this.props.hide) return null;
if (this.state.timerDone) return null;
return (<div id="messageDive" style={whatever}></div>);
}
And you probably need something in componentReceivedProps to reset timerDone and set your timer. Then of course timerDone method to setState({timerDone: true}).

Add class to dynamically generated HTML

I'm working to use custom checkbox styles with a checkbox which is dynamically generated by javascript for the Google Identity Toolkit. For example, we add this div:
<div id="gitkitWidgetDiv"></div>
And the Google Identity Toolkit script generates new html for that div.
I need to add a class to the HTML which is added by the javascript without any action by the user and I'm struggling to make it work. For example, here is my code:
$("#gitkitWidgetDiv").on('ready', ".gitkit-sign-in-options label", function() {
$(this).addClass('checkbox');
});
I've tried switching 'ready' for a few other options and also using the livequery plugin, but nothing is working for me. It works if I use an active event like 'click,' but I can't figure out how to do this when the page loads. Could someone please help? Thanks!
Modern browsers (including IE11) support mutation obervers. You can use one to monitor the parent node of the div that will be added. When the div has been added, just add the class.
Here's something I made which comes in handy in annoying cases like this where it's difficult to tell when the element you need has finished loading in: https://gist.github.com/DanWebb/8b688b31492632b38aea
so after including the function it'd be something like:
var interval = 500,
stopTime = 5000,
loaded = false;
setIntervalTimeout(function() {
if($('.dynanicElementClass').length && !loaded) {
$('.dynanicElementClass').addClass('checkbox');
loaded = true;
}
}, interval, stopTime);
It's not perfect and I'm sure there are better solutions out there but in most cases like this it does the job.

JQuery Mobile Custom Flipbox set initialization value

I have a custom flipbox which is described in the accepted answer here: JQuery Mobile Custom Flipbox get selected value.
I'm struggling to set the initial value after the page is rendered my 'change-page' function is something like this:
changePage:function (page) {
page.$el.attr('data-role', 'page');
page.render();
$('body').append(page.$el).trigger('create');
$.mobile.changePage(page.$el, {changeHash:false});
}
As I use Backbone.js to manage the views and then I delegate the navigation to jQM.
I basically need to set the initial value of this input text field ( maybe there's a workaround)
Ok I figured this out myself and I'm willing to share the solution:
first of all the change page function is slightly different:
changePage:function (page) {
page.$el.attr('data-role', 'page');
//get rid of everything in the old page.
page.remove();
//render the page again
page.render();
$('body').append(page.$el).trigger('create');
$.mobile.changePage(page.$el, {changeHash:false});
}
the remove call is necessary to get rid of every event listener you had in the old rendered HTML.
In every page that needs init parameters you can specify in the render function this:
render: function(){
....
this.$el.on('pageshow',this.initFields);
....
}
initFields: function(){
// put your jQuery code here (e.g. this.$el.find('foo').val('initValue');
}
please note that this solution as of the jQM documentation is valid up to the 1.5.0 version of jQM, after that the pageshow event will not be triggered anymore.

Categories