ამ პოსტს წესით უნდა ერქვას “Sometimes IE is the BEST” მაგრამ ცხადია ამას არ ვიზამ გასაგები მიზეზების გამო
არააბსოლუტური პოზიციის მქონე ელემენტის პოზიციის(top, left) დადგენა დოკუმენტში საკმაოდ ბანალური საკითხია და პრინციპში ამის შესახებ პოსტის გაკეთების დიდ აზრს ვერასოდეს ვხედავდი… თუმცა გუშინ jQuery – ის ახალი რელიზის კოდის კითხვის დროს აღმოვაჩინე ისეთი რამ რასაც მართლა დიდი ხანია ვეძებდი და რასაც როგორც იქნა მივაგენი…
თემა რა თქმა უნდა შეეხეა ელემენტის პოზიციის დადგენას და იმ პრობლემებს რომელიც ამ ამოცანის გადაჭრისას გვხდება, თუმცა უნდა ვაღიარო რომ ყველა ბრაუზერში მივიღე 100% – იანი შედეგი გარდა IE – სი. უფრო მეტიც ამ შემთხვევაში ქცევა IE6 – სა და IE7 – ს შორის განსხვავდება, და გასაგებია რომ ეს სიამოვნებას არ მომანიჭებდა.
მაშ ასე რა გადაწყვეტასთან გვაქვს საქმე და რამ გამახარა ასე ძალიან?
პრობლემა
არააბსოლუტური პოზიციის მქონე ელემენტის პოზიციის დადგენას გააჩნია რამდენიმე ისეთი ნიუანსი რაც ბრაუზერებს შორის განსხვავდება… ბრაუზერებს შორის სხვაობა მეტნაკლებად აღმოფხვრადია თუმცა აქ კვლავ განსაკუთრებით რთულ პრობლემას ვაწყდებით IE – სთან.
ელემენტის პოზიციის დასადგენი ტიპიური კოდი გამოიყურება ასე:
1 2 3 4 5 6 7 8 9 | function getObjectPosition(obj) { var pos = {top: 0, left: 0}; while(obj) { pos.top += obj.offsetTop; pos.left += obj.offsetLeft; obj = obj.offsetParent; } return pos; } |
ეს კოდი მუშაობს მარტივ შემთხვევებში.. თუმცა რეალური რთული ინტერფეისი მქონე ვებ აპლიკაციებისათვის აბსოლუტურად გამოუსადეგარია. ეს კოდი ბოლომდე კორექტული მხოლოდ Opera – შია.
რაში გამოიხატება ეს “გამოუსადეგრობა”? პირველ რიგში იხილეთ ეს მუშა მაგალითი. მაგალითში გამოყენებული კონტეინერები(div ელემენტები), რომლებიც განთავსებულია ერთმანეთში და მათზე მაუსის დაწკაპუნების შემთხვევაში ხდება კონკრეტული ელემენტის მონიშვნა. თუმცა მონიშვნა ხდება სხვა ელემენტის დახმარებით. ეს არის დამალული(საწყის ეტაპზე) div ელემენტი რომელიც ხილული ხდება მხოლოდ მაშინ თუ დოკუმენტში არსებულ რომელიმე div – ზე დავაწკაპუნებთ. იგი არის ნახევრად გამჭვირვალე და თავზე ეფარება ჩვენს მიერ არჩეულ ელემენტს.
იმისათვის რომ არჩეული ელემენტის გადაფარვა მოხდეს სწორად გვესაჭიროება არჩეული ელემენტის ოთხი პარამეტრი. ესენია:
- top - ელემენტის პოზიცია ბრაუზერის ზედა მხრიდან;
- left - ელემენტის პოზიცია ბრაუზერის მარცხენა მხრიდან;
- width - ელემენტის ზუსტი სიგანე;
- height - ელემენტის ზუსტი სიმაღლე;
ჩამოთვლილი პარამეტრებიდან width და height პარამეტრები ადვილი მოსაპოვებელია ელემენტის offsetWidth(სიგანისათვის) და შესაბამისად offsetHeight(სიმაღლისათვის) თვისებების მეშვეობით… თუმცა ელემენტის ზუსტი პოზიციის დადგენა გაცილებით რთული საკითხია რადგან… რადგან offsetTop და offsetLeft თვისებებში ინახებ მნიშვნელობები რომელიც სასურველი ელემენტის ზედა საფეხურზე მდგომი (offsetParent) ელემენტის მიმართებაში გამოითვლება… აქედან არსებობს ერთი გამოსავალი რაც ნაჩვენები იყო ზემოთ მოყვანილ კოდში… ანუ საჭიროა იტერაცია ქვემოდან ზევით რათა ყველა offsetParent – ის offsetTop და offsetLeft მონაცემების დაჯამება მოხდეს…
პირველ ყველაზე მარტივი საკითხი რასაც ვაწყდებით ამ იტერაციისას არის ის რომ თურმე სხვადასხვა ბრაუზერებში ეს საფეხურები(ანუ ელემენტი რომელიც შეესაბამება კონკრეტულ საფეხურს) განსხვავებულია… თუმცა ეს დიდ პრობლემას არ ქმნის… პრობლემები გვხდება სხვა შემთხვევებში, კერძოდ კი თუ ელემენტი განთავსებულია ისეთ ელემენტში რომლის სტილის ატრიბუტის overflow მნიშვნელობა არის auto ხოლო, სკროლერი არის დაძრული ადგილიდან პოზიციის გამოთვლაში ვაწყდებით ცდომილებას… ეს ეხება როგორც მარჯვენა, ასევე მარცხენა სკროლს…
ამისათვის არსებობს ერთი მარტივი გზა, სასურველი ელემენტიდან დაწყებული(ქვემოდან ზემოთ) სრული იერარქიის იტერაცია და scrollLeft და scrollTop მნიშვნელობების გამოკლება შესაბამისი მნიშვნელობებისათვის.
მოდიფიცირებული კოდი:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function getObjectPosition(obj) { var pos = {top: 0, left: 0}, el = obj; while(obj) { pos.top += obj.offsetTop; pos.left += obj.offsetLeft; obj = obj.offsetParent; } for (obj = el.parentNode; obj && obj != document.body; obj = obj.parentNode) { pos.top -= obj.scrollTop; pos.left -= obj.scrollLeft; } return pos; } |
ამ მოდიფიკაციით სკროლერების პრობლემა მოგვარებულია მაგრამ… ეხლა იწყება ყველაზე საინტერესო
ზემოთ ავღნიშნე რომ კოდი რომელიც მოვიყვანე მაგალითად კორექტულად მუშაობს მხოლოდ Opera – ში… რატომ? თუ დააკვირდით მუშა მაგალითს, ყველა ელემენტს აქვს განსხვავებული ზომის გვერდები(border). სწორედ ეს გვერდები ქმნის პრობლემას რადგან, Opera – ს გარდა არცერთი ბრაუზერი არ ითვალისწინებს ამას და ხშირად ვღებულობთ ცდომილებებს…
ამ პრობლემის თავიდან აცილება შესაძლებელია ელემენტის მარცხენა და ზედა გვერდის გამოთვლის მეშვეობით… მაგრამ ეს საკითხიც პრობლემატურია რადგან მიმდინარე სტილების მნიშვნელობის გაგება სხვადასხვაა ბრაუზერებს შორის (იხ. შესაბამისი პოსტი) და გარდა იმისა რომ ეს გამოთვლა აბსოლუტურათ ზედმეტია ოპერასთვის, ასევე დამატებითი კოდის წერაა საჭირო… მითითებული პოსტის გათვალისწინებით კოდი მიიღებს შემდეგ სახეს:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function getObjectPosition(obj) { var pos = {top: 0, left: 0}, el = obj; var isOpera = navigator.userAgent.toLowerCase().indexOf('opera') > -1; while(obj) { pos.top += obj.offsetTop; pos.left += obj.offsetLeft; if (!isOpera) { pos.top += parseInt(Util.style.getStyle.getStyle(obj.offsetParent, 'border-top-width')) || 0; pos.left += parseInt(Util.style.getStyle.getStyle(obj.offsetParent, 'border-left-width')) || 0; } obj = obj.offsetParent; } for (obj = el.parentNode; obj && obj != document.body; obj = obj.parentNode) { pos.top -= obj.scrollTop; pos.left -= obj.scrollLeft; } return pos; } |
კოდში შეტანილი ცვლილებები გამოიყენება სწორედ ამ არათავსებადობის აღმოსაფხვრელად… მაგრამ ცუდი არის ის რომ ეს კოდი ვარგისია ყველგან გარდა IE6 – ისა… პრობლემა მდგომარეობს იმაში რომ გარდა გვერდების ზომისა IE – ს ესაჭიროება ასევე ელემენტის პირველი მშობელი ელემენტის შიდა სივრცის გამოთვლა როგორც ზედა მხრისათვის ასევე მარცხენა მხრისათვის… თან გაითვალისწინეთ რომ ამ ელემენტის position ატრიბუტის მნიშვნელობა არ უნდა იყოს static… მაგრამ განსხვავებულ შემთხვევებში არც ეს გამოსავალია საჭირო… ამ დეტალების აღმოსაფხვრელად საჭირო კოდს არ ვწერ შეგნებულად რადაგნ როგორც ზემოთ ავღნიშნე IE – სთვის მივაგენი იდეალურ გამოსავალს…
ეს არის ელემენტის მეთოდი getBoundingClientRect, რომელიც გვიბრუნებს ობიექტს რომელშიც განთავსებულია ჩვენთვის საჭირო ინფორმაცია და დამატებით დამუშავებას აღარ საჭიროებს.
განმარტება MSDN – იდან:
Returns a TextRectangle object. Each rectangle has four integer properties (top, left, right, and bottom) that represent a coordinate of the rectangle, in pixels.
რაოდენ გასაოცარიც არ უნდა იყოს ეს მეთოდი მუშაობს უშეცდომოდ… ერთი პატარა გამონაკლისის გარდა, ეს არის თავად HTML ელემენტის ნაგულისხმევი(default) მნიშვნელობა border ატრიბუტისათვის… ანუ იგი არის 2 პიქსელის ტოლი.. თუმცა ეს ადვილად მოსაგვარებელი საკითხია და ჩვენი კოდი მიიღებს საბოლოო და უნივერსალურ სახეს:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | getObjectPostion: function(obj) { var pos = {top: 0, left: 0}, el = obj; //IE6/7 solution if (obj.getBoundingClientRect) { var box = obj.getBoundingClientRect(); pos.top = box.top - 2; pos.left = box.left - 2; } else { //all other browsers while(obj) { pos.top += obj.offsetTop; pos.left += obj.offsetLeft; if (!this.browser.isOpera) { pos.top += parseInt(this.getStyle(obj.offsetParent, 'border-top-width')) || 0; pos.left += parseInt(this.getStyle(obj.offsetParent, 'border-left-width')) || 0; } obj = obj.offsetParent; } } //calculation of left and right scrolls for (obj = el.parentNode; obj && obj != document.body; obj = obj.parentNode) { pos.top -= obj.scrollTop; pos.left -= obj.scrollLeft; } el = obj = null; return pos; } |
მაშ ასე, პოზიციების დადგენა IE – სთვის თურმე ყველაზე ელეგანტურად გვარდება განსხვავებით სხვა დანარჩენი ბრაუზერებისგან, კოდის ნაწილი:
1 2 3 4 5 | if (obj.getBoundingClientRect) { var box = obj.getBoundingClientRect(); pos.top = box.top - 2; pos.left = box.left - 2; } |
გამოიყენება სწორედ IE – სთვის
და რაც ყველაზე სასიხარულოა, იგი არის ძალიან სწრაფი და რაც მთავარია უშეცდომო!
პოზიციის დასადგენად საჭირო კოდი სრულად შეგიძლიათ ამოიღოთ ზემოთ მითითებული მაგალითის სორსიდან.
ტეგები: JavaScript, JavaScript DOM
GOOOOOD!!!
და ისააა, იოსებ,
var isOpera = navigator.userAgent.toLowerCase().indexOf('opera')> -1;მე მგონი ეს აკლია რომ სრულად იმუშაოს
სხვა რაღაცეებიც აკლია, მაგრამ არ არის ამ პოსტში
ზემოთ რომ მუშა მაგალითის მისამართია მითითებული იქიდან შეგიძლია აიღო სრული სორსი
http://www.code.ge/samples/positions.htm
საღოლ, სოსო, კაია ძალიან.
ერთ-ერთი ყველაზე მყრალი ამოცანაა ჯავასკრიპტში და ასე საკაიფოდ დაწერილი ამოხსნა, ნამდვილად დასაფასებელია
irakli
დიდი მადლობა იკა