Вход

Просмотр полной версии : Найти в родителе элемент с заданным классом


tadjik1
28.06.2011, 10:58
Добрый день.
Бьюсь на следующей задачей:

структура хтмл такая:

<h3 class="hide-next">1</h3>
<div>
<h4 class="hide-next">2</h4>
<div>
<h5 class="hide-next">3</h5>
<div>
</div>
</div>
</div>

и так далее.

так вот, мне нужно при клике на элементе hide-next найти всех его предков hide-next`ов. и их значение поместить в хэш-тэг страницы, чтобы при клике на самый нижний заголов в адресе получилось http://***.ru#1/2/3

получается пока что-то такое:

$('.hide-next').click(function() {

$(this).parents()
.map(function () {
return this.prev().text;
})
.get().join("/");

window.location.hash = parentEls;


});

я толкьо начинаю изучать jquery, абсолютно не понимаю, что в этом коде не так, в переменную ничего не записывается

Sweet
28.06.2011, 11:12
Круто! Даже в доку посмотрел. Только не туда посмотрел: там пример ниже показывает, что можно в parents селектор задавать:.parents(".hide-next")

nikita.mmf
28.06.2011, 11:27
.map(function () {
return $(this).text();
})

tadjik1
28.06.2011, 11:30
$('.hide-next').click(function() {
var parentEls = $(this).parents('.hide-next')
.map(function () {
return $(this).text();
})
.get().join("/");
window.location.hash = parentEls;
});

переменная пустая, в хэш тэг ничего не записывается :(
то ли поиск не производится, то ли надо чуть другим путём идти. первоначально идея была такая, найти предка и для каждого предка вернуть содержимое предыдущего элемента.

tadjik1
28.06.2011, 12:18
$('.hide-next').click(function() {

var parentEls = $(this).parents('.hide-next')
.map(function () {
return $(this).text();
})
.get().join("/");
console.info(parentEls);
//window.location.hash = parentEls;


});

вот такой код выводит в консоль пустые строчки.

tadjik1
28.06.2011, 12:25
может быть это из-за того, что .hide-next не является родителем нашего элемента, по которому кликнули?

nikita.mmf
28.06.2011, 13:03
$('.hide-next').click(function() {
var parentEls = $(this).parents("div").children('.hide-next')
.map(function () {
return $(this).text();
})
.get().join("/");
console.info(parentEls);
//window.location.hash = parentEls;
});

tadjik1
28.06.2011, 13:29
о, так работает. только это не до конца решает задачу. дело в том, что в одном диве может быть много элементов hide-next, кроме того, при клике на них висит функция, которая добавляет класс эктив и разворачивает следующий за ним элемент. конкретная задача — «запомнить весь путь до интересующего нас хайдера»

tadjik1
28.06.2011, 13:35
<h3 class="hide-next">1</h3>
<div>
<p>111</p>
<h4 class="hide-next">11</h4>
<div>
<p>12123</p>
<h5 class="hide-next">asda</h5>
<ul>
<li>a</li>
<li>b</li>
</ul>
</div>
<h4 class="hide-next">22</h4>
<p>12123</p>
</div>
<h3 class="hide-next">2</h3>
<div>
<p>12123</p>
<h5 class="hide-next">asda</h5>
<div>
<span class="hide-next">111</span>
<p>123123</p>
</div>
</div>
<h3 class="hide-next">3</h3>
<div>
<p>12123</p>
<h5 class="hide-next">asda</h5>
<ul>
<li>a</li>
<li>b</li>
</ul>
</div>

вот в это констукции по клику на хайдер внутри верхнего дива (по одному из двух) функция вернет значения обоих. если сделать проверку по классу "active", который навесится после клика, тоже может вернутся оба значения, если пользователь до этого открывал предыдущий. т.е. мою задачу по созданию ссылки на конкретный хайдер это не решает

nikita.mmf
28.06.2011, 13:41
$('.hide-next').click(function() {
var parentEls = $(this).parents("div").prev('.hide-next')
.map(function () {
return $(this).text();
})
.get().join("/");
console.info(parentEls);
//window.location.hash = parentEls;
});

tadjik1
28.06.2011, 13:52
огромное спасибо вам!

а я поленился прочитать документацию к prev, думал неьлзя в него селектор передавать :(

tadjik1
28.06.2011, 17:47
ну вот, дальше — хуже.

код чуть изменил, чтобы слэш не прыгал, добавил простенькое условие:
$('.hide-next').click(function() {
var parentEls = $(this).parents("div").prev('.hide-next')
.map(function () {
return $(this).text();
})
.get().join("/");
if(parentEls == "") { parentEls = $(this).text(); }
else { parentEls = parentEls+"/"+$(this).text(); }
window.location.hash = parentEls;
});

но теперь мне нужно по сформировавшейся ссылке открыть все эти элементы.
получается что-то вроде

var hash = decodeURIComponent(location.hash.substring(1));

var hashEls = hash.split("/");

if($("*").find(".hide-next:contains("+hashEls[0]+")").trigger("click")) {
for(var num = 1;num<hashEls.length;num++) {
$("*").find(".hide-next:contains("+hashEls[num]+")").trigger("click");
}
}

но это не совсем то опять что нужно, потому что contains ищет просто совпадение и в итоге разворачивает всё, что только можно. скажите, пожалуйста, как лучше реализовать поиск в этом случае? нужно, чтобы только «те» элементы открывались, т.е. элементы с одинаковым text() могу встречаться на одной странице. я так понимаю, надо как-то связывать с иерархией, ориентироваться на предков и детей. но то ли конец дня, а скорее полная безрамотность в jquery не позволяют додумать код.

nikita.mmf
28.06.2011, 18:04
$(".hide-next").each(function(){
var $self = $(this);
if ( $.inArray( $self.text(), hashEls ) > -1 ) {
$self.click();
}
})

tadjik1
29.06.2011, 10:08
а каким образом можно исключить дублирования кликов? дело в том, что значения этих элементов могут совпадать. уникальным является только первый, самый верхний элемент и сам путь.

есть предположение, что можно в самом-самом верхнем диве найти первый хайдер, затем в его чилдрен второй и т.д.

nikita.mmf
29.06.2011, 10:36
tadjik1, тогда остается только такой алгоритм

tadjik1
29.06.2011, 10:56
var hash = decodeURIComponent(location.hash.substring(1));

var hashEls = hash.split("/");

$(".hide-next").each(function(){
var $self = $(this);
if ($self.text() == hashEls[0]) {
$self.click();
}
$self.children('.hide-next').each(function() {
var lll = $(this);
if ( $.inArray( $lll.text(), hashEls ) > -1 ) {
$self.click();
}

})
});

tadjik1
29.06.2011, 12:04
а, нет, не так. это не его дети, а соседские)

tadjik1
29.06.2011, 13:05
function openHider () {
var hash = decodeURIComponent(location.hash.substring(1));
var hashEls = hash.split("/");
$(".hide-next").each(function(){
var $self = $(this);
if ($self.text() == hashEls[0]) {
$self.click();
hashEls = hashEls.splice(0,1);
var child = $self.next().children();
openNextHiders(hashEls, child);
}
});
}
function openNextHiders(hashElss, childs) {
childs.find('.hide-next').each(function() {
var $self = $(this);
if ($self.text() == hashElss[0]) {
$self.click();
hashElss = hashElss.splice(0,1);
childs = $self.next().children();
openNextHiders(hashElss, childs);
}
});
}


проблема в том, что массив стирается полностью в первой функции, не только 0-вой элемент. подскажите, пожалуйста, в каком месте нарушена логика?

nikita.mmf
29.06.2011, 13:18
var hashEls = decodeURIComponent( location.hash.replace( /^#/, "" ) ).split("/");
function clickByHideElems( elems, k) {
var $children = $(elems).children();
var $hideNext = $children.filter(".hide-next").filter(function(){ return $(this).text() === hashEls[k]; });
if ( $hideNext.length ) {
$hideNext.click();
if ( k < hashEls.length - 1 ) {
clickByHideElems($hideNext.next("div"), k++);
}
} else {
clickByHideElems( $children, k );
}
}
clickByHideElems( document.body, 0 );

tadjik1
29.06.2011, 15:07
не могу понять, в чем ошибка, но кликает только по первому элементу..
кстати, в document.body filter() не работает, там приходится через файнд() искать, дальше уже через фильтер. но факт остаётся фактом — кликт только по первому элементу. смотрю в консоли хрома переменная объявилась правильно, она глобальная.

tadjik1
29.06.2011, 15:48
и для чего вот это условие:

else {
clickByHideElems( $children, k );
}

tadjik1
29.06.2011, 15:57
function clickByHideElems( elems, k ) {

$children = $(elems).children();
if( k == 0 ) {
$hideNext = $children.find(".hide-next").filter(function(){ return $(this).text() === hashEls[k]; });
} else {
$hideNext = $children.filter(".hide-next").filter(function(){ return $(this).text() === hashEls[k]; });
}
if ($hideNext.length) {
$hideNext.click();
console.info(k);
if ( k < hashEls.length - 1 ) {
k = k+1;
clickByHideElems($hideNext.next("div"), k);
}
} else {
clickByHideElems( $children, k );
}
}

вот в таком виде вроде бы работает. спасибо!!!

tadjik1
29.06.2011, 15:58
только всё равно поясните, пожалуйста, для чего последний else?

tadjik1
29.06.2011, 16:00
это в том случае сработает, когда хайдер не в верхнем диве?

melky
01.07.2011, 00:48
код в else сработает, когда будет нулевая длина $hideNext

т.е. если этот код ничего не найдет


if( k == 0 ) {
$hideNext = $children.find(".hide-next").filter(function(){ return $(this).text() === hashEls[k]; });
} else {
$hideNext = $children.filter(".hide-next").filter(function(){ return $(this).text() === hashEls[k]; });
}

tadjik1
05.07.2011, 13:09
ок, спасибо всем большое за помощь. отдельный респект Никите. код итоговой функции:

function clickByHideElems( elems, k ) {

$children = $(elems).children();
if( k == 0 ) {
$hideNext = $children.find(".hide-next").filter(function(){ return $(this).text() === hashEls[k]; });
} else {
$hideNext = $children.filter(".hide-next").filter(function(){ return $(this).text() === hashEls[k]; });
}
if ($hideNext.length) {
$hideNext.click();
if ( k < hashEls.length - 1 ) {
k = k+1;
clickByHideElems($hideNext.next("div"), k);
} else {
destination = $hideNext.offset().top;
$('html,body').animate({ scrollTop: destination }, 1000);
}
} else {
clickByHideElems( $children, k );
}
}

tadjik1
05.07.2011, 19:27
не поверите, но это оказалось еще не всё. есть такая проблема: при нажатии на кнопку «назад» браузера происходит переход на прошлую страницу, а надо бы просто отменять последний открытый хайдер. подскажите, пожалуйста, каким образом это реализовать?

trikadin
05.07.2011, 20:20
History API (http://htmlbook.ru/html5/history). Правда поддержка браузерами минимальная пока.

tadjik1
06.07.2011, 12:23
ну мне как раз нужна полная поддержка))

есть идея сделать через hashchange и onhashchange (IE). только я не знаю каким образом отличить изменение хэша при нажатии на «назад» от изменения при клике.

trikadin
07.07.2011, 18:10
Проверять location.hash через setTimeout (http://javascript.ru/setTimeout) или setInterval (http://javascript.ru/setInterval).