MozillaPL.org - polskie centrum Mozilli

Główne menu:

[JS] No, co z tym przewijaniem

Jak tworzyć strony WWW zgodnie ze standardami

Moderator: Pomocy?!

[JS] No, co z tym przewijaniem

Postautor: trejder » 26 września 2007, 18:22

Przeglądarka: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7

Witam,

Krótko: czemu Firefox automatycznie przewija pole typu <textarea>, po wstawieniu do niego czegoś na sposób identyczny do edytora postów w tym forum? Problem ten zauważyłem we wszystkich serwisach (fora, blogi), gdzie można używać BBCode. Zauważyłem również we własnym rozwiązaniu. Wziąłem je z tej strony. Wygląda to tak.

Ponieważ - jak zwykle - IE ma problemy z obsługą standardów, więc kody wstawiające BBCode dla obu przeglądarek różnią się. Ogólny kod, który decyduje, jaka przeglądarka (i jaka funkcja) wygląda tak:

Kod: Zaznacz cały
function insert_bbcode(txtarea, lft, rgt)
{
     if (document.all)
     {
     insert_bbcode_ie(lft, rgt);
     }
     else if (document.getElementById)
     {
     insert_bbcode_mozilla(txtarea, lft, rgt);
     }
}


I w zależności od przeglądarki mamy kod dla FF (Mozilli):

Kod: Zaznacz cały
function insert_bbcode_mozilla(txtarea, lft, rgt)
{
     var selLength = txtarea.textLength;
     var selStart = txtarea.selectionStart;
     var selEnd = txtarea.selectionEnd;
     if (selEnd == 1 || selEnd == 2) selEnd = selLength;
     var s1 = (txtarea.value).substring(0, selStart);
     var s2 = (txtarea.value).substring(selStart, selEnd)
     var s3 = (txtarea.value).substring(selEnd, selLength);
     txtarea.value = s1 + lft + s2 + rgt + s3;
     txtarea.focus();
}
,

a dla IE:

Kod: Zaznacz cały
function insert_bbcode_ie(lft, rgt)
{
     strSelection = document.selection.createRange().text;
     if (strSelection!="")
     {
     document.selection.createRange().text = lft + strSelection + rgt;
     }
}


W sumie wszystkie trzy rozwiązania działają cacy w obu przeglądarkach. Problem w tym, że w przypadku FF następuje to drażniące mnie przewijanie pola <textarea> do samego początku. W IE tego nie ma.

Ponieważ - jak pisałem na początku - problem zauważyłem na wielu stronach internetowych (w tym w phpBB, na którym chodzi to forum), więc podejrzewam, że rozwiązać się go nie da. Mimo wszystko proszę chociaż o prostą odpowiedź DLACZEGO się tak dzieje? Dlaczego FF zachowuje się w sposób - bądź co bądź - idiotyczny? Czy jest jakiś sens w przewijaniu pola do samego początku? Bo ja go nie dostrzegam?
trejder
 
Posty: 197
Z nami od: 20 stycznia 2005, 15:31
Lokalizacja: Katowice

Postautor: Mirek » 27 września 2007, 00:43

Przeglądarka: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7

W IP.Board - gdy mamy zaznaczony tekst wewnątrz pola tekstowego, to po kliknięciu na przycisku BBCode, pozycja scrolla (a chyba właściwie zaznaczenia, a co za tym idzie scrolla) jest jakoś zapamiętywana. Ale przy braku zaznaczenia, po wstawieniu znacznika w miejsce kursora, kursor ląduje na końcu pola, a scroll na początku... Jak wrócę (pewnie w poniedziałek), to w wolnej chwili pogłówkuje jeszcze nad tym, bo mi też to czasem przeszkadza - ale z tymi wolnymi chwilami ostatnio kiepsko - np. ciągle jeszcze nie dokończyłem tematu z Przydatnymi skryptozakładkami - więc może ktoś będzie szybszy lub coś sam wygooglasz:
[javascript textarea (scroll|rewind|"caret.position")]
Zresztą sam poszukałem i natrafiłem m.in. na: http://www.thescripts.com/forum/thread567094.html - ale to brzydki sposób.
Dlatego chwilkę pogłówkowałem, zajrzałem do mało przejrzystego IPB i wymyśliłem coś takiego:
Kod: Zaznacz cały
javascript:(function(){  st=document.post.message.scrollTop; pos=document.post.message.selectionStart; document.post.message.value = document.post.message.value.substring(0, pos) + '!!!!!!!!' + document.post.message.value.substring(pos, document.post.message.value.length); document.post.message.focus(); document.post.message.setSelectionRange(pos+8, pos+8); document.post.message.scrollTop=st;})();
Kluczowa jest pierwsza i ostatnia komenda wewnątrz funkcji (ta ze zmienną scrollTop) - i chyba wystarczy, że je podobnie dodasz. Dla testu napisz coś tutaj w polu odpowiedzi - tak, żeby zajmowało kilka pełnych widoków textarea, ustaw kursor i scroll gdzieś pośrodku, wklej powyższy kod w pole adresu i obserwując pozycję, gdzie był kursor wciśnij ENTER - powinno dodać 8 wykrzykników i przewinąć w mniej więcej to samo miejsce. To jest wersja tylko dla FF - ale dla IE już wiesz co działa - trzeba tylko połączyć, żeby działały oba...
Chyba muszę sprzedać pomysł twórcą IPB ;-)

Pozdrawiam,
Mirek
Mirek
Moderator
 
Posty: 2158
Z nami od: 09 sierpnia 2007, 20:37

Postautor: trejder » 27 września 2007, 15:32

Przeglądarka: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7

Mam nadzieję, że nikt nie zje mnie za długość tego posta! :)

Do przedmówcy, czyli Mirka [PL]. Oczywiście masz rację! Wystarczyło, jako pierwszą i ostatnią linijkę funkcji wstawić:

Kod: Zaznacz cały
var st = txtarea.scrollTop;
txtarea.scrollTop = st;


i pozycja suwaka pionowego przewijania jest zapamiętywana.

Ja dodałem jeszcze od siebie: txtarea.focus();, żeby po wstawieniu BBCode'u fokus wracał do pola edycyjnego. Dzięki temu można pisać dalej i nie trzeba klikać ponownie w pole.

Jednak Twój post zmobilizował, żeby dalej rozwijać i poprawiać do rozwiązanie, aby dostosować je w 100% do moich wymagań. Zdaję sobie sprawę, że jestem estetą i większość moich zmian to kosmetyczne poprawki lub bzdurki, którymi większość osób się nie przejmuje. Mimo wszystko - skoro zacząłem ten post - to czuję obowiązek go dokończyć i podzielić się tym, co udało się mi osiągnąć.

W pierwszej kolejności - wywaliłem cały kod odpowiedzialny za obsługę dziwactw w IE. Ja osobiście używam tego rozwiązania tylko w FF, a poza tym jestem zagorzałym fanem (czasem - fanatykiem) czystości kodu, usuwania zbędnych fragmentów oraz zgodności ze standardami. Na pewno nie mam zamiaru pisać oddzielnego kodu dla owej wieśniackiej przeglądarki. Dlatego, jeśli ktoś planuje wykorzystać to rozwiązanie na stronie, która będzie musiała działać też pod IE, musi pobrać oryginalną wersję kodu z tej strony (gdzie cała ta "zabawa" się zaczęła) i dostosować ją do swoich potrzeb.

W drugiej kolejności wziąłem się za pozycję kursora. Drażniło mnie bardzo to, że mimo, iż jest zapamiętywana pozycja suwaka pionowego, to mimo wszystko kursor po każdym wstawieniu ląduje na końcu tekstu, a w miejscu wstawienia. Dodałem więc taki kod:

Kod: Zaznacz cały
var str = lft + s2 + rgt;
txtarea.selectionStart = selStart + str.length;
txtarea.selectionEnd = selStart + str.length;


Dzięki temu, po wstawieniu BBCode kursor ląduje dokładnie za wstawką i można kontynuować pisanie od miejsca, w którym się ono skończyło.

Dokładnie to samo dzieje się, jeśli klawisz wstawiania naciśniemy, gdy nie jest zaznaczony żaden tekst. Wówczas kursor również ląduje na końcu wstawki. Ja jednak chciałem mieć funkcjonalność znaną np. z kEDa. Tam, jeśli brak jest zaznaczenia, to kursor po wstawieniu ląduje wewnątrz wstawki (pomiędzy otwierającym i zamykającym fragmentem) i od razu można wpisać tekst, który ma być tą wstawką objęty. Bez konieczności przesuwania kursora. Powyższy kod został więc zmodyfikowany do takiej postaci:

Kod: Zaznacz cały
if (selStart == selEnd)
{
     var str = lft;
     txtarea.selectionStart = selStart + str.length;
     txtarea.selectionEnd = selStart + str.length;
}
else
{
     var str = lft + s2 + rgt;
     txtarea.selectionStart = selStart + str.length;
     txtarea.selectionEnd = selStart + str.length;     
}


Nie wiem, jak inni, ale ja wielokrotnie zaznaczam tekst w edytorze w ten sposób, że dwuklikam LKM w dowolnym miejscu, co powoduje zaznaczenie całego wyrazu. Jednakże, jeśli po wyrazie jest spacja to jest ona również brana w zaznaczenie. Gdy się potem wstawi wstawkę (np. pogrubienie) to obejmuje ono również tą spację. Niby bzdurka, ale mnie to drażni, więc postanowiłem to zmienić. Tym bardziej, że kod potrzebny do usunięcia tego to zaledwie dwie linijki:

Kod: Zaznacz cały
var t = (txtarea.value).substring(selEnd - 1, selEnd);
if (t == ' ') selEnd--;


Mamy więc ostatecznie blokowanie automatycznego przesuwania się suwaka do początku po każdej wstawce, ignorowanie ostatniej spacji w zaznaczeniu, umieszczanie kursora po wstawce (gdy jest zaznaczony tekst), a nie na końcu całości. Lub wewnątrz wstawki (gdy nic nie było zaznaczone). I - cytując przedmówcę - twórcy phpBB i IPB mogliby się od nas uczyć! :P

Ostateczna wersja funkcji wygląda więc tak:

Kod: Zaznacz cały
function insert_bbcode(txtarea, lft, rgt)
{
     var st = txtarea.scrollTop;
     var selLength = txtarea.textLength;
     var selStart = txtarea.selectionStart;
     var selEnd = txtarea.selectionEnd;
     
     if (selEnd == 1 || selEnd == 2) selEnd = selLength;
     
     var t = (txtarea.value).substring(selEnd - 1, selEnd);
     if (t == ' ') selEnd--;
     
     var s1 = (txtarea.value).substring(0, selStart);
     var s2 = (txtarea.value).substring(selStart, selEnd)
     var s3 = (txtarea.value).substring(selEnd, selLength);
     
     txtarea.value = s1 + lft + s2 + rgt + s3;
     
     if (selStart == selEnd)
     {
     var str = lft;
     txtarea.selectionStart = selStart + str.length;
     txtarea.selectionEnd = selStart + str.length;
     }
     else
     {
     var str = lft + s2 + rgt;
     txtarea.selectionStart = selStart + str.length;
     txtarea.selectionEnd = selStart + str.length;     
     }
     
     txtarea.scrollTop = st;
     txtarea.focus();
}


Dla zainteresowanych podaję też prostą funkcję, której również używam, a która wstawia adres URL sensowniej (moim zdaniem), niż robi to klawisz URL w tym forum, czy na IPB. Też ktoś mógłby to zaimportować tutaj:

Kod: Zaznacz cały
function insert_bbcode_url(txtarea)
{
     var my_link = prompt("Podaj adres URL:", "http://");
     
     if (my_link != null)
     {
     lft = "[url=" + my_link + "]";
     rgt = "[/url]";
     insert_bbcode(txtarea, lft, rgt);
     }
     txtarea.focus();
     return;
}


Nie wszystko jednak udało mi się tak prosto rozwiązać. Czy ktoś uprzejmie może mi wytłumaczyć, dlaczego Firefox totalnie olewa atrybut accesskey? Nawet tutaj, w tym forum, są porobione skróty klawiszowe do klawiszy wstawiających wstawki BBCode. Co więcej - nawet jest help, który wyraźnie podaje te skróty, gdy się najedzie na odpowiedni klawisz. B to jest alt+b, Quote to alt+q i... tak dalej. Wszystko pięknie, ładnie - tylko szkoda, że to nie działa. Mogę do śmierci naciskać te kombinacje klawiszy i nic niestety nie jest wstawiane! :/

Brak obsługi accesskey w Firefoxie zauważyłem już dużo wcześniej, zanim zacząłem myśleć o własnym edytorze BBCode. Dlatego już na etapie jego projektowania nawet nie liczyłem na tą funkcjonalność. Tu z pomocą znowu przyszło oryginalne rozwiązanie, znajdujące się na tej stronie. Użyłem funkcji stamtąd, jedynie lekko ją poprawiając:

Kod: Zaznacz cały
document.onkeypress = function (e)
{
     ctrl = e.ctrlKey;
     shft = e.shiftKey;
     chr = e.charCode;
     
     if (ctrl && shft && chr == 65) insert_bbcode_url(txtarea);
     if (ctrl && shft && chr == 66) insert_bbcode(txtarea,'[b]','[/b]');
     if (ctrl && shft && chr == 73) insert_bbcode(txtarea,'[i]','[/i]');
     if (ctrl && shft && chr == 85) insert_bbcode(txtarea,'[u]','[/u]');

     return true;
}


Tu w tym przypadku działa kombinacja Ctrl+Shift+a dla wstawiania adresu, b - dla pogrubienia, i - dla kursywy i u - dla podkreślenia. Ale łatwo można to rozbudować o kolejne skróty. Zastosowanie kombinacji dwóch klawiszy funkcyjnych (ctrl+alt) pozwala ominąć większość skrótów wbudowanych w Firefoxa. Bowiem w wersji tylko z altem np. zaraz po wstawieniu podkreślenia wyświetlało się źródło strony. A przy innych klawiszach działy się inne równie ciekawe rzeczy.

Na koniec chciałem jeszcze zapytać o jedną, prostą rzecz. Dlaczego takie coś nie działa:

Kod: Zaznacz cały
<button type="button" onclick="insert_bbcode(document.edit_post.message, '\"[i]', '[/i]\"');">Cytat</button>


W konsoli błędów dostaję komunikat "Unterminated string literal". Próbowałem różnych kombinacji - cudzysłowów pojedynczych i podwójnych i w żadnej wersji nie udało mi się osiągnąć zamierzonego celu. Jak więc przekazać cudzysłów jako parametr funkcji JS wywoływanej z poziomu XHTML?

Ufff... To chyba wszystko. Ponownie przepraszam tych, których przeraziła długość tego posta. Mam nadzieję, że mimo wszystko będzie on komuś przydatny.
trejder
 
Posty: 197
Z nami od: 20 stycznia 2005, 15:31
Lokalizacja: Katowice

Postautor: dexter » 27 września 2007, 15:57

Przeglądarka: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl-PL; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6

trejder pisze:Nie wszystko jednak udało mi się tak prosto rozwiązać. Czy ktoś uprzejmie może mi wytłumaczyć, dlaczego Firefox totalnie olewa atrybut accesskey? Nawet tutaj, w tym forum, są porobione skróty klawiszowe do klawiszy wstawiających wstawki BBCode.


Firefox nigdy nie olewał tego atrybutu. U mnie działa lewy shift + alt + accesskey. Kiedyś było rzeczywiście w mozilli Alt+Acc, w IE:Alt+Acc, Enter, w Operze:Shift+Esc, Acc, na Macu:Control+Acc :)
dexter
Moderator
 
Posty: 8514
Z nami od: 02 października 2004, 21:30

Postautor: trejder » 27 września 2007, 16:12

Przeglądarka: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7

dexter pisze:Firefox nigdy nie olewał tego atrybutu. U mnie działa lewy shift + alt + accesskey. Kiedyś było rzeczywiście w mozilli Alt+Acc, w IE:Alt+Acc, Enter, w Operze:Shift+Esc, Acc, na Macu:Control+Acc :)


A to bardzo przepraszam! Mea culpa, mea maxima! Winę za to (chyba) ponoszą więc twórcy phpBB, bo użyli wprowadzających w błąd opisów w polu powyżej pola edycyjnego. :)
trejder
 
Posty: 197
Z nami od: 20 stycznia 2005, 15:31
Lokalizacja: Katowice

Postautor: Stef » 27 września 2007, 16:43

Przeglądarka: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.7) Gecko/20070927 Firefox/2.0.0.7 Sulfur/1.0a1pre

trejder pisze:W konsoli błędów dostaję komunikat "Unterminated string literal". Próbowałem różnych kombinacji - cudzysłowów pojedynczych i podwójnych i w żadnej wersji nie udało mi się osiągnąć zamierzonego celu. Jak więc przekazać cudzysłów jako parametr funkcji JS wywoływanej z poziomu XHTML?

Ufff... To chyba wszystko. Ponownie przepraszam tych, których przeraziła długość tego posta. Mam nadzieję, że mimo wszystko będzie on komuś przydatny.


Odnośnie znaków to np. sekwencje ucieczki do Unicode, inna sprawa po kiego z poziomu dokumentu? Toć nie można normalnie przez obserwator?
Czasami coś napiszę na stef.log
Stef
Moderator
 
Posty: 3466
Z nami od: 16 kwietnia 2004, 11:05

Postautor: trejder » 27 września 2007, 17:03

Przeglądarka: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7

Stef pisze:Odnośnie znaków to np. ucieczki do Unicode, inna sprawa po kiego z poziomu dokumentu? Toć nie można normalnie przez obserwator?


Dzięki! Z Unicodem działa. Czemu nie przez obserwator? Szczerze? Bo jestem kapeć z JS, DOMa i podobnych. Dlatego tekst pod podanym przez Ciebie linkiem mi niewiele mówi. Poza tym estem człowiekiem trochę zbyt zajętym i trochę zbyt leniwym, by uczyć się czegoś nowego tylko w celu wstawienia dwóch cudzysłowów! :P
trejder
 
Posty: 197
Z nami od: 20 stycznia 2005, 15:31
Lokalizacja: Katowice


Wróć do Tworzenie stron WWW

Kto jest online

Zarejestrowani użytkownicy: Baidu [Spider], dexter, Google [Bot]

Przejdź do powiązanej strony

Nawigacja:

Stopka: