Dom string doesn't change after manipulation with jQuery attr - javascript

I'm making a js-based epub reader as a weekend project and I'm trying to change the src attribute of the images on each of the book's pages from image urls to data URI's loaded from the epub zip. Here's my function:
//page contents is just an html string of the book's page
pageContents = GlobalZipLoader.load('epub.zip://' + pageLocation);
pageContents = replaceImages(pageContents)
...
function replaceImages(pageContents){
$(pageContents).find('img').each(function(){
var domImage = $(this);
//this is something like ".../Images/img.jpg"
var imageLocation = domImage.attr('src');
//this returns the proper data-uri representation of the image
var dataUri = GlobalZipLoader.loadImage('epub.zip://' + imageLocation);
//this doesn't seem to "stick"
domImage.attr('src', dataUri);
});
return pageContents;
}
The returned pageContents from the replaceImages functions still has the old src attributes. I can provide more detail if needed but any help would be very much appreciated.
Correct answer thanks to The System Restart and Ilia G:
function replaceImages(pageContents) {
newContent = $(pageContent);
... manip ...
return newContent;
}

You don't need to clone it. Simply set pageContents = $(pageContents);, then perform your image replacement on pageContents, then return pageContents.html();

you should try to change the image src after image load complete;
and I think this is happening with in loadImage function.
According to your update question:
You don't need any clone(), I think. Just store pageContents in a tempContents variable and use that variable

Since pageContents is just a string, you'll need to return the modified version of it. Try this:
function replaceImages(pageContents){
// save jQuery object
var $pageContents = $(pageContents);
$pageContents.find('img').each(function(){
var domImage = $(this);
//this is something like ".../Images/img.jpg"
var imageLocation = domImage.attr('src');
//this returns the proper data-uri representation of the image
var dataUri = GlobalZipLoader.loadImage('epub.zip://' + imageLocation);
//this doesn't seem to "stick"
domImage.attr('src', dataUri);
});
// return contents of the modified jQuery object
return $pageContents.html();
}

Related

Show an image stream to client in blazor server without javascript?

Right now i have the following lines of code:
private async Task SetImageUsingStreamingAsync()
{
var imageStream = await GetImageStreamAsync();
var dotnetImageStream = new DotNetStreamReference(imageStream);
await JSRuntime.InvokeVoidAsync("setImageUsingStreaming",
"image1", dotnetImageStream);
}
Above snippet gets a Stream, and repackages that stream into DotNetStreamReferance, and then passes that DotNetStreamReferance to a JS function:
async function setImageUsingStreaming(imageElementId, imageStream) {
const arrayBuffer = await imageStream.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
document.getElementById(imageElementId).src = url;
}
which populates an html element by ID. This is all fine, and works, but if i want to have a loadingscreen while i get the streams from the DB things get messy:
if(isloading)
{
show loadingscreen
}
else
{
foreach(string in listofstrings)
{
<img id="somename"></img>
}
}
the img element does not "show up", and causes an error in the js snippet if the bool isloading is not changed to false BEFORE populating the images on the site. Which kind of defeats the purpose with a loadingscreen.
Is there a smarter way of doing this? Preferably without using javascript, since i dont really know that language and cannot modify it to my needs.
The above code is a direct reference from .net documentation on blazor(not the loading part, but the stream to image part): https://learn.microsoft.com/en-us/aspnet/core/blazor/images?view=aspnetcore-6.0
if (isloading)
{
// show loadingscreen
}
else
{
// Populate a list with image elements...
List imageElements = new List();
foreach(string in listofstrings)
{
Element image = new Element("img");
image.Id = "somename";
imageElements.Add(image);
}
// ...and append the list to the DOM using RenderElementsAsync
// to ensure that it is rendered only when the list is fully
// populated.
await JSRuntime.InvokeAsync("setImageUsingStreaming", imageElements, dotnetImageStream);
}
The idea is to populate an in-memory list of elements, and then append the list to the DOM in one go, by rendering it asynchronously using RenderElementsAsync.
Note: In your original code, you should invoke the JavaScript function setImageUsingStreaming() asynchronously. That is, you should use InvokeAsync instead of InvokeVoidAsync.
Let me know if this helps...
I figured it based on Yogi's answer in the comment section of my post:
there is absolutely no need to use JS here, i dont know why the blazor docs prefer it that way.
a simple sample on how i did:
private async Task PopulateImageFromStream(Stream stream)
{
MemoryStream ms = new MemoryStream();
stream.CopyTo(ms);
byte[] byteArray = ms.ToArray();
var b64String = Convert.ToBase64String(byteArray);
string imageURL = "data:image/png;base64," + b64String;
}
}
add the imageURL to an array, or simply declare it globaly to get it in HTML:
<img src="#imageURL">

Unable import existing data in jSignature

This is the code I'm saving my jSignature value in my database:
var datapair = $('.sign-wrapper').jSignature();
datapair.bind('change', function(e){
var data = datapair.jSignature("getData", "svgbase64");
var i = new Image()
i.src = "data:" + data[0] + "," + data[1];
$(this).prev('input').val(i.src);
});
So I'm having following value in my DB:
data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4xIiB3aWR0aD0iODkiIGhlaWdodD0iMTEyIj48cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNIDEgMTExIGMgMC4wNCAtMC4xNCAxLjAyIC01LjQ1IDIgLTggYyAyLjM0IC02LjA4IDUuOSAtMTEuODcgOCAtMTggYyAxLjg2IC01LjQ0IDMuMDkgLTExLjIyIDQgLTE3IGMgMS4xIC02Ljk4IDAuODggLTEzLjk4IDIgLTIxIGMgMS4yNSAtNy44MyAzLjA0IC0xNS4zOSA1IC0yMyBjIDAuNzEgLTIuNzYgMS43NiAtNS41MiAzIC04IGMgMS4wNSAtMi4wOSAyLjUyIC00LjA4IDQgLTYgYyAxLjg5IC0yLjQ2IDMuOTIgLTQuOTIgNiAtNyBjIDAuODEgLTAuODEgMi4yNiAtMi4yMiAzIC0yIGMgMS43NyAwLjUzIDUuMzcgMyA3IDUgYyAyLjM3IDIuOTEgNC40NiA3LjEzIDYgMTEgYyA1LjQ2IDEzLjc5IDkuMzIgMjguNTIgMTUgNDIgYyAyLjIzIDUuMjkgNi4zOCA5Ljc2IDkgMTUgYyAzLjQyIDYuODQgNS43NiAxNC43NiA5IDIxIGwgNCA0Ii8+PHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTSAyMCA2NCBjIDAuMTEgLTAuMTIgMy43MyAtNS40IDYgLTcgYyAyLjk4IC0yLjEgNy4yMSAtMy44NSAxMSAtNSBjIDcuMDcgLTIuMTQgMTQuNzggLTMuNzEgMjIgLTUgYyAxLjkyIC0wLjM0IDYgMCA2IDAiLz48L3N2Zz4=
So while reloading the page I'm having the above base64 image data url in a hidden variable.
I tried to load the data to the existing signature canvas using below code:
// If value exist load existing
var src = $(this).prev('input').val();
if(src != '') {
datapair.jSignature("importData", src);
}
But it is not working and I am getting following error in console:
Uncaught Error: jSignature is unable to find import plugin with for
format 'image/svg+xml;base64'
The problem occurs when your data is not recognized by the plugin.
The reason why it throws an error is, the format 'image/svg+xml;base64' is not supported by the importer, there is only an exporter support for it.
Reference:
https://github.com/brinley/jSignature#data-import--export-and-plugins
I suggest you store the data using base30 because there is support for both export and import.
var data = datapair.jSignature("getData", "base30 ");
setData takes two arguments - data object, data format name. When data object is a string formatted in data-url pattern you don't need to specify the data dormat name. The data format name (mime) will be implied from the data-url prefix. See example below for that. Returns (in a traditional jQuery chainable way) jQuery object ref to the element onto which the plugin was applied.
You can store the value in base64 and load it. I load the value in base64 in an input and through Jquery I load it in the object.
// Creo el objeto de firma.
$("#jq_sig").jSignature
({
// width/height of signature pad
width: 497,
height: 200,
// Format bootstrap 4
cssclass: "bg-light border",
lineWidth: 2,
color: "black",
});
// I take the value of the form field that I previously loaded with PHP.
var firma = $('#form input[name=firma]').val();
if (firma.length > 0)
{
$("#jq_sig").jSignature("setData", 'data:image/png;base64,' + firma);
console.log('Signature loaded.');
}
else
{
console.log('Signature not loaded.');
}
You can save to your database in any format. Then you tell 'setdata' what kind of format it is.

Get Filename from URL and Strip File Extension

I need to get just the filename without the extension from a url and can't quite get there.
Here's my url in question:
https://www.mealenders.com/shop/index.php/shop/solo-pack.html
Here's what I've tried:
function () {
var value={{Page Path}}.split("/");
return value.reverse()[0];
}
That almost gets me there as it returns "solo-pack.html". What else do I need to do to get rid of the ".html" for this?
Thanks in advance.
You can do the following using javascript. Pop returns the last element which is a string, and then you can use the replace function to get just the filename without .html on the end.
function getFilename () {
return {{ Page Path }}.split('/').pop().replace('.html', '');
}
I see that {{ Page Path }} is probably some templating language but you could modify the above script, to get the current URL and then get the filename as so.
function getFilename () {
return window.location.href.split('/').pop().replace('.html', '');
}
Furthermore you could make it more dynamic to handle any file extension with the following. You need to get the index of the period using indexOf and then sub string from the start of the filename up to the position of the period.
function getFilename () {
var filename = window.location.href.split('/').pop();
return filename.substr(0, filename.lastIndexOf('.');
}
function getFileName(url) {
return url.split("/").pop().split(".")[0];
}
var url = "https://www.mealenders.com/shop/index.php/shop/solo-pack.html";
console.log(getFileName(url));
function () {
var value={{Page Path}}.split("/");
var fileName= value.reverse()[0].split('.')[0];
return fileName;
}
If you need to get rid of any extension, you can use .replace() with regular expression:
var url = "https://www.mealenders.com/shop/index.php/shop/solo-pack.html";
function getFilename (path) {
return path.toString().split('/').pop().replace(/\.\w+$/, '');
}
console.log(getFilename(url));
This will for example change test/index.html into index but index.php.default into index.php and also test.name.with.dots.txt -> test.name.with.dots
Short and sweet:
"https://url/to/file/solo-pack.html".split(/[\\/]/).pop().replace(/\.[^/.]+$/, "")
Returns:
solo-pack

(Image in loop) Huge array of images and wants to display if image is really present then show the images otherwise delete/hide the image node

I have a huge array of images
Actually I am bit worry about checking the image that really exist and also want do something if image present
and if not found I am doing some other work.
Note: If one image is checking on server and takes 3 minutes
then how to handle in loop for other images.
I am just found below image if image not exist :-
Please give a standard way that can be used for nth number of images in array.
var urlArray = [{productImage:'http://www.queness.com/resources/images/png/appleeeeeee_ex.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_cooooooolor_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'}];
<div>
<img src=""> // actually this comes through urlArray
</div>
Thanks in advance!
If you want to check image URL exist or not, You can try this with javaScript,
It will check all URLs one by one and call the callBack function, then it will call URL check for next URL.
Next URL will be check after one completes, no matter how much time it will take.
function isImageExists(imgSrc, callBackAfterCheck) {
var img = new Image();
img.onload = function() { callBackAfterCheck(true) };
img.onerror = function() {callBackAfterCheck(false) };
img.src = imgSrc;
}
var urlArray = [{productImage:'http://www.queness.com/resources/images/png/appleeeeeee_ex.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_cooooooolor_272x92dp.png'},{productImage:'https://www.google.co.in/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'}];
var imageIndex = 0;
function callBackAfterCheck(status){
if(status == true){
//do your stuff here
console.log('image Exist');
}else{
//do your stuff here
console.log('image not Exist');
}
imageIndex++
if(imageIndex < urlArray.length){
var imageUrl = urlArray[imageIndex].productImage;
isImageExists(imageUrl, callBackAfterCheck)
}
}
var imageUrl = urlArray[imageIndex].productImage;
isImageExists(imageUrl, callBackAfterCheck);
Here you can pass your image source and the function you want to execute after checking the source.
It will check your image existence and you can get the status (true or false) in your callBack function,
Now you can do your stuff according to your need in callback function.

how to use jQuerys each with checking an image exists

I have some source that actually drives me crazy...
First, I have a object like this:
var extensions = {"jpg":".jpg","JPG":".JPG","png":".png","PNG":".PNG"};
Now I want to iterate the object to check if a image with one of these extensions exists. The source for this looks like:
var imgURL = "some/path/";
var imgName = "myFileName";
var counter = 0;
$.each(extensions, function( key, imgExtension ){
var tmpImgUrl = imgURL + imgName + imgExtension;
console.log('test');
$( ".imageCheck" ).unbind().attr( "src", tmpImgUrl ).error( function(){
console.log( tmpImgUrl );
counter++;
if( counter >= Object.keys( extensions ).length){
return false;
}
});
});
This each should take the image name and try to test if there is an error for each extension. Actually it does what it should do. Only thing is, counter doesn't gets counted up and the console log appears wrong.
I expact the log should look like:
test
some/path/myFileName.jpg
test
some/path/myFileName.JPG
test...
and so on. But it appers like:
test
test
test
test
some/path/myFileName.jpg
some/path/myFileName.JPG
...
actually I want to use the image check to return a break (false) for the each so not all extensions get checked. (performance)
And also I really need the counter when exiting the check part....
Any guesses what going wrong here ?
Create a loader to do the image test.
Init the index to test.
Create an <img>, add onload and onerror behavior. onload will notify the user, or you can event let the loader accept callbacks for onload to call it, onerror will try to load image with next type.
The tryLoad function first check if index is equal to extensions.length, if it is, which means all possibles are failed, notify user the image load failed, and do some fail fallback or something.
Otherwise, it use current index with give fileName and url to create imagepath, set it to img.src and increase the index for next attempts.
call the tryload to start the process.
Now the image stops attempt when current url is valid, and will try the extesions one-by-one until no other can be used.
// Inits, can also be put into the loader.
var extensions = ['.jpg', '.JPG', '.png', '.PNG'];
var imgUrl = "some/path/";
var loader = function(fileName, success, fail) {
// Start at first image type.
var index = 0;
// Create a img for loading.
var $image = $('<img>');
// Add success and fail behavior to the image.
$image
.load(function() {
// Do some notification or....
console.log('success', $image.attr('src'));
// If you give a success callback, call it
// Or you can write the logic here.
if ($.isFunction(success)) {
success();
}
})
.error(function() {
console.log('fail', $image.attr('src'));
// Try to load next image
tryLoad(index);
});
// The function for attempts.
var tryLoad = function() {
// When all attemps tried.
if (index === extensions.length) {
alert('Boom, all failed');
// Do some fallbacks....
// $image.remove();
// If you give a fail callback, call it
// Or you can write the logic here.
if ($.isFunction(fail)) {
fail();
}
return;
}
// Create url to load.
var tmpImgUrl = imgURL + fileName + extensions[index];
console.log('test', tmpImgUrl);
$image.attr('src', tmpImgUrl);
++index;
};
// Start the first attempt.
tryLoad();
// return jquery wrapper of image. It'll have image only if any of the test is passed.
return $image;
};
You won't want to use jQuery's .each to loop through an object - the proper way to do this is using javascript's for in loop.
Looping through your extensions object should look like this:
for(var key in extensions){
console.log("Key", key, "Value",extensions.key);
}
You can then do whatever it is you need with those keys or values using the above looping construct.

Categories