Browser freezing when updating backbone model? - javascript

I am working with the facebook api, I am trying to update an existing model in my localstorage collection with some attributes from a facebook object.
fbUser: function(e) {
var self = this;
var faceid = $(e.currentTarget).data('face');
FB.api('/'+faceid, function(response) {
console.log(response.name)
self.model.set({'name': response.name});
});
}
The above works, but lags the browser up and causes a (not responding) "warning: unresponsive script" error.
This method executes on a click event within the view, I have another method that loads in a list of people from facebook and appends to the element. Let me show you that code.
I am loading members from a group. Then loading all those members and for each appending them as a list item with a custom attribute that contains their profile id.
loadFbMembers: function() {
var self = this;
FB.api('/554870764588961/members/', function(response){
for (var i=0; i < response.data.length; i++) {
FB.api('/'+response.data[i].id, function(response) {
var userImage = '<img src="https://graph.facebook.com/'+response.id+'/picture?width=32&height=32" style="border-radius: 100em; width: 50px; height: 50px;">';
self.$el.find('.test-list').append('<li class="fb-player" data-face="'+response.id+'">'+userImage+response.name+'</li>');
});
}
});
}
I feel like this is obviously not ideal? I am not sure why this is causing so much lag.

Related

JavaScript last element of HTML collection not defined

I am trying to create a bookable product, where upon selected (= selectbox) a room type, the picture changes to that specific room with good old javascript.
the interesting part is that it works for the first element of the HTML collection, but the last element is giving an undefined and makes it impossible to override.
I am not getting why that is. I tried via the console log to view what I am missing, but I see nothing problematic.
HTML collection:
0: <a href="https://staging.deloftli…09/Steck-coachruimte.jpg" hidefocus="true" style="outline: currentcolor none medium;">​
1: <img class="zoomImg" role="presentation" alt="" src="https://staging.deloftli…09/Steck-coachruimte.jpg" style="position: absolute; top:…none; max-height: none;">
I have the following script:
<script id="bookingschanges">
var activities = document.getElementById("wc_bookings_field_resource");
var image = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[0].firstChild;
var zoompic = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[1];
activities.addEventListener("click", function() {
var options = activities.querySelectorAll("option");
});
activities.addEventListener("change", function() {
if(activities.value == "1949")
{
image.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
image.srcset = ""
zoompic.scr = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
}
console.log(image);
console.log(zoompic);
});</script>
The first element (image) is correct, the second element (zoompic) gives undefined.
To see it live, go to https://staging.deloftlisse.nl/product/vergaderruimte-huren/ and check the console log.
What am I missing here?
Variable zoompic is not defined at the time the variable is declared (its called before the element is created on loading, debug the page and refresh it to see) you will need to use an onload event listener.
https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
As someone else has suggested it would be better to call the image change function in the original javascript to change the image that is selected and you will avoid any issues. This might not be easy though if it is an external library.
EDIT: Added an example of onLoad
window.addEventListener('load', (event) => {
var activities = document.getElementById("wc_bookings_field_resource");
var image = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[0].firstChild;
var zoompic = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[1];
activities.addEventListener("click", function() {
var options = activities.querySelectorAll("option");
});
activities.addEventListener("change", function() {
if (activities.value == "1949") {
image.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
image.srcset = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg 768w";
zoompic.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
}
console.log(image);
console.log(zoompic);
})
});

Open multiple tabs at once with JavaScript

I am trying to open a few tabs at once from my shopping cart page using Javascript. I have looked at a few similar questions and I already know that you can use several window.open(url) but I'm not really looking forward to that... I currently have this code
function buyAll()
{
$.ajax({
url:'Basket',
type:'POST',
data: { listOfWines : $.toJSON(chosenIds), action : 'buyAll' },
success : function(responseText)
{
for (var i = 0; i < allSliders.length; i++)
{
var cellsCollection = document.getElementById(allSliders[i].id).closest('tr').children;
var row = document.getElementById(allSliders[i].id).closest('tr');
$(cellsCollection)
.animate({ padding: 0 }, 800)
.wrapInner('<div />')
.children()
.slideUp(function() { $(row).remove(); });
}
showSnackOpened();
}
});
for(i = 0; i < allSliders.length; i++)
{
var url = allSliders[i].getAttribute('data-redir');
window.open(url, '_blank');
}
}
Forgive me if the code isn't the best or there's a bit of a mix with the jQuery (JS isn't my best-known language) I take an array of elements on the shopping cart and get a particular attribute of these elements, which is the URL I need to redirect the user to. Once I have the list of URLs I try to iterate over them and open the new tabs. However, the browser obviously blocks the pages from the second onward.
Also, Chrome's tabs API won't be of any use to this project, since not all of the users will be using Chrome.
Any help is much appreciated!

Javascript : building an image gallery with backbone

I'm building an image gallery using backbone. User can only view one image at a time, when he navigates next/previous using arrow keys he will see next image in the collection.
I have one top level backbone view called gallery view.
GalleryView = Backbone.View.extend({
initialize: function(options) {
this.collection = PhotosCollection();
}
});
Then for each image I'll create a view
PhotoView = Backbone.View.extend
I'll keep track of current model in collection that is in view, and I'll create photoView for that model.
My question is regarding pagination, or prefetching images that are not in view. I'm not sure about how galleryView can hold multiple PhotoViews and then show the one that is in focus. I'm thinking about building one long div for entire collection and then adding div's for each photo onto it in the indexed position. What would be the strategy of adding removing new images from that div.
Update
I've modified my original code. Though this is obviously untested it should give you a reasonable start. Images start pre-loading as soon as an img element has been created and the src attribute has been set (as mentioned here). They don't have to be attached to the DOM. This revamped version that I've included below will preload images before and after the current image based on the paginationPadding variable.
End Update
When working with things in Backbone it helps to keep all state related information in your your models and then have the UI respond to it. In this case, you can simply keep track of which image you want to display in a model and then in response to a change in the model tracking what photo you want to view, you can simply re-render the photo gallery to display the desired image. It's reactive.
This is a rough implementation but should hopefully give you some idea of how you might implement a gallery where you can click through the photos like this. Of course, you'd still have a bunch of CSS and such to do. It's incomplete. Let me know if you have any questions.
var StateModel = Backbone.Model.extend({
defaults: {
visibleImage: 0
},
initialize: function(attributes, options) {
this.photoCollection = options.photoCollection;
},
setIndex: function(photoIndex) {
if(!this.photoCollection.length) return;
if(photoIndex < 0) {
photoIndex = this.photoCollection.length - 1;
} else if(photoIndex >= this.photoCollection.length) {
photoIndex = 0;
}
this.set('visibleImage', photoIndex);
},
setPrev: function() {
this.setIndex(this.get('visibleImage') - 1);
},
setNext: function() {
this.setIndex(this.get('visibleImage') + 1);
}
});
var PhotoControls = Backbone.View.extend({
tagName: 'div',
initialize: function(options) {
this.stateModel = options.stateModel;
this.paginationPadding = options.paginationPadding;
},
render: function() {
var that = this;
this.$el.empty();
var ctrlStyle = {cursor: 'pointer', display: 'inline-block', padding: '5px 10px', margin: '0px 5px', border: '1px solid #000'};
this.$el.append($('<div><< Previous</div>')
.css(ctrlStyle))
.click(function() {
that.stateModel.setNext();
});
// Display numbers
var visibleImage = this.stateModel.get('visibleImage');
var pgStart = Math.max(visibleImage - this.paginationPadding, 0);
var pgEnd = Math.min(visibleImage + this.paginationPadding, this.stateModel.photoCollection.length - 1);
for(var i = pgStart; i <= pgEnd; i++) {
var $numEl = that.$el.append(
$('<div>' + (i + 1) + '</div>')
.css(ctrlStyle)
.click(function() {
that.stateModel.setIndex(i);
});
if(i == visibleImage) {
$numEl.css({fontWeight: 'bold', textDecoration: 'underline'});
}
);
}
this.$el.$('<div>Next >></div>')
.css(ctrlStyle))
.click(function() {
that.stateModel.setPrev();
});
return this;
}
});
var PhotoView = Backbone.View.extend({
render: function() {
this.$el.html('<img src="' + this.model.get('url') + '" />');
return this;
}
});
var GalleryView = Backbone.View.extend({
tagName: 'div',
initialize: function(options) {
this.paginationPadding = 2;
this.collection = PhotosCollection();
this.renderedViews = {};
this.state = new StateModel(null, {photoCollection: this.collection});
this.photoControls = new PhotoControls({
stateModel: this.state,
paginationPadding: this.paginationPadding
}).render();
},
render: function() {
if(this.photoView) {
this.photoView.remove();
}
// Pre-fetch images before and after current one based on pagination padding
var visibleImage = this.stateModel.get('visibleImage');
var pgStart = Math.max(visibleImage - this.paginationPadding, 0);
var pgEnd = Math.min(visibleImage + this.paginationPadding, this.stateModel.photoCollection.length - 1);
for(var i = pgStart; i <= pgEnd; i++) {
if(!this.renderedViews[fetchModel.cid]) {
// Images will begin fetching as soon as the 'src' attribute is set on the 'img' element rendered by the view
this.renderedViews[model.cid] = new PhotoView(this.collection.at(i));
this.renderedViews[model.cid].render();
}
this.$el.html(this.photoView.render().el);
}
// Attach the view for the current image
var renderModel = this.collection.at(this.state.get('visibleImage'));
if(renderModel && this.renderedViews[renderModel.cid]) {
this.photoView = this.renderedViews[renderModel.cid];
this.$el.html(this.photoView.el);
}
this.$el.append(this.photoControls.el);
return this;
}
});
I'm not sure about how galleryView can hold multiple PhotoViews and then show the one that is in focus.
This is the easy part. As long as the view isnt attached to the DOM it stays invisible, you can instance as many Views you want and only have one of them attached at any time.
I'm thinking about building one long div for entire collection and then adding div's for each photo onto it in the indexed position. What would be the strategy of adding removing new images from that div.
Make that an UL with an LI for each picture, anyway, if you want to pre load images, i guess the next one in line is enough, you dont really need to remove the PhotoViews as long as they arent absurd numbers of photos, just scroll them out of view or hide them, if you wish to actually delete views call remove().

What is the Best Method for Loading a URL Returned from a JSON List in JQuery / Javascript

I am trying to AJAX some content into the #BlogContainer div and cannot seem to get my click function working properly. It seems to be falling apart in the if statement, but I can't figure out why. I have tested with alert and the return from the JSON is coming through as it should. The initial load also works fine its just the iterating through the JSON return and returning the url to the load function that doesn't seem to be working.
The effect that I want to achieve is to load page content (blog entries) into the #BlogContainer div using .load and the URLs from the JSON file which point to the content, iterating to the next with each click on the #Nexus div.
My main question is what is the best method for loading dynamically through a JSON return? I had an each function which did the exact same thing as well (it seemed to return the correct result, but would not actually load the content). I decided to use the for loop instead since what I read indicated it was slightly more efficient, though I doubt it would have much impact in my case.
I am completely new to JS / JQ and previously had everything functioning the way I wanted using JQueryTools, but I thought the best way to learn would be to do it myself (with a little help of course :) ).
HTML
<div id="BlogContainer"></div>
<div id="NexusRef" alt="03151201.htm">'03151201.htm'</div>
JSON
{
"0": "'03151201.htm'",
"1": "'01191201.htm'",
"2": "'00000103.htm'",
"3": "'00000102.htm'",
"4": "'00000101.htm'"
}​
Javascript
$(function() {
$('#Navigation').load('navi.htm');
$('#BlogContainer').load('03151201.htm');
$('#TT1').load('ajtt1.htm');
$('#TT2').load('ajtt2.htm')
});
$(function() {
$('#Nexus').click(function() {
$.getJSON("BlogRoll.json", function(data) {
var i = 0;
var n = ($('#NexusRef').html());
var length = data.length;
for (i = 0; i < length; i++) {
if (data[i] == n) {
$(function() {
$('#BlogContainer').load(data[i + 1]);
});
} else {
alert(data[i + 1]);
}
}
});
});
});​
this should do it
$(function() {
//assuming you have this JSON to work with:
var pages = {
"0": "'03151201.htm'",
"1": "'01191201.htm'",
"2": "'00000103.htm'",
"3": "'00000102.htm'",
"4": "'00000101.htm'"
}
//add a click handler to your button
$('#Nexus').on('click', function() {
//prepare a storage url
var url;
//pick out first link, copy, and remove it
//works like Array.shift()
for (var key in pages) {
if (pages.hasOwnProperty(key)) {
url = pages[key];
delete pages[key]
break;
}
}
//Do a GET to retrieve contents and append to container
$.get(url, function(contents) {
var contents = url;
$('#BlogContainer').append(contents);
});
});
});​

Dashcode webapp flickers upon transition, only in iPhone Safari

I created a simple RSS web app using the template in Dashcode. Problem is, when choosing items in the list from the feed the transition flickers (even with the default settings). I am guessing its because of the images in the posts.
I tried disabling the transitions completely but even then I get a flickering when returning to the list. This problem does not appear to affect safari on OSX only on the iphone.
Here is the code that I think is responsible:
var topStories = parseInt(attributes.topStories, 30);
function load()
{
dashcode.setupParts();
// set today's date
var todaysDate = document.getElementById("todaysDate");
todaysDate.innerText = createDateStr(new Date()).toUpperCase();
setupFilters("headlineList");
// This message checks for common errors with the RSS feed or setup.
// The handler will hide the split view and display the error message.
handleCommonErrors(attributes.dataSource,
function(errorMessage) {
var stackLayout = document.getElementById("StackLayout")
if (stackLayout) {
stackLayout.style.display = 'none';
}
showError(errorMessage);
});
// get notifications from the stack layout when the transition ends
document.getElementById("StackLayout").object.endTransitionCallback = function(stackLayout, oldView, newView) {
// clear selection of lists when navigating to the first view
var firstView = stackLayout.getAllViews()[0];
if (newView == firstView) {
document.getElementById("headlineList").object.clearSelection(true);
}
}
}
function articleClicked(event)
{
document.getElementById("StackLayout").object.setCurrentView("articlePage", false, true);
}
function backToArticlesClicked(event)
{
document.getElementById("StackLayout").object.setCurrentView("frontPage", true);
}
function readMoreClicked(event)
{
var headlineList = dashcode.getDataSource('headlineList');
var secondHeadlines = dashcode.getDataSource("secondHeadlines");
var selectedItem = null;
if (headlineList.hasSelection()) {
selectedItem = headlineList.selectedObjects()[0];
} else if (secondHeadlines.hasSelection()) {
selectedItem = secondHeadlines.selectedObjects()[0];
}
if (selectedItem) {
var link = selectedItem.valueForKeyPath('link');
// If the link is an object, not a string, then this may be an ATOM feed, grab the actual
// href from the href attr
if (typeof(link) == 'object') {
link = selectedItem.valueForKeyPath('link.$href');
// If the link is an array (there is more then one link), just grab the first one
if (DC.typeOf(link) == 'array') {
link = link[0];
}
}
window.location = link;
}
}
var headlineListDataSource = {
// The List calls this method once for every row.
prepareRow: function(rowElement, rowIndex, templateElements) {
if (rowIndex >= topStories) {
templateElements['headlineDescription'].style.display = 'none';
templateElements['headlineTitle'].style.fontSize = '15px';
}
}
};
The following CSS rule fixed all of my "-webkit-transition" animation flickering issues on the iPad:
body {-webkit-transform:translate3d(0,0,0);}
I am not sure how well that applies to your problem but in general you should set the backface visibility to hidden if not needed. That will most likely kill all flickering on a page.
-webkit-backface-visibility: hidden;

Categories