Я застрял!.. У меня есть сценарий jQuery, в котором я заполняю галерею изображений из файла JSON, это что-то вроде лайтбокса. Но я хочу иметь название для своих изображений, поэтому я создал второй файл JSON, в котором я просто сохраняю имя изображения и заголовок. Я хочу в цикле проверить второй JSON, есть ли там имя изображения, и если да, вернуть текст, который является заголовком...
Это titles.json
[
{"Img" : "fun1_001.jpg", "title" : "Title of image 001"},
{"Img" : "fun1_002.jpg", "title" : "title of image 002"},
{"Img" : "fun1_003.jpg", "title" : "title of image 002"}
]
Что я хочу сделать, так это просмотреть все изображения, заполнить галерею и в то же время, ЕСЛИ имя изображения присутствует в titles.json, поместить этот заголовок в цикл.
Это jQuery, который заполняет галерею. он вызывает функцию с именем GetTitle(имя изображения,папка)
//Load the gallery if you click the dropdown.
$(document).on('click','#zmenu>div.item', function(){
$('#dc_preload_image').empty();
let zGallery=$(this).attr('data-value');
$.getJSON('JSON/Gallerys/'+zGallery+'.json', { get_param: 'value' }, (data)=>{
$.each(data, function(index, element) {
console.info('anrop')
$('.galpop-info').galpop();
$("#dc_preload_image").preloader();
$('#dc_preload_image').append('<li><a class = "galpop-info" data-galpop-group = "info" title = "'+GetTitle(element.imageName, zGallery)+'" href = "BKND/Galleries/'+zGallery+'/pict/'+element.imageName+'"><img src = "BKND/Galleries/'+zGallery+'/tmb/'+element.imageName+'" alt = "'+element.imageName+'" id = "'+element.imageName+'" class = "corners" /></a></li>');
});
});
});
Это функция, которая должна сопоставлять имя изображения из цикла и возвращать заголовок.
function GetTitle(a,z){
$.getJSON('BKND/Galleries/'+z+'/titles.json', (adata) => {
// Handle the JSON data here
let zTitle
$.grep(adata, function(value) {
if (value.Img = a){
zTitle = value.title;
}
else{zTitle = " ";}
});
console.info(zTitle)
return zTitle
});
}
Как бы я ни старался, я не могу сделать это правильно. Я почти решил проблему, когда поместил код внутри функции в цикл, но затем он повторил его и вместо этого создал дубли изображений в галерее.
Кажется, на Stackoverflow уже есть ответ на вашу проблему. Вы можете использовать обратный вызов (вы передаете функцию в качестве параметра своей функции и вызываете функцию обратного вызова с параметром заголовка вместо возврата заголовка). В противном случае вы можете использовать обещание, но вам нужно будет использовать асинхронность.
Пример обратного вызова:
function GetTitle(a, z, callback) {
$.getJSON("BKND/Galleries/" + z + "/titles.json", (adata) => {
// Handle the JSON data here
let zTitle = " ";
$.each(adata, function (_, value) {
if (value.Img == a) {
zTitle = value.title;
}
});
callback(zTitle);
});
}
И как его использовать:
GetTitle(element.imageName, zGallery, (title) => {
console.info(title)
})
(Это серьезное переписывание моего ответа — предыдущие версии этого поста вы можете найти здесь.)
Ваша первоначальная попытка включала бы частые вызовы $.getJSON('BKND/Galleries/'+z+'/titles.json)
(по крайней мере, один вызов для каждого изображения в вашей галерее), чтобы получить заголовки. Лучшим подходом было бы получить информацию один раз и сохранить ее в словаре. Ниже я подготовил небольшую демонстрацию, основанную на общедоступных данных, имитирующую очень простую галерею:
$.when(
$.getJSON("https://dummyjson.com/users/").then(d=>d.users.map(u=>u.username)),
$.getJSON("https://dummyjson.com/users/").then(d=>Object.fromEntries(d.users.map(u=>[u.username,u.firstName+" "+u.lastName])))
).then((usrs,ttls)=>
// Now we can build the gallery:
$("#container").html(usrs.map(u=>
`<img src = "https://dummyjson.com/icon/${u}/128" title = "${ttls[u]}">${ttls[u]}`
).join("<br>"))
);
<script src = "https://code.jquery.com/jquery-3.7.1.min.js"></script>
<div id = "container"></div>
Обновление в ответ на сообщение «ответ» ОП:
Вы можете использовать опцию async:false
в вызове Ajax, но это создаст негативный пользовательский опыт. Браузер фактически зависает во время получения данных.
Тем не менее, я взял ваш код и кое-где подправил его, чтобы он работал. В демо-версии в качестве общедоступного источника данных используется https://dummyjson.com.
(( Я бы по-прежнему рекомендовал вам использовать конструкцию $.when(...).then()
, как показано в моем фрагменте выше. ))
function GetTitle(galleryFolder){
// Handle the JSON data here
var cArray = []
$.ajax({
'async': false, // **** Attention: this will block your page while the data is being retrieved! *****
'global': true,
// 'url': 'BKND/Galleries/'+galleryFolder+'/titles.json',
'url': 'https://dummyjson.com/'+galleryFolder,
'dataType': "json",
'success': function (data) {
cArray = data[galleryFolder].map(e=>({Img:e.image, title:e.name || e.firstName+" "+e.lastName })); // mimick the titles array ...
}
});
return cArray;
}
//Ladda in galleriet om man väljer i listrutan.
$(document).on('click','#zmenu>div.item', function(){ //When you click on dropdown with ID #zmenu
$('#dc_preload_image').empty(); //initialize preloader
let zGallery=$(this).attr('data-value'); //Get attr data-value from zmenu, to determine which folder you want to access
let titlesArray = GetTitle(zGallery); //load JSON array into a variable
ttls=Object.fromEntries(titlesArray.map(e=>[e.Img,e.title])); //convert JSON array to an object
// console.info(ttls);return;
// $.getJSON('JSON/Gallerys/'+zGallery+'.json', { get_param: 'value' }, (data)=>{ //Get the JSON with all the imagenames from backend
$.getJSON('https://dummyjson.com/'+zGallery, { get_param: 'value' }, (data)=>{ //Get the JSON with all the imagenames from backend
// a little adjustment for my dummy data:
data=data[zGallery];
$.each(data, function(index, element) { //loop through each imgagename and build the htmls structure inside the gallery div
element.imageName=element.image; // adjustement for my dummy data ... ;-)
var title =`Title for ${element.imageName} is: ${ttls[element.imageName]?`${ttls[element.imageName]}`:'unfortunately unknown'}.`;
$('#dc_preload_image').append('<li><a class = "galpop-info" data-galpop-group = "info" title = "'+title
+'" href = "BKND/Galleries/'+zGallery+'/pict/'+element.imageName+'"><img src = "'+element.imageName+'" alt = "'+element.imageName+'" id = "'+element.imageName+'" class = "corners" /></a></li>');
});
// the following expressions only need to be executed once - after the $.each() loop!
// ========================================================
// $('.galpop-info').galpop();
// $("#dc_preload_image").preloader();
});
});
div.item { padding: 6px; margin:6px; display: inline-block; background-color: #ddd;
border: 1px solid #888; cursor:pointer }
li img {height: 100px}
<script src = "https://code.jquery.com/jquery-3.7.1.min.js"></script>
<div id = "zmenu">
<div class = "item" data-value = "users">users</div>
<div class = "item" data-value = "recipes">recipes</div>
</div>
<div id = "dc_preload_image"></div>
Помимо отказа от использования async:false
, я бы также рекомендовал вам заменить частые .append()
звонки одним .html()
звонком. Таким образом, полная уценка HTML будет анализироваться и отображаться браузером только один раз, вместо повторной обработки страницы для каждого отдельного изображения.
Я не удержался и написал версию async
с $.when(...).then()
, см. ниже:
function GetTitle(galleryFolder){ // this function returns a "thenable" ...
// 'BKND/Galleries/'+galleryFolder+'/titles.json'
return $.getJSON('https://dummyjson.com/'+galleryFolder )
.then(data => data[galleryFolder].map(e=>({Img:e.image, title:e.name || e.firstName+" "+e.lastName }))) // mimick the titles array ...
}
//Ladda in galleriet om man väljer i listrutan.
$(document).on('click','#zmenu>div.item', function(){ // When you click on dropdown with ID #zmenu ...
let zGallery=$(this).attr('data-value'); // get gallery name
$.when( // ***** read both .json files at the same time: *****
GetTitle(zGallery).then(d=>d.map(e=>[e.Img,e.title])), // get titles and ...
$.getJSON('https://dummyjson.com/'+zGallery).then(d=>d[zGallery].map(e=>e.image)) // image names.
).then((ttlarr,imgs)=>{ // after the data has arrived in parallel streams, process it:
const ttls=Object.fromEntries(ttlarr); // convert array to lookup dictionary
$("#dc_preload_image").html(imgs.map((img,i)=> // create whole HTML markdown in one go
`<li><a class = "galpop-info" data-galpop-group = "info"
title = "Title for ${img} is: ${ttls[img]?`${ttls[img]}`:'unfortunately unknown'}"
href = "BKND/Galleries/'+zGallery+'/pict/${img}"><img
src = "${img}" alt = "${img}" id = "${img}" class = "corners"/></a>${ttls[img]}</li>`
).join("\n")); //.preloader() ;
// $('.galpop-info').galpop();
});
});
div.item { padding: 6px; margin:6px; display: inline-block; background-color: #ddd;
border: 1px solid #888; cursor:pointer }
li img {height: 100px}
<script src = "https://code.jquery.com/jquery-3.7.1.min.js"></script>
<div id = "zmenu">
<div class = "item" data-value = "users">users</div>
<div class = "item" data-value = "recipes">recipes</div>
</div>
<div id = "dc_preload_image"></div>
Да!! На самом деле это были мои мысли, когда я пытался решить эту проблему... зачем мне снова и снова обращаться ко второму JSON, это не казалось хорошей идеей. Я сделал именно то, что вы предложили. Вместо этого я сохранил JSON в переменной. и пытаются получить доступ к этой переменной, когда я перебираю изображения.. НО!. как мне получить доступ к другой части переменной... я проверил ее, и это действительно массив JSON... но я просто хочу сравнить имя изображения, а затем извлечь заголовок -
Я действительно очень близок..... !!! Мне пришлось изменить ваш ответ, но я получаю все имена изображений в качестве ключей. там просто написано, что все неизвестны,,,
посмотрите мой вставленный код здесь: Pastecode.dev/s/n53rrgqd
В getTitle()
вы совершаете классическую ошибку, возвращая (и используя) переменную cArray
до того, как она получит свое содержимое в обратном вызове вашего $.ajax()
вызова. У меня была причина использовать конструкцию $.when(...).then()
! 😉
Только сейчас я заметил, что вы установили свойство async
: никогда не рекомендуется использовать async:false
в вызове $.ajax()
, так как это фактически заморозит браузер для ваших пользователей.
Причина, по которой ваш скрипт в Pastecode.dev не работает, заключается в том, что вы определяете переменную и ссылаетесь на нее Img
, но никогда не инициализируете ее. И: пожалуйста, всегда публикуйте весь свой код во фрагменте кода в своем вопросе в Stackoveflow!
Спасибо @Carsten. Извините, я обычно не публикую много сообщений о переполнении стека, я просто использую его для чтения. Я постараюсь добавить ответ, включающий больше кода. Если бы вы могли посмотреть на это и увидеть, где я ошибаюсь. я отказался от доступа к файлу JSON для каждого изображения, чтобы загрузить его в переменную и работать с ней, так что я продвинулся гораздо дольше, чем когда начал.
Спасибо, Карстен, теперь у меня все заработало. Я внес некоторые изменения после ваших заметок и снова изменил async на false, потому что, когда я проверял объект ttls, он был пуст. Я знаю, что async:false — это нехорошо, но, честно говоря, единственное, что происходит, — это то, что он читает JSON и помещает его в переменную, прежде чем перейти к циклу. Я постараюсь улучшить код, но мне нужно узнать больше о $.when.() $.then()
еще раз спасибо.. теперь он работает именно так, как я и предполагал.
Хорошо. Итак, вот как далеко я зашел.
function GetTitle(galleryFolder){
// Handle the JSON data here
var cArray = []
$.ajax({
'async': false,
'global': true,
'url': 'BKND/Galleries/'+galleryFolder+'/titles.json',
'dataType': "json",
'success': function (data) {
cArray = data
}
});
return cArray;}
//Ladda in galleriet om man väljer i listrutan.
$(document).on('click','#zmenu>div.item', function(){ //When you click on dropdown with ID #zmenu
$('#dc_preload_image').empty(); //initialize preloader
let zGallery=$(this).attr('data-value'); //Get attr data-value from zmenu, to determine which folder you want to access
let titlesArray = GetTitle(zGallery); //load JSON array into a variable
var Img //initilase Img?
var ttls=Object.fromEntries(titlesArray.map(e=>[e.Img,e.title])); //convert JSON array to an object
$.getJSON('JSON/Gallerys/'+zGallery+'.json', { get_param: 'value' }, (data)=>{ //Get the JSON with all the imagenames from backend
$.each(data, function(index, element) { //loop through each imgagename and build the htmls structure inside the gallery div
var Titles =`Title: ${element.imageName} is ${ttls[Img]?`"${ttls[Img]}"`:'unfortunately unknown'}.`
$('.galpop-info').galpop();
$("#dc_preload_image").preloader();
$('#dc_preload_image').append('<li><a class = "galpop-info" data-galpop-group = "info" title = "'+Titles+'" href = "BKND/Galleries/'+zGallery+'/pict/'+element.imageName+'"><img src = "BKND/Galleries/'+zGallery+'/tmb/'+element.imageName+'" alt = "'+element.imageName+'" id = "'+element.imageName+'" class = "corners" /></a></li>');
});
});
});
Теперь все работает так, как я предполагал, я не бомбардирую файл JSON (вероятно, ускоряя процесс), а вместо этого работаю с ним как с объектом. все изображения обработаны (я проверил с помощью console.info), но я думаю, что с этой строкой что-то не так
$.each(data, function(index, element) { //loop through each imgagename and build the htmls structure inside the gallery div
var Titles =`Title: ${element.imageName} is ${ttls[Img]?`"${ttls[Img]}"`:'unfortunately unknown'}.`
потому что все изображения возвращаются со строкой «к сожалению, неизвестно» Есть ли лучший способ сделать это, подскажите, пожалуйста, неосведомленному :)
Этот вопрос похож на: Как вернуть значение из функции, которая вызывает $.getJSON?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.