Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Найти в родителе элемент с заданным классом (https://javascript.ru/forum/events/18373-najjti-v-roditele-ehlement-s-zadannym-klassom.html)

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. Правда поддержка браузерами минимальная пока.

tadjik1 06.07.2011 12:23

ну мне как раз нужна полная поддержка))

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

trikadin 07.07.2011 18:10

Проверять location.hash через setTimeout или setInterval.


Часовой пояс GMT +3, время: 23:44.